Logging - nstarman/utilipy GitHub Wiki
Logging can be hacked together when constructing a simple script: throw in a couple print statements, maybe save the output to a file, call it a day. However, as soon as the codebase becomes more complicated, espcially with functions spanning multiple files, logging becomes simultaneously essential and challenging. That's why utilipy
offers easy-to-use and powerful logging functions which can easily be incorporated into functions and scripts. Making a legible logfile should be simple. Now it is.
Adding logging to a script is simple. First we will instantiate a logfile. Then, while doing stuff in the script, we will periodically report on what is going on. The best part is that the loggers can both print and save the output to a file. Now we can have a real-time account and a keepsake for posterity.
This is an example python script using utilipy's
logging functions.
##########################################################################
# IMPORTS
from utilipy import LogFile # import the standard logging
... # other imports
##########################################################################
# SETUP
# setup the logger
# this logger will both print the output and save it to script.log
_LOGFILE = LogFile('script.log')
##########################################################################
# SCRIPT
... # do stuff
_LOGFILE.write('did stuff here') # printing and saving
### Doing Other Stuff
_LOGFILE.newsection(title='Title') # making a new section
... # do more stuff
_LOGFILE.write('did more stuff here') # printing and saving
##########################################################################
# END
the printed output and saved logfile, script.log
, will look like:
script.log Log:
===============================================================================
did stuff here
-------------------------------------------------------------------------------
Title
did more stuff here
For simple scripts like this one, the same result can be accomplished with print statements. However, even at this most basic level we think that this logger method is cleaner and more readable than the print statement equivalent. The next sction, on functions, demonstrates the real power of LogFile
.
function_file.py
##########################################################################
# IMPORTS
from utilipy import LogFile # import the standard logging
... # other imports
##########################################################################
# SETUP
# instantiate a blank logfile to use as a default value
# this PrintLog can only print, not write to a file
_LOGFILE = PrintLog(show_header=False, # prevent displaying
verbose=0) # set report verbosity level
##########################################################################
# CODE
# define a function, including a logger and verbosity
# the logger is defaulted to the printlog
# the verbose=None will use the default verbosity of the logger
def function(*args, logger=_LOGFILE, verbose=None):
... # function does stuff here
# reporting on the workings of the function
logger.report('verbose = 1 message',
'verbose = 2 message',
'verbose >= 3 message',
verbose=verbose)
return output
# /def
##########################################################################
# END
Now, function
can give reports, with different levels of detail, on what it is doing.
Calling function
without specifying a logger or a verbosity will neither print nor write to a file.
function()
>> # nothing
Calling function
without specifying a logger, but giving a verbosity, will print but not write to a file.
function(verbose=1)
>> 'verbose = 1 message' # not saved to file
Calling function
with a logger will print and write to a file, unless the verbosity is 0 or the logger is a PrintLog
, which only prints.
function(logger=LogFile('log.txt'), verbose=2)
>> 'verbose = 2 message' # printed and saved
Here is an example script implementing LogFile
.
script.py
##########################################################################
# IMPORTS
from utilipy import LogFile # import the standard logging function
from .function_file import function
##########################################################################
# SETUP
# instantiate a real logfile and set a verbosity level
_LOGFILE = LogFile('name_of_log.log',
verbose=1)
##########################################################################
# SCRIPT
# call the function with built-in verbosity (verbose=1)
output = function(arg1, arg2, ..., logger=_LOGFILE, verbose=None)
_LOGFILE.newsection('set verbosity')
# call the function with set verbosity
output = function(arg1, arg2, ..., logger=_LOGFILE, verbose=0)
output = function(arg1, arg2, ..., logger=_LOGFILE, verbose=10)
##########################################################################
# END
The output to this script, on both the console and saved file, is:
name_of_log.log Log:
===============================================================================
'verbose = 1 message' # b/c verbosity = 1
-------------------------------------------------------------------------------
set verbosity
'verbose >= 3 message'
Note that the verbose=0
message was neither printed, nor recorded.
All the logging functions are designed to have identical methods, for guaranteed compatibility when passed between functions, even if those methods accomplish slightly things. For instance, PrintLog
has a .write
method even though it cannot write to a file. For PrinLog
, this will print the output (whereas for LogFile
it both prints and writes).
__init__
open
open_to_write
open_to_read
read_log
print
write
record
verbort
report
newsection
close
Docstring
a basic logger which can both print and record to a file
** this class uses `open', not a more extensive logger, like `logging'
The arguments filename - opener are all for `open`
their descriptions are in
https://docs.python.org/3/library/functions.html#open
Parameters
----------
filename : str, optional
the file name / path at which to save this log
If no filename, makes a PrintLog() instead
verbose : int
the verbosity level to use in .report / .verbort
mode : str (default 'w')
recommend either 'w' or 'a'
sec_div : str
the section divider used in `newsection'
header : None, str (default None)
the header for the file
None -> filename
show_header : bool (default True)
whether to print the header
...
Notes
-----
mode options:
'r' open for reading
'w' open for writing, truncating the file first
'x' open for exclusive creation, failing if the file already exists
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode
NOT ALLOWED '+' open a disk file for updating (reading and writing)
Inherited Methods
-----------------
.print
.newsection
.write (reimplemented for docstring)
.record (reimplemented for docstring)
.verbort (reimplemented for docstring)
Overwritten Methods
-------------------
._write: writes to file
._print_and_write: prints and writes
__init__
:
initialize a FileLog
with a filename
if no filename is provided, it switches itself to a PrintLog
if no header is provided it uses the filename + Log, if header is True, it just uses Log. If header is False, then no header is made.
all the arguments are given in the docstring. Most are for open
and should be consulted accordingly.
open
open_to_write
open_to_read
read_log
print
write
record
verbort
report
newsection
close