This is the first post of 2022. I haven’t updated the blog site for a while. This is new year and new start, and I wish to spend more time on Python.
The Python logging module deserves more time and I am doing a little more research into this module. Here are some useful links regarding the module.
Those two youtube videos by Corey Schafer are excellent introduction to the module.
Those two videos cover the most contents in the official Python How-To article. The How-to article is written by the logging module author Vinay Sajip. The official Python documentation also has a Logging Cookbook article.
A 2018 PyCon presentation by Mario Corchero is also very nice. PyCon talk video is usually better than other youtube teaching videos. Mario Corchero also writes an article which is published on opensource.com.
Mario Corchero - Effortless Logging - Video
The Python logging module has three files (see below) and the documentations for them are listed below.
logging — Logging facility for Python
logging.config — Logging configuration
logging.handlers — Logging handlers
The Python version 3.9.7 is installed in my Ubuntu linux computer. The Python is
installed via a tool pyenv and the files are in the ~/.pyenv
directory.
Here is the bash command to find out where the logging module is located.
$ find ~/.pyenv -type d -name "logging"
/home/george/.pyenv/versions/3.9.7/lib/python3.9/logging
Here is a command to list number of lines in the logging module files.
$ find . -name '*.py' -exec wc -l '{}' + | sort -n
945 ./config.py
1544 ./handlers.py
2220 ./__init__.py
4709 total
The source code is well written and well organized. The __init__
module
defines most classes like LogRecord, Formatter, Filter, Filterer, Handler,
Manager, Logger, and RootLogger. It also defines several global variables
and functions like root (variable), basicConfig, getLogger, and info. The
root logger is an instance of RootLogger
class. The code is on line
1900 (version 0.5.1.2) as shown below.
root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)
The Manager
class instance holds a dictionary of all loggers and it is
saved as a class variable of the class Logger. The key of the dict is the
name of the logger (like ‘A.B.C’) and the value is the logger itself. The
doc string of the Manager
class is,
There is [under normal circumstances] just one Manager instance, which holds the hierarchy of loggers.
If we have those two lines of code in a module, the logger A.B
will become
parent of A.B.C
. If we don’t have a logger ‘A’, the class will create
a PlaceHolder
instance. The purpose of the hierarchy is that if we
do not attach a handler for abc
logger, it will use the handler of
its parent to handle a LogRecord instance. The root
logger sits on
the top of the hierarchy.
ab = getLogger('A.B')
abc = getLogger('A.B.C')
The getLogger
function in the __init__
module looks like this,
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.
If no name is specified, return the root logger.
"""
if not name or isinstance(name, str) and name == root.name:
return root
return Logger.manager.getLogger(name)
The getLogger(self, name)
method in the Manager
class is essentially like
the code below. If the logger is in the dict, it returns the logger.
It it is not, the method creates a Logger instance and saves it in the dict.
loggerDict = {}
if name in self.loggerDict:
rv = self.loggerDict[name]
...
else:
rv = Logger(name)
...
self.loggerDict[name] = rv
...
return rv
A logger method like warning
will call the _log
method in the Logger class.
The _log
method then calls makeRecord
and handle
method. The
makeRecord
method creates an instance of LogRecord
class, and the handle
method calls callHandlers
method, which “loops through all handlers for
this logger and its parents in the logger hierarchy”.
There are lots of details hiding in those classes and functions. The above description gives you a general idea of the logging module.
The config.py
file has configuration related classes and functions, and
the handlers.py
file has additional handlers. The __init__
module
defines two common handlers StreamHandler
and FileHandler
.