๐Ÿ“logging: levels, handlers, formatters, getLoggerLESSON

Python Logging

The logging module is Python's built-in solution for recording program events. Replacing print() with logging gives you control over what gets recorded, where it goes, and how it's formatted โ€” without changing your code.

Why Use logging Instead of print()

print() goes to stdout and has no built-in way to filter by severity, route to files, or disable in production. The logging module provides all of this:

Log Levels

Python defines five standard log levels, in increasing severity:

LevelValueWhen to use
DEBUG10Detailed diagnostic info โ€” only useful for debugging
INFO20Confirmation that things are working as expected
WARNING30Something unexpected happened, but the program continues
ERROR40A serious problem โ€” the program couldn't do something
CRITICAL50A fatal error โ€” the program may not be able to continue

logging.basicConfig()

The simplest setup โ€” configures the root logger. Call this once, early in your program:

Important: basicConfig() only works if the root logger has no handlers yet. It has no effect if called after logging has already been configured.

Format String Variables

VariableMeaning
%(asctime)sHuman-readable time
%(name)sLogger name
%(levelname)sLevel name (DEBUG, INFO, โ€ฆ)
%(message)sThe log message
%(filename)sSource filename
%(lineno)dLine number in source
%(funcName)sFunction name

logging.getLogger(name)

In any module, get a logger named after the module:

Using __name__ creates a hierarchy that mirrors your package structure. You can configure logging for myapp and it automatically applies to myapp.database, myapp.api, etc.

Handlers โ€” Where Logs Go

A handler sends log records to a destination. One logger can have multiple handlers:

Formatters โ€” How Logs Look

Attach a Formatter to a handler to control the output format:

Logging Exceptions

Log exceptions with their full traceback:

Logger Propagation

By default, log records propagate up to parent loggers. The root logger is the ultimate parent:

Logging Context with extra={}

Pass extra fields to add contextual information to log records:

Best Practices

  1. Never use print() in library code โ€” it pollutes user output. Use logging.
  2. Use logging.getLogger(__name__) โ€” not the root logger directly.
  3. Set levels on handlers, not messages โ€” one codebase, multiple deployment configurations.
  4. Log the why, not the what โ€” "Connection failed after 3 retries" not "Error occurred".
  5. Use lazy formatting: logger.debug("Value: %s", expensive_repr) โ€” the string isn't formatted if DEBUG is disabled.

Knowledge Check

What is the correct order of Python logging levels from lowest to highest severity?

Why should you use `logging.getLogger(__name__)` instead of using the root logger directly?

What is the most efficient way to log a debug message that involves an expensive string operation?