Logging in Python is a crucial aspect of software development, particularly for debugging, monitoring, and understanding the flow of your program. Python provides a built-in logging module that makes it easy to incorporate logging into your applications.
Logging is essential in software development for several reasons:
Python’s logging module supports different levels of logging, each serving a specific purpose:
Let’s start with a simple logger setup:
"""
logging module
"""
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
FMT = "%(levelname)s %(asctime)s [%(filename)s : %(funcName)s : %(lineno)d] %(message)s"
formatter = logging.Formatter(FMT)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
Here, the stream handler sends logs to the console, and we define a custom format for log messages.
We can now use this logger in another module:
"""
division function module
"""
from logger import logger
try:
num1 = int(input("Enter first number: "))
num2 = int(input("Enter second number: "))
result = num1 / num2
print(result)
except ZeroDivisionError as e:
logger.error(e)
When a division by zero occurs, the output looks like this:
$ python3 division.py
Enter first number: 2
Enter second number: 0
ERROR 2024-04-16 10:13:48,341 [division.py : <module> : 13] division by zero
We can extend our logger to write logs to both the console and a file:
"""
logger module with file and shell handler
"""
import logging
logger = logging.getLogger(__name__)
shell_handler = logging.StreamHandler()
file_handler = logging.FileHandler("debug.log")
logger.setLevel(logging.DEBUG)
shell_handler.setLevel(logging.WARNING)
file_handler.setLevel(logging.DEBUG)
FMT_SHELL = "%(levelname)s %(asctime)s %(message)s"
FMT_FILE = (
"%(levelname)s %(asctime)s [%(filename)s:%(funcName)s:%(lineno)d] %(message)s"
)
shell_formatter = logging.Formatter(FMT_SHELL)
file_formatter = logging.Formatter(FMT_FILE)
shell_handler.setFormatter(shell_formatter)
file_handler.setFormatter(file_formatter)
logger.addHandler(shell_handler)
logger.addHandler(file_handler)logger = logging.getLogger(__name__)
shell_handler = logging.StreamHandler()
file_handler = logging.FileHandler("debug.log")
In this setup, the shell only shows warnings and above, while the file captures detailed debug information.
Using the rich library, we can make our logs more readable:
"""
rich logger module
"""
import logging
from rich.logging import RichHandler
logger = logging.getLogger(__name__)
# the handler determines where the logs go: stdout/file
shell_handler = RichHandler()
file_handler = logging.FileHandler("debug.log")
logger.setLevel(logging.DEBUG)
shell_handler.setLevel(logging.DEBUG)
file_handler.setLevel(logging.DEBUG)
# the formatter determines what our logs will look like
FMT_SHELL = "%(message)s"
FMT_FILE = (
"%(levelname)s %(asctime)s [%(filename)s:%(funcName)s:%(lineno)d] %(message)s"
)
shell_formatter = logging.Formatter(FMT_SHELL)
file_formatter = logging.Formatter(FMT_FILE)
shell_handler.setFormatter(shell_formatter)
file_handler.setFormatter(file_formatter)
logger.addHandler(shell_handler)
The rich library enhances console output with better formatting and color.
Python’s logging module is a powerful tool that helps developers track, debug, and monitor applications efficiently. Whether you’re using basic logging or integrating advanced features like the rich library, well-structured logs can save time and effort when diagnosing issues and analyzing performance.
Happy logging!