Logging#

Logging is the act of keeping track of events that occur in a program and storing a message for each such event in a log file.

Logging#

The logging module is the Python built-in facility to handle log messages. Many third-party libraries use it under the hood, so it’s helpful to understand how it works and can be configured.

Log levels#

Multiple log levels allow to filter messages by severity: debug (10) < info (20) < warning (30) < error (40) < critical (50). By default debug and info messages are not shown, unless the log level is increased. When calling the logging methods directly (rather than creating a named logger as shown below), the behavior can be customized via logging.basicConfig. In particular we want to adapt the log format to include a timestamp and the name of the log level. Optionally all log messages can be redirected to a log file instead of being streamed to stderr.

import logging

logging.basicConfig(
    level=logging.DEBUG,  # set log level (default: logging.WARNING)
    format="{asctime} {levelname} {message}",  # log message format
    datefmt="%Y-%m-%dT%H:%M:%S%z",  # format timestamps as ISO 8601
    style="{",  # format string given in f-string style
    # filename="app.log",  # write to log file instead of stderr
    # filemode="w",  # write new log file instead of appending to old
)

logging.debug("detailed output for diagnostic purposes")
logging.info("typical events that occur during normal operation")
logging.warning("warn that something unusual happened")
logging.error("a serious problem occured but the program can continue")
logging.critical("a critical condition that must be investigated")

The above generates the following log output.

2025-02-24T15:50:24+0100 DEBUG detailed output for diagnostic purposes
2025-02-24T15:50:24+0100 INFO typical events that occur during normal operation
2025-02-24T15:50:24+0100 WARNING warn that something unusual happened
2025-02-24T15:50:24+0100 ERROR a serious problem occured but the program can continue
2025-02-24T15:50:24+0100 CRITICAL a critical condition that must be investigated

When logging errors related to exceptions, the full traceback can be included in the log entry.

import logging

try:
    1 / 0
except Exception:
    logging.exception("an exception occured with the following traceback")

Verbosity command-line argument#

Instead of having to edit the code to modify the log level, one can use command-line arguments -v, -vv, -vvv to increase the verbosity of the log messages.

import argparse
import logging

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("-v", help="verbosity (-v, -vv, -vvv)", action="count", default=0)
args = parser.parse_args()

LOGLVL = (4 - args.v) * 10  # default (error), -v (warning), -vv (info), -vvv (debug)
logging.basicConfig(level=LOGLVL)

logging.debug("detailed output for diagnostic purposes")
logging.info("typical events that occur during normal operation")
logging.warning("warn that something unusual happened")
logging.error("a serious problem occured but the program can continue")
logging.critical("a critical condition that must be investigated")

Named logger#

Logging is performed by calling methods on instances of the Logger class. The instances have names and are arranged in a hierarchy, with the root logger being on top. Named loggers allow to handle multiple file handles or streams.

The following example sends severity error and above to stderr, but all messages to a debug log file.

import logging

# Get or create logger instance with given name.
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Set log format.
log_formatter = logging.Formatter(
    datefmt="%Y-%m-%dT%H:%M:%S%z",
    fmt="{asctime} {levelname}: {message}",
    style="{",
)

# Set log to stderr.
handler_console = logging.StreamHandler()
handler_console.setFormatter(log_formatter)
handler_console.setLevel(logging.ERROR)
logger.addHandler(handler_console)

# Set log to file.
handler_file = logging.FileHandler("debug.log")
handler_file.setFormatter(log_formatter)
logger.addHandler(handler_file)

logger.debug("detailed output for diagnostic purposes")
logger.info("typical events that occur during normal operation")
logger.warning("warn that something unusual happened")
logger.error("a serious problem occured but the program can continue")
logger.critical("a critical condition that must be investigated")

See also

To learn more about logging, the official Python documentation includes a howto as well as a cookbook.

Tracing#

Tracing is the act of watching the flow of execution of a program. In contrast to print debugging, where only a few print statements provide selective insight into the program’s flow, tracing follows all calls. The trace module allows to trace Python statement execution without much effort and can provide valuable debugging info.

$ python3 -m trace -t myscript.py  # trace executed lines
$ python3 -m trace -l myscript.py  # list executed functions

If the output is too overwhelming it can help to filter out calls to the standard library by ignoring those directories.

$ python3 -m trace --ignore-dir=$(python3 -c 'import sys; print(":".join(sys.path)[1:])') -t myscript.py