Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
log.f90
Go to the documentation of this file.
1! Copyright (c) 2021-2024, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
34module logger
35 use neko_config, only : neko_version
36 use comm, only : pe_rank
37 use utils, only : neko_error, neko_warning
38 use, intrinsic :: iso_fortran_env, only : stdout => output_unit, &
39 stderr => error_unit
40 implicit none
41 private
42
43 ! > Size of the log message buffer
44 !! @note This adjust for the leading space applied by `write`. 80 character
45 !! output log leaves 79 characters for the message.
46 integer, public, parameter :: log_size = 79
47
49 integer, public, parameter :: sec_head_size = 30
50
51 ! Possible levels of logging
52 integer, public, parameter :: neko_log_quiet = 0
53 integer, public, parameter :: neko_log_info = 1
54 integer, public, parameter :: neko_log_verbose = 2
55 integer, public, parameter :: neko_log_deprecation = 5
56 integer, public, parameter :: neko_log_debug = 10
57
58 type, public :: log_t
59 integer, private :: indent_ = 0
60 integer, private :: section_id_ = 0
61 integer, private :: tab_size_ = 1
62 integer, private :: level_ = neko_log_info
63 integer, private :: unit_ = stdout
64
65 character(len=LOG_SIZE), private :: section_header = ""
66
68 character(len=50), private, dimension(:), allocatable :: deprecated_list
69
70 contains
71 procedure, pass(this) :: init => log_init
72 procedure, pass(this) :: free => log_free
73 procedure, pass(this) :: begin => log_begin
74 procedure, pass(this) :: end => log_end
75 procedure, pass(this) :: indent => log_indent
76 procedure, pass(this) :: newline => log_newline
77 procedure, pass(this) :: message => log_message
78 procedure, pass(this) :: section => log_section
79 procedure, pass(this) :: header => log_header
80 procedure, pass(this) :: error => log_error
81 procedure, pass(this) :: warning => log_warning
82 procedure, pass(this) :: deprecated => log_deprecated
83 procedure, pass(this) :: end_section => log_end_section
84 procedure, pass(this) :: flush => log_flush
85 procedure, pass(this) :: level => log_level
86
87 procedure, private, pass(this) :: print_section_header => &
89 end type log_t
90
92 type(log_t), public :: neko_log
93
94contains
95
101 subroutine log_init(this, env_prefix)
102 class(log_t), intent(inout) :: this
103 character(len=*), intent(in), optional :: env_prefix
104 character(len=255) :: log_level
105 character(len=255) :: log_tab_size
106 character(len=255) :: log_file
107 character(len=32) :: prefix
108 integer :: envvar_len
109 if (pe_rank .ne. 0) return
110
111 if (present(env_prefix)) then
112 prefix = env_prefix
113 else
114 prefix = "NEKO"
115 end if
116
117 this%indent_ = 0
118 this%section_id_ = 0
119
120 call get_environment_variable(trim(prefix) // "_LOG_TAB_SIZE", &
121 log_tab_size, envvar_len)
122 if (envvar_len .gt. 0) then
123 read(log_tab_size(1:envvar_len), *) this%tab_size_
124 else
125 this%tab_size_ = 1
126 end if
127
128 call get_environment_variable(trim(prefix) // "_LOG_LEVEL", &
129 log_level, envvar_len)
130 if (envvar_len .gt. 0) then
131 read(log_level(1:envvar_len), *) this%level_
132 else
133 this%level_ = neko_log_info
134 end if
135
136 call get_environment_variable(trim(prefix) // "_LOG_FILE", &
137 log_file, envvar_len)
138 if (envvar_len .gt. 0) then
139 open(newunit = this%unit_, file = trim(log_file), status = 'replace', &
140 action = 'write')
141 else
142 this%unit_ = stdout
143 end if
144
145 end subroutine log_init
146
148 subroutine log_free(this)
149 class(log_t), intent(inout) :: this
150 integer :: i
151
152 ! Flush the log before closing off.
153 call this%flush()
154
155 if (this%section_id_ .ne. 0) then
156 call neko_error("Log is unbalanced")
157 end if
158
159 if (allocated(this%deprecated_list)) then
160 call this%section("Deprecated features summary", neko_log_deprecation)
161
162 do i = 1, size(this%deprecated_list)
163 call this%message(trim(this%deprecated_list(i)), neko_log_deprecation)
164 end do
165 call this%end_section()
166 end if
167
168 if (this%unit_ .ne. stdout) then
169 close(this%unit_)
170 end if
171
172 this%indent_ = 0
173 this%section_id_ = 0
174 this%tab_size_ = 1
175 this%level_ = neko_log_info
176 this%unit_ = stdout
177
178 if (allocated(this%deprecated_list)) then
179 deallocate(this%deprecated_list)
180 end if
181
182 end subroutine log_free
183
187 subroutine log_flush(this)
188 class(log_t), intent(in) :: this
189 if (pe_rank .ne. 0) return
190
191 ! The unit is compared against the sentinel value set by `log_free`, and
192 ! not simply tested for being positive, since NEWUNIT-assigned units
193 ! (used for NEKO_LOG_FILE) are always negative
194 flush(this%unit_)
195
196 end subroutine log_flush
197
199 pure function log_level(this) result(lvl)
200 class(log_t), intent(in) :: this
201 integer :: lvl
202 if (pe_rank .ne. 0) return
203
204 lvl = this%level_
205
206 end function log_level
207
209 subroutine log_begin(this)
210 class(log_t), intent(inout) :: this
211 if (pe_rank .ne. 0) return
212
213 this%section_id_ = this%section_id_ + 1
214 this%indent_ = this%indent_ + this%tab_size_
215
216 end subroutine log_begin
217
219 subroutine log_end(this)
220 class(log_t), intent(inout) :: this
221 if (pe_rank .ne. 0) return
222
223 if (this%section_id_ .eq. 0) then
224 call neko_error("Log is unbalanced")
225 end if
226 this%section_id_ = this%section_id_ - 1
227 this%indent_ = this%indent_ - this%tab_size_
228
229 this%section_header = ""
230
231 end subroutine log_end
232
234 subroutine log_indent(this)
235 class(log_t), intent(in) :: this
236 if (pe_rank .ne. 0) return
237
238 write(this%unit_, '(A)', advance = 'no') repeat(' ', this%indent_)
239
240 end subroutine log_indent
241
243 subroutine log_newline(this, lvl)
244 class(log_t), intent(in) :: this
245 integer, optional :: lvl
246
247 integer :: lvl_
248 if (pe_rank .ne. 0) return
249
250 if (present(lvl)) then
251 lvl_ = lvl
252 else
253 lvl_ = neko_log_info
254 end if
255
256 if (lvl_ .gt. this%level_) then
257 return
258 end if
259
260 write(this%unit_, '(A)') ''
261
262 end subroutine log_newline
263
265 subroutine log_message(this, msg, lvl)
266 class(log_t), intent(inout) :: this
267 character(len=*), intent(in) :: msg
268 integer, optional :: lvl
269 integer :: lvl_
270 if (pe_rank .ne. 0) return
271
272 if (present(lvl)) then
273 lvl_ = lvl
274 else
275 lvl_ = neko_log_info
276 end if
277
278 if (lvl_ .gt. this%level_) then
279 return
280 end if
281
282 if (len_trim(this%section_header) .gt. 0) then
283 call this%print_section_header(lvl)
284 end if
285
286 call this%indent()
287 write(this%unit_, '(A)') trim(msg)
288
289 end subroutine log_message
290
292 subroutine log_header(this, version, build_info)
293 class(log_t), intent(in) :: this
294 character(len=*), intent(in) :: version
295 character(len=*), intent(in) :: build_info
296 if (pe_rank .ne. 0) return
297
298 write(this%unit_, '(A)') ''
299 write(this%unit_, '(1X,A)') ' _ __ ____ __ __ ____ '
300 write(this%unit_, '(1X,A)') ' / |/ / / __/ / //_/ / __ \ '
301 write(this%unit_, '(1X,A)') ' / / / _/ / ,< / /_/ / '
302 write(this%unit_, '(1X,A)') '/_/|_/ /___/ /_/|_| \____/ '
303 write(this%unit_, '(A)') ''
304 write(this%unit_, '(1X,A,A,A)') '(version: ', trim(version), ')'
305 write(this%unit_, '(1X,A)') trim(build_info)
306 write(this%unit_, '(A)') ''
307 flush(this%unit_)
308
309 end subroutine log_header
310
312 subroutine log_error(this, msg)
313 class(log_t), intent(in) :: this
314 character(len=*), intent(in) :: msg
315 if (pe_rank .ne. 0) return
316
317 call this%flush()
318 call this%indent()
319 call neko_error(trim(msg))
320
321 end subroutine log_error
322
324 subroutine log_warning(this, msg)
325 class(log_t), intent(in) :: this
326 character(len=*), intent(in) :: msg
327 if (pe_rank .ne. 0) return
328
329 call this%indent()
330 write(this%unit_, '(A,A,A)') '*** WARNING: ', trim(msg), ' ***'
331 call this%flush()
332
333 end subroutine log_warning
334
339 subroutine log_deprecated(this, feature, removal_version, extra_info)
340 class(log_t), intent(inout) :: this
341 character(len=*), intent(in) :: feature
342 character(len=*), intent(in) :: removal_version
343 character(len=*), intent(in), optional :: extra_info
344 character(len=50), dimension(:), allocatable :: tmp_list
345 character(len=255) :: deprecation_error
346 character(len=LOG_SIZE) :: msg
347 integer :: i
348 if (pe_rank .ne. 0) return
349
350 if (this%level_ .ge. neko_log_deprecation .or. &
351 is_deprecated(removal_version)) then
352
353 ! Check that the feature have not already been logged
354 if (.not. allocated(this%deprecated_list)) then
355 allocate(character(len=50) :: this%deprecated_list(1))
356 this%deprecated_list = trim(feature)
357 else
358 do i = 1, size(this%deprecated_list)
359 if (trim(this%deprecated_list(i)) .eq. trim(feature)) return
360 end do
361
362 ! Save the feature to the list of deprecated features
363 call move_alloc(this%deprecated_list, tmp_list)
364 allocate(character(len=50)::this%deprecated_list(size(tmp_list)+1))
365 this%deprecated_list(1:size(tmp_list)) = tmp_list
366 this%deprecated_list(size(tmp_list) + 1) = trim(feature)
367 deallocate(tmp_list)
368 end if
369
370 ! Construct deprecation message
371 write(msg, '(A,A)') '*** DEPRECATION: ', trim(feature)
372 call this%message(msg, neko_log_deprecation)
373 write(msg, '(A,A,A)') 'The feature "', trim(feature), '" is deprecated.'
374 call this%message(msg, neko_log_deprecation)
375 write(msg, '(A,A,A)') 'It will be removed in version ', &
376 trim(removal_version), '.'
377 call this%message(msg, neko_log_deprecation)
378
379 if (present(extra_info)) then
380 call this%message(extra_info, neko_log_deprecation)
381 end if
382
383 call this%message('***', neko_log_deprecation)
384
385 deprecation_error = ""
386 call get_environment_variable("NEKO_DEPRECATION_ERROR", &
387 deprecation_error)
388
389 if (trim(deprecation_error) .eq. "1") then
390 call neko_error('Deprecated feature "' // trim(feature) // &
391 '" is scheduled for removal in version: ' // &
392 trim(removal_version) // ' (current version: ' // &
393 trim(neko_version) // ').')
394 else if (is_deprecated(removal_version)) then
395 call neko_warning('Deprecated feature "' // trim(feature) // &
396 '" is scheduled for removal in version: ' // &
397 trim(removal_version) // ' (current version: ' // &
398 trim(neko_version) // ').')
399 end if
400 end if
401
402 end subroutine log_deprecated
403
405 subroutine log_section(this, msg, lvl)
406 class(log_t), intent(inout) :: this
407 character(len=*), intent(in) :: msg
408 integer, optional :: lvl
409
410 integer :: pre, pos
411 if (pe_rank .ne. 0) return
412
413 if (len_trim(this%section_header) .gt. 0) then
414 call this%print_section_header(lvl)
415 end if
416
417 call this%begin()
418
419 pre = (30 - len_trim(msg)) / 2
420 pos = 30 - (len_trim(msg) + pre)
421
422 if (pre .lt. 0 .or. pos .lt. 0) then
423 pre = 1
424 pos = 1
425 write(this%section_header, '(A,A,A)') &
426 repeat('-', pre), trim(msg(1: sec_head_size - 2)), &
427 repeat('-', pos)
428 else
429 write(this%section_header, '(A,A,A)') &
430 repeat('-', pre), trim(msg), repeat('-', pos)
431 end if
432
433 end subroutine log_section
434
436 subroutine log_print_section_header(this, lvl)
437 class(log_t), intent(inout) :: this
438 integer, optional :: lvl
439 integer :: lvl_
440 if (pe_rank .ne. 0) return
441
442 if (present(lvl)) then
443 lvl_ = lvl
444 else
445 lvl_ = neko_log_info
446 end if
447
448 if (lvl_ .gt. this%level_) then
449 return
450 end if
451
452 call this%newline(lvl)
453 call this%indent()
454 write(this%unit_, '(A)') trim(this%section_header)
455 this%section_header = ""
456
457 end subroutine log_print_section_header
458
460 subroutine log_end_section(this, msg, lvl)
461 class(log_t), intent(inout) :: this
462 character(len=*), intent(in), optional :: msg
463 integer, optional :: lvl
464 if (pe_rank .ne. 0) return
465
466 if (present(msg)) then
467 call this%message(msg, lvl)
468 end if
469
470 call this%end()
471
472 end subroutine log_end_section
473
474 !
475 ! Rudimentary C interface
476 !
477
480 subroutine log_message_c(c_msg) bind(c, name = 'log_message')
481 use, intrinsic :: iso_c_binding
482 character(kind=c_char), dimension(*), intent(in) :: c_msg
483 character(len=LOG_SIZE) :: msg
484 integer :: len
485 if (pe_rank .ne. 0) return
486
487 len = 0
488 do
489 if (c_msg(len+1) .eq. c_null_char) exit
490 len = len + 1
491 msg(len:len) = c_msg(len)
492 end do
493
494 call neko_log%message(trim(msg(1:len)))
495
496 end subroutine log_message_c
497
500 subroutine log_error_c(c_msg) bind(c, name = "log_error")
501 use, intrinsic :: iso_c_binding
502 character(kind=c_char), dimension(*), intent(in) :: c_msg
503 character(len=LOG_SIZE) :: msg
504 integer :: len
505 if (pe_rank .ne. 0) return
506
507 len = 0
508 do
509 if (c_msg(len+1) .eq. c_null_char) exit
510 len = len + 1
511 msg(len:len) = c_msg(len)
512 end do
513
514 call neko_log%flush()
515 call neko_log%indent()
516 write(stderr, '(A,A,A)') '*** ERROR: ', trim(msg(1:len)), ' ***'
517 flush(stderr)
518
519 end subroutine log_error_c
520
523 subroutine log_warning_c(c_msg) bind(c, name = "log_warning")
524 use, intrinsic :: iso_c_binding
525 character(kind=c_char), dimension(*), intent(in) :: c_msg
526 character(len=LOG_SIZE) :: msg
527 integer :: len
528 if (pe_rank .ne. 0) return
529
530 len = 0
531 do
532 if (c_msg(len+1) .eq. c_null_char) exit
533 len = len + 1
534 msg(len:len) = c_msg(len)
535 end do
536
537 call neko_log%indent()
538 write(neko_log%unit_, '(A,A,A)') &
539 '*** WARNING: ', trim(msg(1:len)), ' ***'
540 call neko_log%flush()
541
542 end subroutine log_warning_c
543
546 subroutine log_section_c(c_msg) bind(c, name = "log_section")
547 use, intrinsic :: iso_c_binding
548 character(kind=c_char), dimension(*), intent(in) :: c_msg
549 character(len=LOG_SIZE) :: msg
550 integer :: len
551 if (pe_rank .ne. 0) return
552
553 len = 0
554 do
555 if (c_msg(len+1) .eq. c_null_char) exit
556 len = len + 1
557 msg(len:len) = c_msg(len)
558 end do
559
560 call neko_log%section(trim(msg(1:len)))
561
562 end subroutine log_section_c
563
566 subroutine log_end_section_c() bind(c, name = "log_end_section")
567
568 call neko_log%end_section()
569
570 end subroutine log_end_section_c
571
576 logical function is_deprecated(version_removal)
577 character(len=*), intent(in) :: version_removal
578 character(len=50) :: current_str, removal_str
579 integer :: current_number(3), removal_number(3)
580 integer :: i, current_size, removal_size
581 integer :: iostat_current, iostat_removal
582 logical :: versions_are_valid, is_newer_than_removal
583
584 current_str = trim(neko_version)
585 removal_str = trim(version_removal)
586
587 current_size = 1
588 do i = 1, len_trim(current_str)
589 if (current_str(i:i) .eq. '.') then
590 current_str(i:i) = ' '
591 current_size = current_size + 1
592 end if
593 end do
594
595 removal_size = 1
596 do i = 1, len_trim(removal_str)
597 if (removal_str(i:i) .eq. '.') then
598 removal_str(i:i) = ' '
599 removal_size = removal_size + 1
600 end if
601 end do
602
603 read(current_str, *, iostat = iostat_current) &
604 current_number(1:current_size)
605 read(removal_str, *, iostat = iostat_removal) &
606 removal_number(1:removal_size)
607 versions_are_valid = (iostat_current .eq. 0 .and. iostat_removal .eq. 0)
608
609 if (.not. versions_are_valid) then
610 call neko_error('Invalid version string in deprecation check: ' // &
611 'NEKO_VERSION=' // trim(neko_version) // ', ' // &
612 'removal_version=' // trim(version_removal))
613 end if
614
615 is_deprecated = .true.
616 do i = 1, current_size
617 if (current_number(i) .gt. removal_number(i)) then
618 is_deprecated = .true.
619 exit
620 else if (current_number(i) .lt. removal_number(i)) then
621 is_deprecated = .false.
622 exit
623 end if
624 end do
625
626 end function is_deprecated
627end module logger
Definition comm.F90:1
integer, public pe_rank
MPI rank.
Definition comm.F90:59
Module for file I/O operations.
Definition file.f90:34
Logging routines.
Definition log.f90:34
subroutine log_end_section_c()
End a log section (from C)
Definition log.f90:567
subroutine log_end(this)
Decrease indention level.
Definition log.f90:220
subroutine log_print_section_header(this, lvl)
Print a section header.
Definition log.f90:437
pure integer function log_level(this)
Return the log level. Messages with a level above it are not written.
Definition log.f90:200
subroutine log_deprecated(this, feature, removal_version, extra_info)
Write a deprecation warning to a log.
Definition log.f90:340
subroutine log_header(this, version, build_info)
Write the Neko header to a log.
Definition log.f90:293
integer, parameter, public neko_log_deprecation
Deprecation.
Definition log.f90:55
integer, parameter, public neko_log_verbose
Verbose.
Definition log.f90:54
subroutine log_message(this, msg, lvl)
Write a message to a log.
Definition log.f90:266
integer, parameter, public neko_log_quiet
Always.
Definition log.f90:52
subroutine log_begin(this)
Increase indention level.
Definition log.f90:210
subroutine log_message_c(c_msg)
Write a message to a log (from C)
Definition log.f90:481
subroutine log_end_section(this, msg, lvl)
End a log section.
Definition log.f90:461
subroutine log_warning(this, msg)
Write a warning message to a log.
Definition log.f90:325
subroutine log_init(this, env_prefix)
Initialize a log.
Definition log.f90:102
subroutine log_error_c(c_msg)
Write an error message to a log (from C)
Definition log.f90:501
subroutine log_indent(this)
Indent a log.
Definition log.f90:235
subroutine log_flush(this)
Flush the log stream, ensuring buffered output leaves the process.
Definition log.f90:188
subroutine log_warning_c(c_msg)
Write a warning message to a log (from C)
Definition log.f90:524
subroutine log_newline(this, lvl)
Write a new line to a log.
Definition log.f90:244
logical function is_deprecated(version_removal)
Compare version strings.
Definition log.f90:577
integer, parameter, public sec_head_size
Length of the section header.
Definition log.f90:49
subroutine log_section_c(c_msg)
Begin a new log section (from C)
Definition log.f90:547
integer, parameter, public neko_log_debug
Debug.
Definition log.f90:56
type(log_t), public neko_log
Global log stream.
Definition log.f90:92
integer, parameter, public log_size
Definition log.f90:46
integer, parameter, public neko_log_info
Default.
Definition log.f90:53
subroutine log_free(this)
Free a log.
Definition log.f90:149
subroutine log_section(this, msg, lvl)
Begin a new log section.
Definition log.f90:406
subroutine log_error(this, msg)
Write an error message to a log.
Definition log.f90:313
Build configurations.
character(len=10), parameter neko_version
Utilities.
Definition utils.f90:35
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:454