Python logging - better than just print
Logging
module is one of most useful modules out of the box. In every single software development, there is a chance the program runs into an error. And that's a reason we should prevent the case with this.
All programmers are familiar with print
function. We use it to debug some values. However, it would be greatly better to understand how to effectively utilize Logging
module because we may need to debug from somewhere more persistent than console log.
For more info please follow the link below to read the module's detail, but we're gonna see some basic use cases of this module through this blog.
Simple use
Just a baby step. We can start from this.
There are 5 levels of log there + 1 "NOTSET" which I never use it as there is no use cases for me. Learn more about levels by this link.
The result will be messages from Logging
.
And what if we want more info of the message such as time or formatting level? We can add argument format
into the object like this.
And it will produce the message based on the given format.
Handlers
Wanna try more advance? Let's make a logger that perform logging on both console and file writing.
For this requirement, we are going to review two main handlers for injecting log records to these displays.
console
Other than using basicConfig()
as above, we will use StreamHandler()
. The example is below:
And let's see what happen when we run it.
file
We can parse the log outputs to a file via FileHandler
.
See the output file here.
Combine together
Of course, we can mix both handlers together to inject logs through console and file at the same time. Try this:
Now run it and there are console log along with file log. Yes!
More fancy examples
dynamic level per message
We can supply the message level by calling .log()
instead of .info()
or other level-specific methods.
Date time format
We would add argument datefmt
to logging.Formatter()
. Visit this link to learn more about format codes.
Multiple loggers
How to manage multiple loggers?
We can create many loggers as we want but we need to make sure we named each logger distinguishly. Let's see the case.
- Firstly, program calls
outer
. outer
create a logger name "outer" to write logs in file "outer.log". The messages are "[outer] start" and "[outer] done".- Between both messages from
outer
,outer
also callinner
through a loop. - There are 2 items and it will call
inner
2 times. - First item is a text "abc" that
inner
will log in the filepath "msg1/inner.log" with "[inner]" beginning the line. - Second is a text "def" that
inner
will log in the filepath "msg2/inner.log" with "[inner]" beginning the line as well. inner
has a logger name "inner".
Looking good but the result was not as expected. The log file "msg1/inner.log" should have only "[inner] abc" but it inproperly has "[inner] def".
What we missed? We had given the same log name for inner
so that the first inner
call receive the consequent logs.
We have to fix by giving different name. One solution is to use UUID
to generate a random UUID (line 27) for each inner
call.
And this time we got the correct logs.
All examples are in my repo below.