1
- """fancylog
2
- ===============
3
-
4
- Wrapper around the standard logging module, with additional information.
5
-
6
- """
1
+ """Wrapper around the standard logging module, with additional information."""
7
2
8
3
import contextlib
9
4
import logging
@@ -39,32 +34,51 @@ def start_logging(
39
34
timestamp = True ,
40
35
logger_name = None ,
41
36
):
42
- """Prepares the log file, and then begins logging.
43
-
44
- :param output_dir: Directory to save the log file
45
- :param package: What python package are we logging?
46
- :param variables: List of objects whose attributes we want to log at the
47
- beginning of the log file
48
- :param verbose: If true, all info (i.e. 'DEBUG') is printed to
49
- console. Else, only 'INFO' and above. Default: True
50
- :param file_log_level: What level of logging to print to file.
51
- Default: 'DEBUG'
52
- :param filename: Filename for log file. Default: 'package.__name__'
53
- :param log_header: Header for the log file, if the args are written'
54
- :param multiprocessing_aware: Log from multiple processes. Default: True
55
- :param write_header: Write a header for the log file. Default: True
56
- :param write_git: Write information about the git repository.
57
- Default: True
58
- :param write_cli_args: Log the command-line arguments. Default: True
59
- :param write_variables: Write the attributes of selected objects.
60
- Default: True
61
- :param log_to_file: If True, write a log file, otherwise just print to
62
- terminal.
63
- :param log_to_console: Print logs to the console or not: Default: True
64
- :param timestamp: If True, add a timestamp to the filename
65
- :param logger_name: If None, logger uses default logger. Otherwise,
66
- logger name is set to `logger_name`.
67
- :return: Path to the logging file#
37
+ """Prepare the log file, and then begin logging.
38
+
39
+ Parameters
40
+ ----------
41
+ output_dir
42
+ Directory to save the log file.
43
+ package
44
+ What Python package are we logging?
45
+ variables
46
+ List of objects whose attributes we want to log at the
47
+ beginning of the log file.
48
+ verbose
49
+ If True, all info (i.e. 'DEBUG') is printed to console;
50
+ else only 'INFO' and above. Default: True
51
+ file_log_level
52
+ What level of logging to print to file. Default: 'DEBUG'
53
+ filename
54
+ Filename for log file. Default: 'package.__name__'
55
+ log_header
56
+ Header for the log file, if the args are written.
57
+ multiprocessing_aware
58
+ Log from multiple processes. Default: True
59
+ write_header
60
+ Write a header for the log file. Default: True
61
+ write_git
62
+ Write information about the git repository. Default: True
63
+ write_cli_args
64
+ Log the command-line arguments. Default: True
65
+ write_variables
66
+ Write the attributes of selected objects. Default: True
67
+ log_to_file
68
+ If True, write a log file; otherwise just print to terminal.
69
+ log_to_console
70
+ Print logs to the console or not. Default: True
71
+ timestamp
72
+ If True, add a timestamp to the filename.
73
+ logger_name
74
+ If None, logger uses default logger; otherwise, logger
75
+ name is set to `logger_name`.
76
+
77
+ Returns
78
+ -------
79
+ path
80
+ Path to the logging file.
81
+
68
82
"""
69
83
output_dir = str (output_dir )
70
84
print_log_level = "DEBUG" if verbose else "INFO"
@@ -107,10 +121,12 @@ def start_logging(
107
121
108
122
109
123
class LoggingHeader :
124
+ """Manage and write the log header."""
125
+
110
126
def __init__ (
111
127
self ,
112
128
file ,
113
- program ,
129
+ program , # TODO: should this be called package?
114
130
variable_objects ,
115
131
output_dir ,
116
132
write_header = True ,
@@ -119,6 +135,10 @@ def __init__(
119
135
write_variables = True ,
120
136
log_header = None ,
121
137
):
138
+ """Initialize LoggingHeader and write header to the log file.
139
+
140
+ See start_logging() for parameters.
141
+ """
122
142
self .program = program
123
143
124
144
with open (file , "w" , encoding = "utf-8" ) as self .file :
@@ -132,6 +152,17 @@ def __init__(
132
152
self .write_variables (variable_objects )
133
153
134
154
def write_git_info (self , program_name , header = "GIT INFO" ):
155
+ """Write information about the git repository state.
156
+
157
+ Parameters
158
+ ----------
159
+ program_name
160
+ The name of the installed package, to
161
+ locate and inspect its Git repository.
162
+ header
163
+ The section header to use. Default is "GIT INFO".
164
+
165
+ """
135
166
self .write_separated_section_header (header )
136
167
try :
137
168
program_path = find_spec (program_name ).submodule_search_locations [
@@ -163,11 +194,27 @@ def write_git_info(self, program_name, header="GIT INFO"):
163
194
)
164
195
165
196
def write_command_line_arguments (self , header = "COMMAND LINE ARGUMENTS" ):
197
+ """Write the command-line arguments used to run the script.
198
+
199
+ Parameters
200
+ ----------
201
+ header
202
+ Title of the section that will be written to the log file.
203
+
204
+ """
166
205
self .write_separated_section_header (header )
167
206
self .file .write (f"Command: { sys .argv [0 ]} \n " )
168
207
self .file .write (f"Input arguments: { sys .argv [1 :]} " )
169
208
170
209
def write_variables (self , variable_objects ):
210
+ """Write a section for variables with their values.
211
+
212
+ Parameters
213
+ ----------
214
+ variable_objects
215
+ A list of python objects, the attributes of which will be written.
216
+
217
+ """
171
218
self .write_separated_section_header ("VARIABLES" , bottom_separator = "\n " )
172
219
if hasattr (variable_objects [0 ], "__dict__" ):
173
220
self .write_variables_from_object_list (variable_objects )
@@ -176,10 +223,27 @@ def write_variables(self, variable_objects):
176
223
self .write_separated_section_header ("LOGGING" )
177
224
178
225
def write_variables_from_slot_type_list (self , variable_objects ):
226
+ """Write variables and their values from a namedtuple-like object.
227
+
228
+ Parameters
229
+ ----------
230
+ variable_objects
231
+ An object with a `_asdict()` method (e.g., a namedtuple)
232
+ containing variables to write.
233
+
234
+ """
179
235
for attr , value in variable_objects ._asdict ().items ():
180
236
self .file .write (f"{ attr } : { value } \n " )
181
237
182
238
def write_variables_from_object_list (self , variable_objects ):
239
+ """Write attributes of each object in a list.
240
+
241
+ Parameters
242
+ ----------
243
+ variable_objects
244
+ A list of objects whose attributes will be written to the file.
245
+
246
+ """
183
247
for variable_object in variable_objects :
184
248
self .file .write (f"\n { variable_object .__class__ .__name__ } :\n " )
185
249
for attr , value in variable_object .__dict__ .items ():
@@ -188,6 +252,19 @@ def write_variables_from_object_list(self, variable_objects):
188
252
self .file .write (f"{ attr } : { value } \n " )
189
253
190
254
def write_log_header (self , output_dir , log_header ):
255
+ """Write the log header.
256
+
257
+ The header includes time, output directory,
258
+ and current directory.
259
+
260
+ Parameters
261
+ ----------
262
+ output_dir
263
+ The path to the output directory to include in the log.
264
+ log_header
265
+ Optional custom header text. If None, defaults to "LOG".
266
+
267
+ """
191
268
if log_header is None :
192
269
log_header = "LOG"
193
270
self .write_separated_section_header (log_header , top_sep = False )
@@ -207,18 +284,53 @@ def write_separated_section_header(
207
284
top_separator = "\n \n " ,
208
285
bottom_separator = "\n \n " ,
209
286
):
287
+ r"""Write a section header with optional top and bottom separators.
288
+
289
+ Parameters
290
+ ----------
291
+ section_header
292
+ The section header text to write.
293
+ top_sep
294
+ Whether to write the top separator before the section header.
295
+ bottom_sep
296
+ Whether to write the bottom separator after the section header.
297
+ top_separator
298
+ The string to use as the top separator. Default: "\\n\\n"
299
+ bottom_separator
300
+ The string to use as the bottom separator. Default: "\\n\\n"
301
+
302
+ """
210
303
if top_sep :
211
304
self .write_separator (separator = top_separator )
212
305
self .write_section_header (section_header )
213
306
if bottom_sep :
214
307
self .write_separator (separator = bottom_separator )
215
308
216
309
def write_separator (self , separator = "\n \n \n " ):
310
+ r"""Write a separator string to the file.
311
+
312
+ Parameters
313
+ ----------
314
+ separator
315
+ The separator string to write. Default: "\\n\\n\\n"
316
+
317
+ """
217
318
self .file .write (separator )
218
319
219
320
def write_section_header (
220
321
self , section_header , lateral_separator = "**************"
221
322
):
323
+ """Write a section header framed by a lateral separator.
324
+
325
+ Parameters
326
+ ----------
327
+ section_header
328
+ The section header text to write.
329
+ lateral_separator
330
+ The string used to frame the section header.
331
+ Default: "**************"
332
+
333
+ """
222
334
self .file .write (
223
335
f"{ lateral_separator } { section_header } { lateral_separator } "
224
336
)
@@ -231,15 +343,22 @@ def initialise_logger(
231
343
log_to_console = True ,
232
344
logger_name = None ,
233
345
):
234
- """Sets up (possibly multiprocessing aware) logging.
235
- :param filename: Where to save the logs to
236
- :param print_level: What level of logging to print to console.
237
- Default: 'INFO'
238
- :param file_level: What level of logging to print to file.
239
- Default: 'DEBUG'
240
- :param log_to_console: Print logs to the console or not
241
- :param logger_name: If None, logger uses default logger. Otherwise,
242
- logger name is set to `logger_name`.
346
+ """Set up (possibly multiprocessing aware) logging.
347
+
348
+ Parameters
349
+ ----------
350
+ filename
351
+ Where to save the logs to.
352
+ print_level
353
+ What level of logging to print to console. Default: 'INFO'
354
+ file_level
355
+ What level of logging to print to file. Default: 'DEBUG'
356
+ log_to_console
357
+ Print logs to the console or not.
358
+ logger_name
359
+ If None, logger uses default logger. Otherwise, logger name
360
+ is set to `logger_name`.
361
+
243
362
"""
244
363
if logger_name :
245
364
logger = logging .getLogger (logger_name )
@@ -280,17 +399,23 @@ def setup_logging(
280
399
log_to_console = True ,
281
400
logger_name = None ,
282
401
):
283
- """Sets up (possibly multiprocessing aware) logging.
284
- :param filename: Where to save the logs to
285
- :param print_level: What level of logging to print to console.
286
- Default: 'INFO'
287
- :param file_level: What level of logging to print to file.
288
- Default: 'DEBUG'
289
- :param multiprocessing_aware: Default: True
290
- :param log_to_console: Print logs to the console or no.
291
- Default: True
292
- :param logger_name: If None, logger uses default logger. Otherwise,
293
- logger name is set to `logger_name`.
402
+ """Set up (possibly multiprocessing-aware) logging.
403
+
404
+ Parameters
405
+ ----------
406
+ filename
407
+ Path to the file where logs will be saved.
408
+ print_level
409
+ Logging level for console output. Default is 'INFO'.
410
+ file_level
411
+ Logging level for file output. Default is 'DEBUG'.
412
+ multiprocessing_aware
413
+ If True, enables multiprocessing-safe logging. Default is True.
414
+ log_to_console
415
+ If True, logs will also be printed to the console. Default is True.
416
+ logger_name
417
+ Name of the logger to use. If None, the default logger is used.
418
+
294
419
"""
295
420
logger = initialise_logger (
296
421
filename ,
@@ -328,8 +453,9 @@ def setup_logging(
328
453
329
454
330
455
def disable_logging ():
331
- """Prevents any more logging. Saves remembering that logging.disable() with
456
+ """Prevent any more logging.
457
+
458
+ Saves remembering that logging.disable() with
332
459
no argument doesn't work.
333
- :return:
334
460
"""
335
461
logging .disable (2 ** 63 - 1 )
0 commit comments