Stop Using “Print” and Start Using “Logging” | by Naser Tamimi | Towards Data Science

Logging is a popular solution for tracking events in a code or debugging. Many of us (Python programmers and data scientists) have this bad habit of using print() to debug and track events in our codes.
Why using print() for logging and debugging is not a good practice?
The print() statement fails if your code does not have access to the console.To define basic logging needs, several lines of code are needed.Including additional logging information is not easy.The print() statement only displays messages on the console. Recording logging data inside a file or sending it over the internet needs additional works.
The print() statement fails if your code does not have access to the console.
To define basic logging needs, several lines of code are needed.
Including additional logging information is not easy.
The print() statement only displays messages on the console. Recording logging data inside a file or sending it over the internet needs additional works.
A better way to track your code events and do debugging is using the “Logging” library (a Python standard library).
The logging library provides you five tools to accomplish the logging tasks.
Debug (logging.debug()): Providing information to diagnosing problems.Info (logging.info()): Tracking the normal operation of a program.Warning (logging.warning()): Although the code is still working as expected, something unexpected happened.Error (logging.error()): The code was unable to run some parts.Critical (logging.critical()): The code cannot run.
Debug (logging.debug()): Providing information to diagnosing problems.
Info (logging.info()): Tracking the normal operation of a program.
Warning (logging.warning()): Although the code is still working as expected, something unexpected happened.
Error (logging.error()): The code was unable to run some parts.
Critical (logging.critical()): The code cannot run.
Before using these tools, let’s understand different levels of logging in Python.
There are six logging levels in Python. The highest level is CRITICAL. If you set your logging level to CRITICAL, only logging messages of the CRITICAL level will be shown. To set your logging level to CRITICAL, you can use logging.basicConfig().
logging.basicConfig(level=logging.CRITICAL)
or
logging.basicConfig(level=50)
As you see the level argument in logging.basicConfig() takes an integer number (i.e. 50 or constant logging.CRITICAL) and sets the logging level. The levels of logging from the lowest to the highest are NOTSET=0, DEBUG=10, INFO=20, WARNING=30, ERROR=40, and CRITICAL=50.
The default level is WARNING (i.e. 30) which means, the logging module only displays/logs events with warning, error, or critical severity. Again, remember that you can set your logging level using logging.basicConfig().
Here is an example code to display different levels of logging messages. Note, I set the logging level to the lowest (logging.NOTSET) to displays all messages.
The output is something like this:
DEBUG:root:Here you have some information for debugging.INFO:root:Everything is normal. Relax!WARNING:root:Something unexpected but not important happend.ERROR:root:Something unexpected and important happened.CRITICAL:root:OMG!!! A critical error happend and the code cannot run!
Let’s change the logging level to WARNING.
In this case, the output is:
WARNING:root:Something unexpected but not important happend.ERROR:root:Something unexpected and important happened.CRITICAL:root:OMG!!! A critical error happend and the code cannot run!
As you see, because I set my logging level to WARNING (using logging.WARNING constant value), only messages with a severity of WARNING or higher are shown.
OK, it is good to use logging tools to display appropriate messages, but can I format my messages better that help me do betting logging of my code? The answer is YES. Let’s read the next section to format our logging messages better (and in a more meaningful way).
One of the advantages of using the logging module to track our codes is the ability to format the messages based on our needs. For example, in my code, I would like to log the date and time with appropriate messages. Here is an example.
And here is the output.
2021-02-14 23:02:34,089 | DEBUG: Here you have some information for debugging.2021-02-14 23:02:34,089 | INFO: Everything is normal. Relax!2021-02-14 23:02:34,090 | WARNING: Something unexpected but not important happend.2021-02-14 23:02:34,092 | ERROR: Something unexpected and important happened.2021-02-14 23:02:34,092 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
As you see, to format my logging messages, I need to pass a string to logging.basicConfig(). The formatting string can include a mix of characters and attributes. For example, in my formatting string, the attributes are asctime (%(asctime)s), levelname (%(levelname)s), and message (%(message)s). The first attribute, asctime, shows the logging date and time. The levelname attribute outputs the logging level (DEBUG, INFO, etc.). Finally, we have the message attribute, which is simply the message we want to show. There are twenty useful attributes that you can use based on your needs. Here is a list of them (source).
So far, we have sent the logging messages to the console for printing. Sometimes, we need to record the logging messages and information in a file for our record. It is super easy to switch from printing to console to writing on a file. We only need to pass a file name to logging.basicConfig()to start logging into a file. Here is an example.
For basic logging, what is mentioned so far is more than enough. Although if you like to do more complicated logging tasks, you might need to do it more professionally.
In a more advanced approach, we use a logger object to track events in the code. You need to set up your logger object in 4 simple steps.
STEP 1) Instantiate a logger object
STEP 2) Setting the lowest severity for logging
STEP 3) Set a destination (also called a handler) for your logs (e.g., console, file, or HTTP.)
STEP 4) Setting format of messages for the handler
The following code show all 4 steps.
The output of this code, as you correctly guessed, is:
2021-02-15 15:04:03,364 | ERROR: Something unexpected and important happened.2021-02-15 15:04:03,364 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
As the final example, let’s go a little more advanced and imagine you need two loggers. One logger prints only important messages (ERROR and higher) to the console. The second logger records more logging message (INFO and higher) in a log file with more details (for example, it includes execution line number or %(lineno)d). Here is the code to handle this scenario.
If you run this code, you will see the following output in the console.
2021-02-15 15:04:25,349 | ERROR: Something unexpected and important happened.2021-02-15 15:04:25,350 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
But in your log file (sample.log), you will see more detailed information.
2021-02-15 15:04:25,340 | INFO | 22: Everything is normal. Relax!2021-02-15 15:04:25,349 | WARNING | 23: Something unexpected but not important happend.2021-02-15 15:04:25,349 | ERROR | 24: Something unexpected and important happened.2021-02-15 15:04:25,350 | CRITICAL | 25: OMG!!! A critical error happend and the code cannot run!
As you see in the code, I simply created a logger object. Then, I started creating my console handler and setting its level and format. I added my console handler to the logger object. I repeated a similar process for the file handler. Finally, I started logging, and the logger managed both handlers properly. Very easy and straightforward. Now, you can imagine how hard it would be to replicate the same functionality with the print() statement.
There are many advanced tools and settings that you can use in your logger. As an example, you can send your logging results to a central URL address over HTTP by using the HTTP logging handler (logging.HTTPHandler()). Or you can use filters to ask the logger to react differently to different events.
Python logging library provides many tools and settings for handling different logging tasks. You can learn more about all available tools and settings in the Logging Cookbook.
Using print() for logging and debugging is a bad practice. In Python, we have specialized tools inside the “Logging” library to handle different logging and debugging tasks. In this article, I showed you how to use the basic logging tools and settings.