Python Curriculum
Logging Module Overview
When writing automation scripts and daemon services, you cannot sit in front of the console and look at outputs. If a script fails at midnight, you need a record of what happened before the crash.
Historically, developers use print() statements to output debug messages. While print statements are simple, they are a major anti-pattern in production DevOps code. Print statements lack severity thresholds, cannot be easily redirected to files or log servers, and do not capture critical context like timestamps, execution threads, or line numbers.
Python includes a powerful built-in logging module. It provides a thread-safe, flexible, and hierarchical system for tracking runtime events.
In this guide, we will compare logging with prints, detail the core architecture of the logging framework, examine the hierarchical propagation tree, and configure basic global logger states.
Logging vs. Print Statements
Here is a comparison of print statements and logging to help you understand why logging is required for production scripts:
Core Components of the Logging Framework
The logging module is organized around four core classes:
- Loggers: The main entry point. Your application code calls methods on a logger (e.g.,
logger.info("Service started")) to generate log events. - Handlers: Determine where the log message goes. A single logger can have multiple handlers (e.g. sending logs to the console AND saving them to a log file).
- Formatters: Define the layout and structure of the final string output (e.g. prepending timestamps or formatting as JSON).
- Filters: Provide fine-grained control to determine which log records should be processed or modified.
The Logger Hierarchy and Propagation Tree
Loggers in Python are organized hierarchically using dot notation (e.g., app.database, app.api.auth). The logger named app is the parent of app.database. At the top of the tree sits the Root Logger.
When a log message is generated on a child logger:
- The child logger evaluates if the message meets its configured level threshold.
- If it passes, the message is sent to the child logger's handlers.
- The message then propagates up the tree to the parent logger's handlers, and continues up to the root logger's handlers.
You can disable this propagation behavior by setting logger.propagate = False.
1. Quick Global Setup with basicConfig
The fastest way to configure your logging system is using logging.basicConfig(). This sets up default handlers and formatters on the root logger.
Limitations of basicConfig
While basicConfig is great for simple scripts, calling it multiple times has no effect. The first call configures the system, and subsequent calls are ignored. For complex applications, you should construct loggers, handlers, and formatters manually.
2. Manual Configuration Example
Here is how you instantiate loggers manually, customize propagation, and add dedicated handlers:
Summary
The Python logging module provides a flexible, hierarchical logging framework. By organizing loggers using dot notation and configuring multiple handlers, you can route runtime data to the correct destinations. In the next section, we will cover Log Levels in detail.