Python Logging Anatomy
- Python’s
logging module has five core components: Loggers, Log Records, Handlers, Formatters and Filters.
- Loggers are hierarchical objects your code calls to emit messages at various severity levels.
- Each call to a logger creates a LogRecord capturing metadata: level, message, timestamp, source, thread/process IDs, exception info, etc.
- Handlers attached to loggers dispatch records to destinations (console, files, network).
- Formatters define how a
LogRecord is rendered into the final string emitted by a handler.
import logging
root_logger = logging.getLogger()
print(f"Root logger: name={root_logger.name}, level={logging.getLevelName(root_logger.level)}")
app_logger = logging.getLogger("app")
print(f"App logger: name={app_logger.name}, level={logging.getLevelName(app_logger.level)}, parent={app_logger.parent.name}")
network_logger = logging.getLogger("app.network")
print(f"Network logger: name={network_logger.name}, level={logging.getLevelName(network_logger.level)}, parent={network_logger.parent.name}")
Log Records
- Each logging call (
logger.info(), logger.error(), etc.) creates a LogRecord object behind the scenes.
- A
LogRecord includes attributes such as name, levelno, levelname, pathname, lineno, funcName, asctime, message, plus any user-supplied extra data.
- Handlers and formatters use these attributes to filter and render the log entry.
from logging import LogRecord
record = LogRecord(
name="app.network",
level=logging.ERROR,
pathname="/path/to/file.py",
lineno=43,
msg="My log message",
args=(),
exc_info=None
)
print("LogRecord contents:")
for attr in ("name", "levelname", "pathname", "msg"):
print(f" {attr} => {getattr(record, attr)}")
Handlers
- Handlers determine where log records are sent (console, file, network, etc.).
- Each handler has its own level: it filters out any record whose level is below its threshold.
- Common handlers include:
StreamHandler (console),
FileHandler (single file),
RotatingFileHandler,
TimedRotatingFileHandler,
SysLogHandler,
HTTPHandler,
NullHandler.
import sys
demo_logger = logging.getLogger("handler_demo")
demo_logger.setLevel(logging.INFO)
demo_logger.handlers.clear()
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.DEBUG)
demo_logger.addHandler(stream_handler)
demo_logger.debug("Debug message: will not show")
demo_logger.info("Info message: will show")
demo_logger.warning("Warning message: will show")
demo_logger.error("Error message: will show")
Formatters
- Formatters specify the layout of the final log message string.
- You define a format string using
%(attribute)s or %(attribute)d placeholders.
- Common attributes:
asctime, levelname, name, message, filename, lineno, funcName, process, thread.
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
stream_handler.setFormatter(formatter)
demo_logger.warning("Formatted warning")