Photo Credit: spockthedog

Copy-Pastable Logging Scheme for Python

The post you were looking for — if you already know some python logging, but lost your favorite logging scheme somewhere in the universe of code.

--

You are here because:

  1. You want to log your Python module.
  2. You are already somewhat acquainted with Python logging (if not, I recommend you browse the first half of this great intro).
  3. You are looking for a quick copy-pastable snippet to jumpstart your logging.

And why am I here?
Because for the gazillionth time I discovered I left an important code snippet somewhere unknown… Therefore, today I have decided that this will be the very last time I will be reinventing the logging wheel.

Well then, let’s get right to it!

Three Copy-Pastable Steps and Your Logger Is Ready

  1. Open a new file for logging utils, where we will initialize our logger, and get our logger from when we need it.
  2. Initialize your logger in the main of your module.
  3. Call your logger from any other file where you need it.

So, without further ado:

1. Open a New File for Logging Utils

Let’s open a new python file, logging_utils.py, with the following code:

Let’s understand what is happening here:

We would like to have two outputs for our logger — one that will be printed to console where we run the code (the StreamHandler), and one that will be saved to file (the FileHandler).

The command logger.propagate = False makes sure that the logs sent to file will not be printed to console.

We use the Foramtter class to define the format of the logs. Many options can go here, but what I’ve chosen is to display is:

  1. The time of the log in a human-readable format, asctime, and I’ve added the pattern at the end to indicate the specific datetime format.
  2. levelname is the level of the log, one out of INFO, DEBUG, WARNING, ERROR, CRITICAL.
  3. The name of the file, filename, from which the log was generated, and the line number, lineno.
  4. Lastly, of course, the message itself — message.

Notice that each handler can have a different log level. I prefer to have only INFO logs and above (i.e., also WARNING, ERROR and CRITICAL) displayed in the console, while the file will also include DEBUG logs.

Remember: there is such a thing as too many logs.

2. Initialize Your Logger

What is the root access to your project? Is it a script with a main? Is your logger contained in a specific module with a parent class that handles the entire pipeline?

Whatever the answer may be, let’s initialize the logger there, with the simple command:

Yup, that’s it.
And of course, if you wish, you can customize the log levels here, and remove the save_dir if you only wish to print to console.

3. Call Your Logger

Now that our logger is initialized, we can call our logger to log all our beautiful logs.

A Note About the Order of Actions

If you call your logger without initializing it, the default log_level is WARNING, and the default formatting is empty (just your message). So if you call the function we defined get_logger() before/without ever initializing the logger, then you will get:

But, since we use everywhere the same name for our logger, LOGGER_NAME, our logger is a singleton, meaning the instance we already have at hand is the same one that will get all the features we defined in our init_logger(). Therefore, if we initialize the logger after we already called it, then on the next call, it will be set as desired:

A Final Note About Logging

You can get all the architecture right, but at the end of the run, your log message itself is what counts. So make it clear and concise!

Joking! This is so not what this post is about… I just saw this funny meme and had to find a context for it. #sorrynotsorry

Epilogue

This post is a part of my growing collection (mostly drafts for now…) of code snippets that are context-agnostic. The ones I want to take with me every time I start a new job or a new personal project, completely IP-conscience-free.
I hope you benefit from this too!

And to those reading during the Covid-19 outbreak: if nothing else, I hope you enjoyed the NON-era-related memes 😉

Uzi, my sunshine during these hard times, after a long day of woofing_from_home

--

--