Python Logging for Absolute Beginners

Stop using print statements for debugging and switch to something more advanced

Python Logging for Absolute Beginners
A nice stack of logs (image by Sebastian Pociecha on Unsplash)

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

  1. debug
  2. info
  3. warning
  4. error
  5. 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.

Create a fast auto-documented, maintainable and easy-to-use Python API in 5 lines of code with…
Perfect for (unexperienced) developers who just need a complete, working, fast and secure API

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:

Logging some message (image by author)

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:

Now all of our logs are handled (image by author)

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).

Cython for absolute beginners: 30x faster code in two simple steps
Easy Python code compilation for blazingly fast applications

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:

Our logs now carry much more information (image by author)

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:

An example stack trace (image by author)

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.

A complete guide to using environment variables and files with Docker and Compose
Keep your containers secure and flexible with this easy tutorial

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:

Happy coding!

— Mike

P.S: like what I’m doing? Follow me!

Join Medium with my referral link - Mike Huls
As a Medium member, a portion of your membership fee goes to writers you read, and you get full access to every story…