Python Logging for Absolute Beginners
Stop using print statements for debugging and switch to something more advanced
Logging is essential to understanding what happens in your application, why it happens and when it happens. Not only can you catch errors more easily, debugging becomes much faster as well since you have a nice cookie crumb trail that leads you right to the problem.
No only does logging make developing and debugging more easy, there are many other advantages which we’ll get into in this article. Let’s code!
But first
Let’s first understand logging by comparing it to the most-used way of debugging Python code: just using print()
statements. In short: logging offers all that just printing offers and much more. Let’s go through the main advantages of why logging is better than printing.
Setting the importance — execute in debug mode
There are 5 main types of logging that all have a different level of importance
- debug
- info
- warning
- error
- critical
So in stead of print(somevalue)
you can logger.debug(somevalue)
or logger.error(somevalue)
. You can set a logger level application-wide. This means that if you set your logger to the warning
level, it will only handle warning
logs and all above, ignoring lower importance levels like logger.debug()
and logger.info()
statements.
The biggest advantage of this is that you can start up your application in debug-mode, where the logger is set to the lowest (debug
) level. Then you’ll see everything that happens in your app, making development and debugging much easier. Once your code is ready for production you can just set your logger to the error
-level, for example, to only catch the severe logs.
Additional information — metadata
Logging provides more information. Imagine you log an error. In addition to just printing the error message you can add all kinds of metadata that lead you straight to the error:
- datetime
- filename and path of the file where the log was issued
- name of the function where the log was issued
- line number
- a user-defined message
Imagine you receive a log in your huge application that points you right to the bug. You can literally log errors like the example below:
With all this extra information you can debug and fix your code super fast.
Streaming and saving your logs — log to a file or api
In addition to just printing out the message, you can add handlers to you logger that add each of your logs to a file on disk or even send it to an API!
These kinds of functionalities are very useful. Has your app crashed? Just read the log file and see what happens right before the crash.
Using a HTTP handler for sending your logs to an API take this further; getting your logs to where they are needed instantly. You can create an API that monitors all incoming logs (from all kinds of applications) and notifies you when needed. This is also a great way to collect user statistics on your app.
Practical examples — the code part
Enough explanations, show me some code! In this part we’ll focus on the logging basics:
- Setting up a logger
- Setting the logging level
- Determining which information (fields) we want to log, writing logs to a file
In this article we’ll focus on adding colors to the log so that we can analyze them more easily and in this article we’ll use handlers to write logs to a file and to an API.
Setting up a logger
This part is not very hard; we’ll import the package and get a logger using the getLogger
method.
We can also use logging.warning(msg="a message")
, this will use the root logger. It’s best practice use the getLogger method so that our loggers don’t get mixed up.
Now we can log out like so:
The output in my console looks like this:
You’ll notice that we are correctly missing the debug and info message. This is because by default the logger is set to the warning
level. Let’s fix this.
Setting the logging level
Setting the logging defaults is easy:
As you’ll see it will now print out all of our logs. Additionally, we receive some more information: we see the level-name of our log and the name of our logger. These are called fields. In the next part we’ll customize our fields and how our log message looks.
After setting the logging.basicConfig’s level you can set the level of your logger on the fly with logger.setLevel(level=logging.ERROR)
.
Setting up fields
In the previous part you’ve seen that by default two fields are logged: the level-name and logger-name. Let’s customize:
Let’s first check out our output:
We correctly don’t see the debug-message because we’ve set the logger level to INFO. Additionally you see that we have specified our logging fields and the datetime-format in the logging.basicConfig
.
Fields are specified like so%(THEFIELDNAME)s
. There are many available fields:
Check out more fields here.
Also notice that we can style our field a little bit. With the -8
in %(levelname)-8s
we specify that it should be 8 characters long, adding spaces if it’s shorter than that.
Printing out stack traces
When something goes wrong in your code you’ll see a stack trace like below:
The logging module also allows us to catch this exception and log it! This is done very easily:
Adding exc_info=True
will put the stack trace in our logging message and offers us even more information on what causes our bug. In the article below we’ll see that we can write this stack trace to a file or even send it to an a API to store it in a database or do whatever with it.
Conclusion
In this article we’ve covered the basics of logging in Python and I hope to have shown you all the advantages of logging over printing. The next step is to add file handlers so that we can save our logs to a file, send it over email or to an API. Check out this article for that.
If you have any tips on improving this article; please comment! In the meantime, check out my other articles on all kinds of programming-related topics like these:
- Cython for absolute beginners — 30x faster code in two simple steps
- Why Python is so slow and how to speed it up
- Git for absolute beginners: understanding Git with the help of a video game
- Simple trick to work with relative paths in Python
- Docker for absolute beginners: the difference between an image and a container
- Docker for absolute beginners — what is Docker and how to use it (+ examples)
- Virtual environments for absolute beginners — what is it and how to create one (+ examples)
- Create and publish your own Python package
- Create a fast auto-documented, maintainable, and easy-to-use Python API in 5 lines of code with FastAPI
Happy coding!
— Mike
P.S: like what I’m doing? Follow me!