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?