Configuring with dictConfig

logging · Advanced · 14 min

One reviewable config (often loaded from YAML or TOML) replaces scattered setup code and works across environments.

logging.config.dictConfig(config) builds the whole logging setup from a dictionary, the format frameworks like Django use. Keys:

  • version: always 1.
  • formatters: named formatters (format, datefmt, style, or () for a custom factory).
  • filters: named filters.
  • handlers: each with class, level, formatter, and constructor arguments such as filename or stream.
  • loggers: per-name level, handlers, propagate.
  • root: the root logger's config.
  • disable_existing_loggers: defaults to True, which disables every logger created before the call. Set it to False almost always, otherwise module-level loggers created at import time go silent.

Values like "ext://sys.stdout" refer to Python objects, and "cfg://..." refers to other parts of the config. Since 3.12 a QueueHandler can be configured fully here, including its listener.

Keep the dict in a module or load it from a file (json.load, tomllib.load), apply it once at startup, and vary only the levels per environment. logging.getHandlerByName("console") (3.12+) fetches a configured handler later. The older fileConfig (INI format) still works but is less capable.

Example

import logging, logging.config

CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {"short": {"format": "%(levelname)s %(name)s: %(message)s"}},
    "handlers": {
        "console": {"class": "logging.StreamHandler", "level": "INFO",
                    "formatter": "short", "stream": "ext://sys.stdout"},
    },
    "loggers": {"urllib3": {"level": "WARNING"}},
    "root": {"level": "DEBUG", "handlers": ["console"]},
}
logging.config.dictConfig(CONFIG)

logging.getLogger("myapp").info("configured")    # INFO myapp: configured
logging.getLogger("urllib3").info("noise")       # filtered by level
print(logging.getHandlerByName("console").level) # 20

Remember

  • dictConfig sets up formatters, handlers, loggers and root from one dict
  • Set disable_existing_loggers to False
  • ext:// and cfg:// reference objects and other config entries

Watch out for

  • Leaving disable_existing_loggers at its default True, silencing loggers created at import time.

Check yourself

  1. After dictConfig with defaults, loggers created at import time stop logging. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/logging.config.html#logging-config-dictschema