This lesson on Logging & Configuration is hands-on and example-driven. You will replace ad-hoc print statements with Python's built-in logging module to persist execution history and track system behavior over time. You will configure log severity thresholds, direct log output to persistent disk files, and customize log message formatting using standard LogRecord attributes.
What You'll Be Able To Do
- Import and configure Python's standard logging module without third-party dependencies.
- Assign appropriate standard log levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) to application events.
- Redirect console log output to persistent disk files using logging.basicConfig.
- Format log records using attributes such as timestamp, severity level name, and message content.
Detailed Concept Walkthrough
1. Standard Logging Severity Levels
Logging levels categorize diagnostic information into standardized severities so that messages can be filtered systematically rather than dumped unconditionally. Python defines five standard levels with numeric weights that determine whether an event meets the active capture threshold.
- Threshold Evaluation: Python compares each log call's severity to the logger's threshold, emitting the record only if the call's level is greater than or equal to the configured minimum.
- Under the Hood: Log level constants correspond to integer values in steps of 10: DEBUG is 10, INFO is 20, WARNING is 30, ERROR is 40, and CRITICAL is 50.
- Default Behavior: The default root logging level is WARNING (30), which causes all DEBUG and INFO messages to be silently discarded unless explicitly reconfigured.
- Level Semantics: DEBUG tracks granular diagnostics, INFO confirms expected operations, WARNING flags unexpected recoverable conditions, ERROR captures operational failures, and CRITICAL indicates application-halting crashes.
import logging
# Default threshold is WARNING; DEBUG and INFO do not output by default
logging.debug("Diagnostic detail for debugging")
logging.info("Confirmation of normal operation")
logging.warning("Warning: recoverable issue encountered")
logging.error("Error: operation failed")
logging.critical("Critical: application cannot continue")
Key Takeaway: Events below the active threshold are ignored entirely, making severity levels the primary tool for runtime log filtering.
2. Global Configuration with basicConfig
The basicConfig method provides a one-stop interface to set the global root logger's severity threshold, destination file, and output layout.
- Mechanism: Calling logging.basicConfig with keyword arguments adjusts parameters like
level,filename, andformatbefore log records are emitted. - Syntax Rule: Level constants passed to the
levelparameter must be uppercase module attributes (e.g.,logging.DEBUG), distinguishing them from logging methods likelogging.debug(). - Execution Flow: When configured, basicConfig sets up the root logger handlers; subsequent log calls across simple scripts inherit these global definitions.
- Best Practice: Define logging.basicConfig once at the entry point of your script before executing any log-producing logic.
import logging
# Configure root logger with DEBUG threshold
logging.basicConfig(
level=logging.DEBUG
)
# Now DEBUG messages will be captured and output
logging.debug("Add function executed with args: 10, 5")
Key Takeaway: logging.basicConfig applies global settings to the root logger and must use uppercase severity constants for the level argument.
3. File-Based Log Persistence
Writing log records to persistent storage files captures historical telemetry across multiple program runs instead of losing ephemeral console output.
- Mechanism: Providing the
filenameargument to logging.basicConfig instructs the logging system to append log events to a physical disk file instead of standard stream output. - Under the Hood: Each execution appends new records to the designated log file without overwriting existing historical data from prior executions.
- Best Practice: Use persistent log files in data pipelines and long-running services to monitor errors, analyze execution trends over time, and feed downstream visualization tools.
import logging
# Route all INFO and higher logs to a file
logging.basicConfig(
filename="app_execution.log",
level=logging.INFO
)
logging.info("Data extraction job started")
logging.info("Processed 500 records successfully")
Key Takeaway: Setting a filename in basicConfig automatically persists appended log records across script runs for long-term diagnostics.
4. Custom LogRecord Formatting
LogRecord attributes allow you to customize output strings into structured, human-readable entries containing execution context like timestamps and severity names.
- Mechanism: The
formatargument in logging.basicConfig accepts formatted template strings that interpolate predefined LogRecord attributes using%(attribute)ssyntax. - Key Attributes: Commonly used attributes include
%(asctime)sfor human-readable timestamps including milliseconds,%(levelname)sfor the severity string, and%(message)sfor the logged text. - Syntax Rule: Attribute placeholders must match Python's LogRecord attribute names exactly to prevent string interpolation errors during log dispatch.
import logging
# Configure custom layout: timestamp : severity : message
logging.basicConfig(
filename="pipeline.log",
level=logging.INFO,
format="%(asctime)s:%(levelname)s:%(message)s"
)
logging.info("ETL batch completed successfully")
Key Takeaway: LogRecord format strings provide contextual metadata including timestamps and severity names alongside log payloads.
Topics Covered in Logging & Configuration
- Motivation for Logging (0:00 - 1:35) — Explains why structured logging replaces ad-hoc print statements to observe application trends and errors over time.
- Standard Log Levels (1:35 - 3:10) — Introduces the five standard log levels and demonstrates default suppression of DEBUG and INFO messages.
- Configuring Threshold Levels (3:10 - 4:40) — Demonstrates setting the root logger threshold to DEBUG using logging.basicConfig and numeric level constants.
- Logging to Files (4:40 - 5:55) — Configures the filename parameter in basicConfig to append execution records persistently to disk.
- Customizing Log Format (5:55 - 7:35) — Applies format string codes like asctime, levelname, and message to generate structured log outputs.
- Applying Logging to Classes (7:35 - 9:35) — Implements file-based INFO logging inside an Employee class to record instance creation events over time.
Python Cheat Sheet
-
import logging— Imports Python's standard logging module without extra dependenciesimport logging -
logging.basicConfig(level=...)— Sets minimum capture threshold using uppercase level constantslogging.basicConfig(level=logging.DEBUG) -
logging.basicConfig(filename=...)— Directs output to a persistent disk log filelogging.basicConfig(filename="test.log", level=logging.INFO) -
logging.basicConfig(format=...)— Defines log message structure using LogRecord attribute placeholderslogging.basicConfig(format="%(asctime)s:%(levelname)s:%(message)s") -
logging.debug(msg)— Logs detailed diagnostic information (level 10)logging.debug("Calculated total: %s", total_value) -
logging.info(msg)— Logs standard confirmation of expected software behavior (level 20)logging.info("Created employee instance: John Doe") -
logging.warning(msg)— Logs unexpected events or non-fatal software issues (level 30)logging.warning("Low disk space detected") -
logging.error(msg)— Logs serious operational failures preventing specific function execution (level 40)logging.error("Database connection timed out")
Comparison Table
| Logging Level | Numeric Value | Intended Use Case |
|---|---|---|
| DEBUG | 10 | Detailed diagnostic problem investigation |
| INFO | 20 | Confirmation of expected normal behavior |
| WARNING | 30 | Unexpected events or non-fatal problems |
| ERROR | 40 | Serious failure executing a function |
| CRITICAL | 50 | Program-halting failure stopping entire execution |
Common Pitfalls
- Mistake: Expecting logging.debug or logging.info to print output under default settings. Avoid: Set level to logging.DEBUG or logging.INFO via logging.basicConfig because default threshold is WARNING.
- Mistake: Passing lowercase function names like logging.debug as the basicConfig level argument. Avoid: Pass uppercase integer constants such as logging.DEBUG or logging.INFO to the level parameter.
- Mistake: Relying on print statements for production error tracking and monitoring. Avoid: Use logging to persist time-stamped, severity-categorized records across multiple runs to persistent log files.
FAQs
- Why does logging.basicConfig not display DEBUG logs in the console by default? Python's default logging threshold is set to WARNING, which automatically suppresses all DEBUG and INFO messages unless explicitly configured otherwise.
- What is the difference between logging.DEBUG and logging.debug()? logging.DEBUG is an uppercase integer constant (value 10) used for configuration, whereas logging.debug() is the callable method that emits a log record.
- Does writing to a log file overwrite existing logs upon re-running the script? No, logging.basicConfig appends new log records to the end of the specified file, preserving history across runs.
- What are the common format codes for standardizing log message outputs? Common LogRecord attributes include %(asctime)s for timestamps, %(levelname)s for severity names, and %(message)s for the log text.