-
Notifications
You must be signed in to change notification settings - Fork 121
/
logging.ncl
385 lines (344 loc) · 10.6 KB
/
logging.ncl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
; #############################################################################
; GENERAL ROUTINES FOR HANDLING LOG OUTPUT
; #############################################################################
; Please consider using of extending existing routines before adding new ones.
; Check the header of each routine for documentation.
;
; Contents:
; procedure log_info
; procedure log_debug
; procedure enter_msg
; procedure leave_msg
; procedure error_msg
; procedure tool_stop
; procedure exit_if_missing_atts
; procedure log_provenance
;
; #############################################################################
; WARNING: no other interface_scripts shall be loaded here, to avoid nested
; loops (since they will try to load logging.ncl themselves).
; #############################################################################
undef("log_info")
procedure log_info(output_string[*]:string)
;
; Arguments
; output_string: the text to be output as message on screen
;
; Description
; Write an info message to the log file. If the input is an array, each
; element will be written on different lines.
;
; Caveats
;
; References
;
; Modification history
; 20180202-righi_mattia: written to replace info_output.
;
local nn, ii
begin
if (any(config_user_info@log_level.eq.(/"info", "debug"/))) then
nn = dimsizes(output_string)
if (nn.eq.1) then
print("INFO " + output_string)
else
do ii = 0, nn - 1
print("INFO " + output_string(ii))
end do
end if
end if
end
; #############################################################################
undef("log_debug")
procedure log_debug(output_string[*]:string)
;
; Arguments
; output_string: the text to be output as message on screen
;
; Description
; Write a debug message to the log file (only if log_level = debug in
; config-user.yml). If the input is an array, each element will be
; written on different lines.
;
; Caveats
;
; References
;
; Modification history
; 20180202-righi_mattia: written to replace info_output.
;
local nn, ii
begin
if (config_user_info@log_level.eq."debug") then
nn = dimsizes(output_string)
if (nn.eq.1) then
print("DEBUG " + output_string)
else
do ii = 0, nn - 1
print("DEBUG " + output_string(ii))
end do
end if
end if
end
; #############################################################################
undef("enter_msg")
procedure enter_msg(script[1]:string,
func[1]:string)
;
; Arguments
; script: name of the ncl script file from where this function is called.
; func: name of the function or procedure calling this function.
;
; Description
; Print an enter message to the log file (debug mode only).
;
; Caveats
;
; References
;
; Modification history
; 20180202-righi_mattia: removed required verbosity.
; 20150319-righi_mattia: written.
;
begin
if (func.eq."") then
log_debug("<<< Entering " + script)
else
log_debug("<<< Entering " + func + " (" + script + ")")
end if
end
; #############################################################################
undef("leave_msg")
procedure leave_msg(script[1]:string,
func[1]:string)
;
; Arguments
; script: name of the ncl script file from where this function is called.
; func: name of the function or procedure calling this function.
;
; Description
; Print a leave message to the log file (debug mode only).
;
; Caveats
;
; References
;
; Modification history
; 20180202-righi_mattia: removed required verbosity.
; 20150319-righi_mattia: written.
;
begin
if (func.eq."") then
log_debug(">>> Leaving " + script)
else
log_debug(">>> Leaving " + func + " (" + script + ")")
end if
end
; #############################################################################
undef("error_msg")
procedure error_msg(type[1]:string,
script[1]:string,
func[1]:string,
msg:string)
;
; Arguments
; type: type of message, "f" for fatal, "w" for warning.
; script: name of the ncl script file from where this function is called.
; func: name of the function or procedure calling this function.
; msg: actual error message.
;
; Description
; Write an error/warning message to the log file.
;
; Caveats
;
; References
;
; Modification history
; 20150924-lauer_axel: added type "error" (treated as "fatal").
; 20140929-righi_mattia: written.
;
local nn, ii, msg_level, msg_origin
begin
nn = dimsizes(msg)
; Determine which message level we're at (fatal/warning)
if (type .eq. "f" .or. type .eq. "fatal") then
msg_level = "fatal"
else if (any(type.eq.(/"w", "warning", "error"/))) then
msg_level = "warning"
else
print("fatal: in error_msg (logging.ncl), " + \
"invalid value for variable type (=" + type + ")")
status_exit(1)
end if
end if
; Determine origin of message
if (func .ne. "") then
msg_origin = func + " (" + script + "), "
else
msg_origin = script + ", "
end if
; Write error/warning message
do ii = 0, nn - 1
log_info(msg_level + ": in " + msg_origin + msg(ii))
end do
; Exit if fatal
if (msg_level .eq. "fatal") then
status_exit(1)
end if
end
; #############################################################################
undef("tool_stop")
procedure tool_stop()
;
; Arguments
;
; Description
; Force the tool execution to stop at any point of an ncl script.
; For dubugging purposes only.
;
; Caveats
; This should not be used for the standard exit-on-error. See the function
; error_msg in logging.ncl instead.
;
; References
;
; Modification history
; 20150521-righi_mattia: written.
;
begin
print("fatal: tool stop forced by the user")
status_exit(1)
end
; #############################################################################
undef("exit_if_missing_atts")
procedure exit_if_missing_atts(in[1], \
reqatts[*]:string)
;
; Arguments
; in: a logical or file variable to be checked for given attributes
; reqatts: an array of strings with the required attributes of the variable
;
; Description
; Check that the given variable contains the given list of attributes.
; This function shall be used at the beginning of each diag_script to
; check that diag_script_info contains the required attributes.
; It can also be used for checking attributes in observations files, when
; these are read directly in the diag_script (e.g. Emmons.ncl).
;
; Caveats
;
; References
;
; Modification history
; 20141002-righi_mattia: written.
;
local funcname, scriptname
begin
funcname = "exit_if_missing_atts"
scriptname = "interface_scripts/logging.ncl"
enter_msg(scriptname, funcname)
if (.not.all(isatt(in, reqatts))) then
error_msg("f", DIAG_SCRIPT, "", "missing required diag_script_info " + \
"attribute: " + reqatts(ind(.not.isatt(in, reqatts))))
end if
leave_msg(scriptname, funcname)
end
; #############################################################################
undef("log_provenance")
procedure log_provenance(nc_file:string,
outfile:string,
caption:string,
statistics[*]:string,
domains[*]:string,
plot_types[*]:string,
diag_authors[*]:string,
diag_refs[*]:string,
infiles[*]:string)
;
; Arguments
; nc_file: netcdf file with data related to the plot
; outfile: file name of the figure (including path)
; caption: figure caption
; statistics: list of metrics/statistics calculated
; domains: of the data displayed
; plot_types: plot type of the figure
; diag_authors: authors related to the diagnostic
; diag_refs: references related to the diagnostic
; infiles: input files (from preproc) used to generate the plot
;
; Description
; Create a yaml file with meta data from the diagnostic script and save it
; in the run directory
;
; Caveats
;
; References
;
; Modification history
; 20190415-righi_mattia: turn domains into a list.
; 20190415-righi_mattia: extended to avoid overwriting previous call.
; 20190225-bock_lisa: written.
;
local funcname, scriptname, yaml_file, outstring, existing
begin
funcname = "log_provenance"
scriptname = "interface_scripts/logging.ncl"
enter_msg(scriptname, funcname)
; Define output
outstring = new(8, string)
; Set yaml file path
yaml_file = config_user_info@run_dir + "diagnostic_provenance.yml"
; Save entries for NetCDF file
outstring(0) = "? " + nc_file
outstring(1) = ": ancestors: [" + str_join(infiles, ", ") + "]"
outstring(2) = " authors: [" + str_join(diag_authors, ", ") + "]"
outstring(3) = " caption: '" + caption + "'"
outstring(4) = " domains: [" + str_join(domains, ", ") + "]"
outstring(5) = " plot_types: [" + str_join(plot_types, ", ") + "]"
outstring(6) = " references: [" + str_join(diag_refs, ", ") + "]"
outstring(7) = " statistics: [" + str_join(statistics, ", ") + "]"
; Save entries for outfile if not "n/a"
if (outfile .ne. "n/a") then
suffix = get_file_suffix(outfile, 0)
; For PNGs, additionally check for existence of files like
; "plot_file.000001.png", "plot_file.000002.png", etc. and save
; provenance record for each of them
if ((suffix .eq. ".png") .and. (.not. fileexists(outfile))) then
do file_idx = 1, 999999
potential_outfile = suffix@fBase + "." + sprinti("%0.6i", file_idx) + \
suffix
if (fileexists(potential_outfile)) then
if (.not. isvar("all_outfiles")) then
all_outfiles = potential_outfile
else
all_outfiles := array_append_record(all_outfiles, \
potential_outfile, 0)
end if
else
break
end if
end do
else
all_outfiles = outfile
end if
; Save provenance record of all files
original_entry = outstring
do outfile_idx = 0, dimsizes(all_outfiles) - 1
file_to_add := all_outfiles(outfile_idx)
new_entry = (/original_entry/)
new_entry(0) = "? " + file_to_add
outstring := array_append_record(outstring, new_entry, 0)
end do
end if
; Save existing information to avoid overwriting
if (fileexists(yaml_file)) then
existing = asciiread(yaml_file, -1, "string")
outstring := array_append_record(existing, outstring, 0)
delete(existing)
end if
; Save provenance information
asciiwrite(yaml_file, outstring)
log_info("Provenance information saved in " + yaml_file)
leave_msg(scriptname, funcname)
end