Author's image | DALLE-3 and Canva
Many of us start our programming journey with YouTube videos and for the sake of simplicity often use print()
statements to track errors. That's fair enough, but as beginners adopt this habit, it can become problematic. Although these statements may work for simple scripts, as your code base 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 print()
statements, and we will also cover a practical example to fully understand its functionality.
Why use the registration module instead of Print()?
When we talk about debugging, Python's logging module provides much more detailed information than just the print()
statements. This includes timestamps, module names, log levels and line numbers where errors occurred, etc. These additional details help us understand the behavior of our code more effectively. The information we want to record depends on the needs of the application and the preferences of the developer. So before we continue, let's discuss logging levels and how to configure them.
Log 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; higher values indicate more severe events. For example, if you set your logging level to WARNING
you are telling the logging module to only show you messages that are from WARNING
level or higher. This means that you will not see any DEBUG
, INFO
or other less severe messages. This way, you can focus on important events and ignore the noise.
Below is a table showing the details of what each logging level represents:
Log level | Numerical value | Aim |
---|---|---|
DEBUG | 10 | Provides detailed information to diagnose code-related problems, such as printing variable values and function call traces. |
INFORMATION | twenty | Used to confirm that the program is working as expected, such as displaying startup messages and progress indicators. |
WARNING | 30 | Indicates a potential problem that may not be critical to interrupting program execution, but could cause problems later. |
MISTAKE | 40 | Represents unexpected behavior of the code that affects its functionality, such as exceptions, syntax errors, or out of memory errors. |
CRITICAL | fifty | Denotes a serious error that can cause program termination, such as system crashes or fatal errors. |
Logging module configuration
To use the registration module, you must follow some configuration steps. This includes creating a logger, configuring the logging level, creating a formatter, and defining one or more drivers. Basically, a driver decides where to send its log messages, such as to the console or to a file. Let's start with a simple example. Let's configure the logging module to do two things: First, it will display messages in the console, giving us useful information (at the end). INFO
level). Second, it will save more detailed messages to a file (in the DEBUG
level). I would love it if you could follow me!
1. Log level configuration
The default logger level is set to WARNING
. In our case, our two controllers are configured to DEBUG
and INFO
levels. Therefore, to ensure that all messages are handled correctly, we need to set the logger 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 customize your log messages using formatters. These formatters decide what your log messages will look like. Here, we will configure the formatter to include the timestamp, log level, and message content using the following command:
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
3. Creating drivers
As mentioned above, 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 called '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 drivers are then added to the logger using the addHandler()
method.
logger.addHandler(console_handler)
logger.addHandler(file_handler)
4. Test registry settings
Now that our setup is complete, let's test if it works correctly before moving on 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 log messages printed to the console and written to a file called '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
application.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 to record successful login attempts and the other to document failed ones (INFO
for success and WARNING
due to failures).
1. Setting up your environment
Before you begin, set up your virtual environment and install Flask:
python -m venv myenv
# For Mac
source myenv/bin/activate
#Install flask
pip install flask
2. Create a simple Flask application
When you send a POST request to /access end with a username and password parameter, the server will check if the credentials are valid. If so, the logger logs the event using logger.info() to indicate a successful login attempt. However, if the credentials are invalid, the logger logs 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. Test the app
To test the application, run the Python script and access the /access 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
Production
Test case 02
curl -x POST -d "username=admin&password=wrongpassword" http://localhost:5000/login
Production
Invalid username or password
application.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 -
Ending
And that concludes this article. I highly recommend making logging part of your coding routine. It's a great way to keep your code clean and make debugging easier. If you want to go deeper, you can explore the Python logging documentation for more advanced features and techniques. And if you're eager to improve your Python skills even further, feel free to check out some of my other articles:
Kanwal Mehreen Kanwal is a machine learning engineer and technical writer with a deep passion for data science and the intersection of ai with medicine. She is the co-author of the eBook “Maximize Productivity with ChatGPT.” As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She is also recognized as a Teradata Diversity in tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is a passionate advocate for change and founded FEMCodes to empower women in STEM fields.