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
86 procedure, private, pass(this) :: print_section_header => &
88 end type log_t
89
91 type(log_t), public :: neko_log
92
93contains
94
100 subroutine log_init(this, env_prefix)
101 class(log_t), intent(inout) :: this
102 character(len=*), intent(in), optional :: env_prefix
103 character(len=255) :: log_level
104 character(len=255) :: log_tab_size
105 character(len=255) :: log_file
106 character(len=32) :: prefix
107 integer :: envvar_len
108
109 if (present(env_prefix)) then
110 prefix = env_prefix
111 else
112 prefix = "NEKO"
113 end if
114
115 this%indent_ = 0
116 this%section_id_ = 0
117
118 call get_environment_variable(trim(prefix) // "_LOG_TAB_SIZE", &
119 log_tab_size, envvar_len)
120 if (envvar_len .gt. 0) then
121 read(log_tab_size(1:envvar_len), *) this%tab_size_
122 else
123 this%tab_size_ = 1
124 end if
125
126 call get_environment_variable(trim(prefix) // "_LOG_LEVEL", &
127 log_level, envvar_len)
128 if (envvar_len .gt. 0) then
129 read(log_level(1:envvar_len), *) this%level_
130 else
131 this%level_ = neko_log_info
132 end if
133
134 call get_environment_variable(trim(prefix) // "_LOG_FILE", &
135 log_file, envvar_len)
136 if (envvar_len .gt. 0) then
137 open(newunit = this%unit_, file = trim(log_file), status = 'replace', &
138 action = 'write')
139 else
140 this%unit_ = stdout
141 end if
142
143 end subroutine log_init
144
146 subroutine log_free(this)
147 class(log_t), intent(inout) :: this
148 integer :: i
149
150 ! Flush the log before closing off.
151 call this%flush()
152
153 if (this%section_id_ .ne. 0) then
154 call neko_error("Log is unbalanced")
155 end if
156
157 if (allocated(this%deprecated_list)) then
158 call this%section("Deprecated features summary", neko_log_deprecation)
159
160 do i = 1, size(this%deprecated_list)
161 call this%message(trim(this%deprecated_list(i)), neko_log_deprecation)
162 end do
163 call this%end_section()
164 end if
165
166 if (this%unit_ .ne. stdout) then
167 close(this%unit_)
168 end if
169
170 this%indent_ = 0
171 this%section_id_ = 0
172 this%tab_size_ = 1
173 this%level_ = neko_log_info
174 this%unit_ = stdout
175
176 if (allocated(this%deprecated_list)) then
177 deallocate(this%deprecated_list)
178 end if
179
180 end subroutine log_free
181
185 subroutine log_flush(this)
186 class(log_t), intent(in) :: this
187
188 ! The unit is compared against the sentinel value set by `log_free`, and
189 ! not simply tested for being positive, since NEWUNIT-assigned units
190 ! (used for NEKO_LOG_FILE) are always negative
191 if (pe_rank .eq. 0) then
192 flush(this%unit_)
193 end if
194
195 end subroutine log_flush
196
198 subroutine log_begin(this)
199 class(log_t), intent(inout) :: this
200
201 if (pe_rank .eq. 0) then
202 this%section_id_ = this%section_id_ + 1
203 this%indent_ = this%indent_ + this%tab_size_
204 end if
205
206 end subroutine log_begin
207
209 subroutine log_end(this)
210 class(log_t), intent(inout) :: this
211
212 if (pe_rank .eq. 0) then
213 if (this%section_id_ .eq. 0) then
214 call neko_error("Log is unbalanced")
215 end if
216 this%section_id_ = this%section_id_ - 1
217 this%indent_ = this%indent_ - this%tab_size_
218 end if
219
220 this%section_header = ""
221
222 end subroutine log_end
223
225 subroutine log_indent(this)
226 class(log_t), intent(in) :: this
227
228 if (pe_rank .eq. 0) then
229 write(this%unit_, '(A)', advance = 'no') repeat(' ', this%indent_)
230 end if
231
232 end subroutine log_indent
233
235 subroutine log_newline(this, lvl)
236 class(log_t), intent(in) :: this
237 integer, optional :: lvl
238
239 integer :: lvl_
240
241 if (present(lvl)) then
242 lvl_ = lvl
243 else
244 lvl_ = neko_log_info
245 end if
246
247 if (lvl_ .gt. this%level_) then
248 return
249 end if
250
251 if (pe_rank .eq. 0) then
252 write(this%unit_, '(A)') ''
253 end if
254
255 end subroutine log_newline
256
258 subroutine log_message(this, msg, lvl)
259 class(log_t), intent(inout) :: this
260 character(len=*), intent(in) :: msg
261 integer, optional :: lvl
262 integer :: lvl_
263
264 if (present(lvl)) then
265 lvl_ = lvl
266 else
267 lvl_ = neko_log_info
268 end if
269
270 if (lvl_ .gt. this%level_) then
271 return
272 end if
273
274 if (len_trim(this%section_header) .gt. 0) then
275 call this%print_section_header(lvl)
276 end if
277
278 if (pe_rank .eq. 0) then
279 call this%indent()
280 write(this%unit_, '(A)') trim(msg)
281 end if
282
283 end subroutine log_message
284
286 subroutine log_header(this, version, build_info)
287 class(log_t), intent(in) :: this
288 character(len=*), intent(in) :: version
289 character(len=*), intent(in) :: build_info
290
291 if (pe_rank .eq. 0) then
292 write(this%unit_, '(A)') ''
293 write(this%unit_, '(1X,A)') ' _ __ ____ __ __ ____ '
294 write(this%unit_, '(1X,A)') ' / |/ / / __/ / //_/ / __ \ '
295 write(this%unit_, '(1X,A)') ' / / / _/ / ,< / /_/ / '
296 write(this%unit_, '(1X,A)') '/_/|_/ /___/ /_/|_| \____/ '
297 write(this%unit_, '(A)') ''
298 write(this%unit_, '(1X,A,A,A)') '(version: ', trim(version), ')'
299 write(this%unit_, '(1X,A)') trim(build_info)
300 write(this%unit_, '(A)') ''
301 flush(this%unit_)
302 end if
303
304 end subroutine log_header
305
307 subroutine log_error(this, msg)
308 class(log_t), intent(in) :: this
309 character(len=*), intent(in) :: msg
310
311 if (pe_rank .eq. 0) then
312 call this%flush()
313 call this%indent()
314 call neko_error(trim(msg))
315 end if
316
317 end subroutine log_error
318
320 subroutine log_warning(this, msg)
321 class(log_t), intent(in) :: this
322 character(len=*), intent(in) :: msg
323
324 if (pe_rank .eq. 0) then
325 call this%indent()
326 write(this%unit_, '(A,A,A)') '*** WARNING: ', trim(msg), ' ***'
327 call this%flush()
328 end if
329
330 end subroutine log_warning
331
336 subroutine log_deprecated(this, feature, removal_version, extra_info)
337 class(log_t), intent(inout) :: this
338 character(len=*), intent(in) :: feature
339 character(len=*), intent(in) :: removal_version
340 character(len=*), intent(in), optional :: extra_info
341 character(len=50), dimension(:), allocatable :: tmp_list
342 character(len=255) :: deprecation_error
343 character(len=LOG_SIZE) :: msg
344 integer :: i
345
346 if (pe_rank .ne. 0) return
347
348 if (this%level_ .ge. neko_log_deprecation .or. &
349 is_deprecated(removal_version)) then
350
351 ! Check that the feature have not already been logged
352 if (.not. allocated(this%deprecated_list)) then
353 allocate(character(len=50) :: this%deprecated_list(1))
354 this%deprecated_list = trim(feature)
355 else
356 do i = 1, size(this%deprecated_list)
357 if (trim(this%deprecated_list(i)) .eq. trim(feature)) return
358 end do
359
360 ! Save the feature to the list of deprecated features
361 call move_alloc(this%deprecated_list, tmp_list)
362 allocate(character(len=50)::this%deprecated_list(size(tmp_list)+1))
363 this%deprecated_list(1:size(tmp_list)) = tmp_list
364 this%deprecated_list(size(tmp_list) + 1) = trim(feature)
365 deallocate(tmp_list)
366 end if
367
368 ! Construct deprecation message
369 write(msg, '(A,A)') '*** DEPRECATION: ', trim(feature)
370 call this%message(msg, neko_log_deprecation)
371 write(msg, '(A,A,A)') 'The feature "', trim(feature), '" is deprecated.'
372 call this%message(msg, neko_log_deprecation)
373 write(msg, '(A,A,A)') 'It will be removed in version ', &
374 trim(removal_version), '.'
375 call this%message(msg, neko_log_deprecation)
376
377 if (present(extra_info)) then
378 call this%message(extra_info, neko_log_deprecation)
379 end if
380
381 call this%message('***', neko_log_deprecation)
382
383 deprecation_error = ""
384 call get_environment_variable("NEKO_DEPRECATION_ERROR", &
385 deprecation_error)
386
387 if (trim(deprecation_error) .eq. "1") then
388 call neko_error('Deprecated feature "' // trim(feature) // &
389 '" is scheduled for removal in version: ' // &
390 trim(removal_version) // ' (current version: ' // &
391 trim(neko_version) // ').')
392 else if (is_deprecated(removal_version)) then
393 call neko_warning('Deprecated feature "' // trim(feature) // &
394 '" is scheduled for removal in version: ' // &
395 trim(removal_version) // ' (current version: ' // &
396 trim(neko_version) // ').')
397 end if
398 end if
399
400 end subroutine log_deprecated
401
403 subroutine log_section(this, msg, lvl)
404 class(log_t), intent(inout) :: this
405 character(len=*), intent(in) :: msg
406 integer, optional :: lvl
407
408 integer :: pre, pos
409
410 if (len_trim(this%section_header) .gt. 0) then
411 call this%print_section_header(lvl)
412 end if
413
414 call this%begin()
415
416 if (pe_rank .eq. 0) then
417 pre = (30 - len_trim(msg)) / 2
418 pos = 30 - (len_trim(msg) + pre)
419
420 if (pre .lt. 0 .or. pos .lt. 0) then
421 pre = 1
422 pos = 1
423 write(this%section_header, '(A,A,A)') &
424 repeat('-', pre), trim(msg(1: sec_head_size - 2)), &
425 repeat('-', pos)
426 else
427 write(this%section_header, '(A,A,A)') &
428 repeat('-', pre), trim(msg), repeat('-', pos)
429 end if
430 end if
431
432 end subroutine log_section
433
435 subroutine log_print_section_header(this, lvl)
436 class(log_t), intent(inout) :: this
437 integer, optional :: lvl
438 integer :: lvl_
439
440 if (present(lvl)) then
441 lvl_ = lvl
442 else
443 lvl_ = neko_log_info
444 end if
445
446 if (lvl_ .gt. this%level_) then
447 return
448 end if
449
450 if (pe_rank .eq. 0) then
451 call this%newline(lvl)
452 call this%indent()
453 write(this%unit_, '(A)') trim(this%section_header)
454 this%section_header = ""
455 end if
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
465 if (present(msg)) then
466 call this%message(msg, lvl)
467 end if
468
469 call this%end()
470
471 end subroutine log_end_section
472
473 !
474 ! Rudimentary C interface
475 !
476
479 subroutine log_message_c(c_msg) bind(c, name = 'log_message')
480 use, intrinsic :: iso_c_binding
481 character(kind=c_char), dimension(*), intent(in) :: c_msg
482 character(len=LOG_SIZE) :: msg
483 integer :: len
484
485 if (pe_rank .eq. 0) then
486 len = 0
487 do
488 if (c_msg(len+1) .eq. c_null_char) exit
489 len = len + 1
490 msg(len:len) = c_msg(len)
491 end do
492
493 call neko_log%message(trim(msg(1:len)))
494 end if
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
506 if (pe_rank .eq. 0) then
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 end if
519
520 end subroutine log_error_c
521
524 subroutine log_warning_c(c_msg) bind(c, name = "log_warning")
525 use, intrinsic :: iso_c_binding
526 character(kind=c_char), dimension(*), intent(in) :: c_msg
527 character(len=LOG_SIZE) :: msg
528 integer :: len
529
530 if (pe_rank .eq. 0) then
531 len = 0
532 do
533 if (c_msg(len+1) .eq. c_null_char) exit
534 len = len + 1
535 msg(len:len) = c_msg(len)
536 end do
537
538 call neko_log%indent()
539 write(neko_log%unit_, '(A,A,A)') &
540 '*** WARNING: ', trim(msg(1:len)), ' ***'
541 call neko_log%flush()
542 end if
543
544 end subroutine log_warning_c
545
548 subroutine log_section_c(c_msg) bind(c, name = "log_section")
549 use, intrinsic :: iso_c_binding
550 character(kind=c_char), dimension(*), intent(in) :: c_msg
551 character(len=LOG_SIZE) :: msg
552 integer :: len
553
554 if (pe_rank .eq. 0) then
555 len = 0
556 do
557 if (c_msg(len+1) .eq. c_null_char) exit
558 len = len + 1
559 msg(len:len) = c_msg(len)
560 end do
561
562 call neko_log%section(trim(msg(1:len)))
563 end if
564
565 end subroutine log_section_c
566
569 subroutine log_end_section_c() bind(c, name = "log_end_section")
570
571 call neko_log%end_section()
572
573 end subroutine log_end_section_c
574
579 logical function is_deprecated(version_removal)
580 character(len=*), intent(in) :: version_removal
581 character(len=50) :: current_str, removal_str
582 integer :: current_number(3), removal_number(3)
583 integer :: i, current_size, removal_size
584 integer :: iostat_current, iostat_removal
585 logical :: versions_are_valid, is_newer_than_removal
586
587 current_str = trim(neko_version)
588 removal_str = trim(version_removal)
589
590 current_size = 1
591 do i = 1, len_trim(current_str)
592 if (current_str(i:i) .eq. '.') then
593 current_str(i:i) = ' '
594 current_size = current_size + 1
595 end if
596 end do
597
598 removal_size = 1
599 do i = 1, len_trim(removal_str)
600 if (removal_str(i:i) .eq. '.') then
601 removal_str(i:i) = ' '
602 removal_size = removal_size + 1
603 end if
604 end do
605
606 read(current_str, *, iostat = iostat_current) &
607 current_number(1:current_size)
608 read(removal_str, *, iostat = iostat_removal) &
609 removal_number(1:removal_size)
610 versions_are_valid = (iostat_current .eq. 0 .and. iostat_removal .eq. 0)
611
612 if (.not. versions_are_valid) then
613 call neko_error('Invalid version string in deprecation check: ' // &
614 'NEKO_VERSION=' // trim(neko_version) // ', ' // &
615 'removal_version=' // trim(version_removal))
616 end if
617
618 is_deprecated = .true.
619 do i = 1, current_size
620 if (current_number(i) .gt. removal_number(i)) then
621 is_deprecated = .true.
622 exit
623 else if (current_number(i) .lt. removal_number(i)) then
624 is_deprecated = .false.
625 exit
626 end if
627 end do
628
629 end function is_deprecated
630end 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:570
subroutine log_end(this)
Decrease indention level.
Definition log.f90:210
subroutine log_print_section_header(this, lvl)
Print a section header.
Definition log.f90:436
subroutine log_deprecated(this, feature, removal_version, extra_info)
Write a deprecation warning to a log.
Definition log.f90:337
subroutine log_header(this, version, build_info)
Write the Neko header to a log.
Definition log.f90:287
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:259
integer, parameter, public neko_log_quiet
Always.
Definition log.f90:52
subroutine log_begin(this)
Increase indention level.
Definition log.f90:199
subroutine log_message_c(c_msg)
Write a message to a log (from C)
Definition log.f90:480
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:321
subroutine log_init(this, env_prefix)
Initialize a log.
Definition log.f90:101
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:226
subroutine log_flush(this)
Flush the log stream, ensuring buffered output leaves the process.
Definition log.f90:186
subroutine log_warning_c(c_msg)
Write a warning message to a log (from C)
Definition log.f90:525
subroutine log_newline(this, lvl)
Write a new line to a log.
Definition log.f90:236
logical function is_deprecated(version_removal)
Compare version strings.
Definition log.f90:580
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:549
integer, parameter, public neko_log_debug
Debug.
Definition log.f90:56
type(log_t), public neko_log
Global log stream.
Definition log.f90:91
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:147
subroutine log_section(this, msg, lvl)
Begin a new log section.
Definition log.f90:404
subroutine log_error(this, msg)
Write an error message to a log.
Definition log.f90:308
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:452