🎓 A1Learner
🔍

Python Logging Configuration

Logging configuration is the setup that controls how each log line looks and where it goes.

Pick the parts of a line

The format string names each part. This line shows the level, the logger name, and the message.

import logging, sys

logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(levelname)s | %(name)s | %(message)s")
logger = logging.getLogger("billing")
logger.info("Invoice sent")

A handler is a destination

You build a handler and give it a formatter. Then you add the handler to a logger.

import logging, sys

handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger = logging.getLogger("demo")
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.propagate = False
logger.info("Hello from the handler")

A level for each logger

Two loggers share one setup here. Each logger still keeps its own level.

import logging, sys

logging.basicConfig(level=logging.DEBUG, stream=sys.stdout, format="%(name)s %(levelname)s: %(message)s")
quiet = logging.getLogger("quiet")
quiet.setLevel(logging.WARNING)
chatty = logging.getLogger("chatty")
quiet.info("Hidden")
chatty.info("Shown")
quiet.warning("Also shown")

Setup as a dictionary

dictConfig reads the whole setup from a dictionary. This is only a first look. The last lesson of this section uses it in full.

import logging.config

config = {
    "version": 1,
    "formatters": {"simple": {"format": "%(levelname)s: %(message)s"}},
    "handlers": {"console": {"class": "logging.StreamHandler"}},
    "root": {"level": "INFO", "handlers": ["console"]},
}
logging.config.dictConfig(config)

Example

import logging, sys

logger = logging.getLogger("shop")
logger.setLevel(logging.DEBUG)
logger.propagate = False

formatter = logging.Formatter("%(levelname)s %(name)s: %(message)s")

console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.INFO)
console.setFormatter(formatter)

file_handler = logging.FileHandler("app.log", mode="w")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)

logger.addHandler(console)
logger.addHandler(file_handler)

logger.debug("Cart has 3 items")
logger.info("Order placed")
logger.warning("Stock is low")

file_handler.flush()
print("--- file ---")
with open("app.log") as f:
    print(f.read(), end="")

Key points

  • Formatter: an object that turns a log record into one line of text
  • Handler: an object that sends each line to one place, such as the screen or a file
  • Use %(levelname)s, %(name)s, and %(message)s in a format string to pick the parts of a line
  • The logger level is checked first
  • Each handler level is checked after that.
  • Use logging.config.dictConfig(...) to set up logging from a dictionary
  • basicConfig does nothing when the root logger already has handlers
  • Logging configuration controls the look of each log line and where each line goes.

In this example, one logger sends each record to two handlers. The console handler sits at INFO, so it skips the DEBUG line. The file handler sits at DEBUG, so it writes all three lines to app.log. Both handlers share one formatter. The code then flushes the file handler and reads app.log back. The printed file part proves the DEBUG line reached the file. Run this program from the folder that should hold app.log.

Note

Set logging up once, early, or build your own handlers.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

You want DEBUG lines in a file but only INFO lines on screen. What setup does that?