logging

I have always wondered how logs were implemented by the developers. Especially the ones like dmesg and journalctl. Most of the time, there is no need to implement one from scratch since there’s already a library intended for logging.

Luckily, Python has a default library for logging!

Setup

Import logging.

import logging

Use the logger.

logger = logging.getLogger(__name__)
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logger.info("Logging started.")

That’s it!

But the default behavior uses very basic logging format. What if I wanted a different format? A custom one.

Formatter

Define the format for the logger to use.

FORMAT: str = "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)d] - %(message)s"

Create a function for setup.

def setup(level=logging.INFO):
    logger = logging.getLogger()
    logger.setLevel(level)

    for handler in logger.handlers[:]:
        logger.removeHandler(handler)

    formatter = logging.Formatter(FORMAT, datefmt="%Y-%m-%d %H:%M:%S")

    console_handler = logging.StreamHandler()
    console_handler.setLevel(level)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

Use the logger.

logs.setup(logging.DEBUG)
logger = logging.getLogger(__name__)

This is enough for adding a logging mechanism right into the console.

Exceptions and Warnings

Sometimes I want to log Python errors outside the script as well. Not just the current functions.

Setup handler for exceptions.

import sys
def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logger = logging.getLogger("UncaughtException")
    logger.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

Setup handler for warnings.

import warnings
def handle_warning(message, category, filename, lineno, file=None, line=None):
    logger = logging.getLogger("Warnings")
    logger.warning(f"{filename}:{lineno}: {category.__name__}: {message}")

Update the setup function.

def setup(level=logging.INFO):
    sys.excepthook = handle_exception
    warnings.showwarning = handle_warning
    ...

This should now be usable.