A short guide to getting starting with Python’s logging
module.
We’ve all done this:
This is ridiculous!
Use Python’s logging module to clean up your logs.
All the code for the package can be found here.
None — logging
is built in. Note that we will be using python3
.
Let’s dive in with a simple logging example
The output will be:
This is for warnings
This is for errors
Again, this is for debugging
Again, this is for info
Again, this is for warnings
Again, this is for errors
Notice that there is a StreamHandler
called handlerPrint
, and a logger
called log
.
getLogger(name)
command — if the logger with the given name does not exist, it will be created.When do messages appear? Only if both the handler
and the logger
allow it. The possible logging levels are:
DEBUG
INFO
WARNING
ERROR
The default level is WARNING
.
Here we first turned the handler
logging level to DEBUG
, but the logger
level is still the default WARNING
.
Therefore, only the warning messages appear from the first part.
This is for warnings
This is for errors
Next we turn on the logger
to be DEBUG
as well — now all messages will appear:
Again, this is for debugging
Again, this is for info
Again, this is for warnings
Again, this is for errors
Configuration options =========================
Next we should dive into configuration options:
The output will be:
2020–07–03 11:22:05,323 | DEBUG | configuration.py | This is for debugging
2020–07–03 11:22:05,323 | INFO | configuration.py | This is for info
2020–07–03 11:22:05,323 | WARNING | configuration.py | This is for warnings
2020–07–03 11:22:05,323 | ERROR | configuration.py | This is for errors
Fancy!
A complete list of formatting options is here.
A common use scenario is writing logs to a file. Simply add a new handler:
Notice the steps:
TimedRotatingFileHandler
called handlerWrite
. It writes to a log
directory. Every hour, a new file will be created (when=”h”
and interval=1
). The new files will have an index number appended to them. A maximum of 24
files will exist — backupCount=23
plus the normal logs.log
file. After this, logs will be overwritten.Here are the official docs on configuring the logging module for your library.
But I’ll give you my unofficial take: the goals are two-fold:
This is exactly what the division between handlers
and loggers
will let us do.
The scheme is:
logger
for your module, or if there are enough classes, one for each class. Put the names into the documentation.TimedRotatingFileHandler
(or whetever handler you like).loggers
and the handlers
. The default level for both is WARNING
.The DEBUG
level should only be relevant for you or other developers, but INFO
may be interesting for everyone.
I therefore recommend to turn your loggers
levels to INFO
by default.
Let us create a module called mypackage
with the following files:
mypackage/\_\_init\_\_.py
mypackage/foo.py
mypackage/bar.py
mypackage/logging\_options.py
test\_mypackage.py
Let’s look at the logging options first:
Here, we set up formatting and handlers
for the entire module. In the foo.py
and bar.py
files, we use them as follows:
and almost identicalbar.py
:
In the the test method test_mypackage.py
, we can see how to selectively control output:
The output in the print stream is:
2020–07–03 12:42:34,401 | WARNING | foo.py | — — Starting do\_something — -
2020–07–03 12:42:34,402 | INFO | foo.py | Counting for 10 seconds
2020–07–03 12:42:34,402 | INFO | foo.py | count: 0
2020–07–03 12:42:34,402 | INFO | foo.py | count: 1
2020–07–03 12:42:34,402 | INFO | foo.py | count: 2
2020–07–03 12:42:34,402 | INFO | foo.py | count: 3
2020–07–03 12:42:34,402 | ERROR | foo.py | quitting early!
2020–07–03 12:42:34,402 | WARNING | foo.py | — — Finished do\_something — -
2020–07–03 12:42:34,403 | WARNING | foo.py | — — Starting do\_something — -
2020–07–03 12:42:34,403 | INFO | foo.py | Counting for 10 seconds
2020–07–03 12:42:34,403 | INFO | foo.py | count: 0
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 0
2020–07–03 12:42:34,403 | INFO | foo.py | count: 1
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 1
2020–07–03 12:42:34,403 | INFO | foo.py | count: 2
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 3
2020–07–03 12:42:34,404 | INFO | foo.py | count: 3
2020–07–03 12:42:34,404 | DEBUG | foo.py | cumulative count: 6
2020–07–03 12:42:34,404 | ERROR | foo.py | quitting early!
2020–07–03 12:42:34,404 | WARNING | foo.py | — — Finished do\_something — -
2020–07–03 12:42:34,404 | WARNING | bar.py | — — Starting do\_something — -
2020–07–03 12:42:34,405 | ERROR | bar.py | quitting early!
2020–07–03 12:42:34,405 | WARNING | bar.py | — — Finished do\_something — -
and in the log file:
2020–07–03 12:42:34,401 | WARNING | foo.py | — — Starting do\_something — -
2020–07–03 12:42:34,402 | INFO | foo.py | Counting for 10 seconds
2020–07–03 12:42:34,402 | INFO | foo.py | count: 0
2020–07–03 12:42:34,402 | INFO | foo.py | count: 1
2020–07–03 12:42:34,402 | INFO | foo.py | count: 2
2020–07–03 12:42:34,402 | INFO | foo.py | count: 3
2020–07–03 12:42:34,402 | ERROR | foo.py | quitting early!
2020–07–03 12:42:34,402 | WARNING | foo.py | — — Finished do\_something — -
2020–07–03 12:42:34,403 | WARNING | foo.py | — — Starting do\_something — -
2020–07–03 12:42:34,403 | INFO | foo.py | Counting for 10 seconds
2020–07–03 12:42:34,403 | INFO | foo.py | count: 0
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 0
2020–07–03 12:42:34,403 | INFO | foo.py | count: 1
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 1
2020–07–03 12:42:34,403 | INFO | foo.py | count: 2
2020–07–03 12:42:34,403 | DEBUG | foo.py | cumulative count: 3
2020–07–03 12:42:34,404 | INFO | foo.py | count: 3
2020–07–03 12:42:34,404 | DEBUG | foo.py | cumulative count: 6
2020–07–03 12:42:34,404 | ERROR | foo.py | quitting early!
2020–07–03 12:42:34,404 | WARNING | foo.py | — — Finished do\_something — -
2020–07–03 12:42:34,404 | WARNING | bar.py | — — Starting do\_something — -
2020–07–03 12:42:34,404 | INFO | bar.py | Counting for 5 seconds
2020–07–03 12:42:34,404 | INFO | bar.py | count: 0
2020–07–03 12:42:34,404 | INFO | bar.py | count: 1
2020–07–03 12:42:34,404 | INFO | bar.py | count: 2
2020–07–03 12:42:34,405 | ERROR | bar.py | quitting early!
2020–07–03 12:42:34,405 | WARNING | bar.py | — — Finished do\_something — -
We can therefore control output selectively for:
That does it for me, I’m tired of logging and blogging. All the code for the package can be found here.
In a future part I will discuss logging in C++
using spdlog
.
Oliver K. Ernst
July 3, 2020