Share Python logging configuration across modules

Create a named logger in each module and configure handlers at application startup, with explicit control over propagation and existing loggers.

Best practices
Python programming and application logging

Create logging.getLogger(__name__) in each module, then configure logging once at the application entry point. Modules emit records; the application decides their destinations and formatting.

Keep module code focused on records

# myapp/service.py
import logging

logger = logging.getLogger(__name__)

def process():
    logger.info("Processing started")

When imported as myapp.service, its logger has that name. The dotted name connects it to the myapp logger and ultimately the root logger.

Configure the application before it starts work

# main.py
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
)

from myapp import service
service.process()

A logger with the default NOTSET level inherits an effective level from its ancestors. Accepted records normally propagate to ancestor handlers. Propagation can be disabled, and handler levels and filters still apply; the root handler does not automatically receive every possible record.

basicConfig() normally does nothing if root already has handlers. Calling it from many modules does not reliably overwrite configuration. Repeatedly attaching handlers, or attaching them to both a child and its ancestors, can produce duplicate output.

Use an external configuration

For an application that uses an INI file:

import logging.config
logging.config.fileConfig("logging.conf", disable_existing_loggers=False)

A complete small logging.conf is:

[loggers]
keys=root

[handlers]
keys=console

[formatters]
keys=std

[logger_root]
level=INFO
handlers=console

[handler_console]
class=StreamHandler
level=NOTSET
formatter=std
args=(sys.stderr,)

[formatter_std]
format=%(asctime)s [%(name)s] %(levelname)s: %(message)s

disable_existing_loggers=False preserves existing named loggers instead of unexpectedly disabling ones created before configuration. dictConfig() is another option for managing multiple handlers and structured application settings.

FAQ

How do I enable DEBUG for one module? Set logging.getLogger("myapp.db").setLevel(logging.DEBUG). Its propagated DEBUG records can reach a root handler even if root's own logger level is INFO, because ancestor logger levels are not rechecked during propagation. The destination handler must allow DEBUG too.

How do I reduce third-party noise? Set the relevant library logger, such as urllib3, to WARNING. Check whether a descendant has its own explicit level or handler if records still appear.

Should reusable libraries configure handlers? Let the calling application control output. A library may attach a NullHandler according to standard logging guidance, but should not impose file or console output on every consumer.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting