Say Goodbye to Print(): Use Logging Module for Effective Debugging



Image by Author | DALLE-3 & Canva

 

Many of us start our programming journey with YouTube videos, and for the sake of simplicity, they often use print() statements to track bugs. That’s fair enough, but as beginners adopt this habit, it can become problematic. Although these statements might work for simple scripts, as your codebase expands, this approach becomes highly inefficient. Therefore, in this article, I will introduce you to Python’s built-in logging module, which solves this problem. We will see what logging is, how it differs from the print() statements, and we will also cover a practical example to fully understand its functionality.

 

Why Use the Logging Module Instead of Print()?

 

When we talk about debugging, the Python logging module provides much more detailed information than simple print() statements. This includes timestamps, module names, log levels, and line numbers where errors occurred, etc. These extra details help us understand the behavior of our code more effectively. The information we want to log depends on the needs of the application and the developer’s preference. So, before we proceed further, let’s discuss log levels and how to set them.

 

Logging Levels

 
You can control the amount of information you want to see using these log levels. Each log level has a numerical value that denotes its severity, with higher values indicating more severe events. For example, if you set your log level to WARNING, you’re telling the logging module to only show you messages that are of WARNING level or higher. This means you won’t see any DEBUG, INFO, or other less severe messages. This way, you can focus on the important events and ignore the noise

Here’s a table that shows the details of what each log level represents:

Log Level Numerical Value Purpose
DEBUG 10 Provides detailed information for diagnosing code-related issues, such as printing variable values and function call traces.
INFO 20 Used to confirm that the program is working as expected, like displaying startup messages and progress indicators.
WARNING 30 Indicates a potential problem that may not be critical to interrupt the program’s execution but could cause issues later on.
ERROR 40 Represents an unexpected behavior of the code that impacts its functionality, such as exceptions, syntax errors, or out-of-memory errors.
CRITICAL 50 Denotes a severe error that can lead to the termination of the program, like system crashes or fatal errors.

 

Setting Up the Logging Module

 

To use the logging module, you need to follow some steps for configuration. This includes creating a logger, setting the logging level, creating a formatter, and defining one or more handlers. A handler basically decides where to send your log messages, such as to the console or a file. Let’s start with a simple example. We’re going to set up the logging module to do two things: first, it’ll show messages on the console, giving us useful information (at the INFO level). Second, it’ll save more detailed messages to a file (at the DEBUG level). I’d love it if you could follow along!

 

1. Setting the log level

The default level of the logger is set to WARNING. In our case, our two handlers are set to DEBUG and INFO levels. Hence, to ensure all messages are managed properly, we have to set the logger’s level to the lowest level among all handlers, which, in this case, is DEBUG.

import logging

# Create a logger
logger = logging.getLogger(__name__)

# Set logger level to DEBUG
logger.setLevel(logging.DEBUG)

 

 

2. Creating a Formatter

You can personalize your log messages using formatters. These formatters decide how your log messages will look. Here, we will set up the formatter to include the timestamp, the log level, and the message content using the command below:

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

 

 

3. Creating Handlers

As discussed previously, handlers manage where your log messages will be sent. We will create two handlers: a console handler to log messages to the console and a file handler to write log messages to a file named ‘app.log’.

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)

 

Both handlers are then added to the logger using the addHandler() method.

logger.addHandler(console_handler)
logger.addHandler(file_handler)

 

4. Testing the Logging Setup

Now that our setup is complete, let’s test if it’s working correctly before moving to the real-life example. We can log some messages as follows:

logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

 

When you run this code, you should see the log messages printed to the console and written to a file named ‘app.log’, like this:

Console

2024-05-18 11:51:44,187 - INFO - This is an info message
2024-05-18 11:51:44,187 - WARNING - This is a warning message
2024-05-18 11:51:44,187 - ERROR - This is an error message
2024-05-18 11:51:44,187 - CRITICAL - This is a critical message

 
app.log

2024-05-18 11:51:44,187 - DEBUG - This is a debug message
2024-05-18 11:51:44,187 - INFO - This is an info message
2024-05-18 11:51:44,187 - WARNING - This is a warning message
2024-05-18 11:51:44,187 - ERROR - This is an error message
2024-05-18 11:51:44,187 - CRITICAL - This is a critical message

 

Logging User Activity in a Web Application

 

In this simple example, we will create a basic web application that logs user activity using Python’s logging module. This application will have two endpoints: one for logging successful login attempts and the other to document failed ones (INFO for success and WARNING for failures).

 

1. Setting Up Your Environment

Before starting, set up your virtual environment and install Flask:

python -m venv myenv

# For Mac
source myenv/bin/activate

#Install flask
pip install flask

 

2. Creating a Simple Flask Application

When you send a POST request to the /login endpoint with a username and password parameter, the server will check if the credentials are valid. If they are, the logger records the event using logger.info() to signify a successful login attempt. However, if the credentials are invalid, the logger records the event as a failed login attempt using logger.error().

#Making Imports
from flask import Flask, request
import logging
import os

# Initialize the Flask app
app = Flask(__name__)

# Configure logging
if not os.path.exists('logs'):
    os.makedirs('logs')
log_file="logs/app.log"
logging.basicConfig(filename=log_file, level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
log = logging.getLogger(__name__)


# Define route and handler
@app.route('/login', methods=['POST'])
def login():
    log.info('Received login request')
    username = request.form['username']
    password = request.form['password']
    if username == 'admin' and password == 'password':
        log.info('Login successful')
        return 'Welcome, admin!'
    else:
        log.error('Invalid credentials')
        return 'Invalid username or password', 401

if __name__ == '__main__':
    app.run(debug=True)

 

3. Testing the Application

To test the application, run the Python script and access the /login endpoint using a web browser or a tool like curl. For example:

Test Case 01

 curl -X POST -d "username=admin&password=password" http://localhost:5000/login

 
Output

 
Test Case 02

curl -X POST -d "username=admin&password=wrongpassword" http://localhost:5000/login

 
Output

Invalid username or password

 
app.log

2024-05-18 12:36:56,845 - INFO - Received login request
2024-05-18 12:36:56,846 - INFO - Login successful
2024-05-18 12:36:56,847 - INFO - 127.0.0.1 - - [18/May/2024 12:36:56] "POST /login HTTP/1.1" 200 -
2024-05-18 12:37:00,960 - INFO - Received login request
2024-05-18 12:37:00,960 - ERROR - Invalid credentials
2024-05-18 12:37:00,960 - INFO - 127.0.0.1 - - [18/May/2024 12:37:00] "POST /login HTTP/1.1" 200 -

 

Wrapping Up

 

And that wraps up this article. I strongly suggest making logging a part of your coding routine. It’s a great way to keep your code clean and make debugging easier. If you want to dive deeper, you can explore the Python logging documentation for more features and advanced techniques. And if you’re eager to enhance your Python skills further, feel free to check out some of my other articles:

 
 

Kanwal Mehreen Kanwal is a machine learning engineer and a technical writer with a profound passion for data science and the intersection of AI with medicine. She co-authored the ebook “Maximizing Productivity with ChatGPT”. As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She’s also recognized as a Teradata Diversity in Tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change, having founded FEMCodes to empower women in STEM fields.

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here