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