Up and Running With Python Logging Facility


Python comes bundled with logging module. This module is very versatile, and feature rich. However, as a beginner, you mostly want to just get up and runnig, and use the logger in the most simple way. Luckally, logging in Python does not have to be hard and painful.

Log levels

There are five log levels, and they are listed here in order of increasing severity:

  • Debug
  • Info
  • Warning
  • Error
  • Critical

Quick and dirty way

In order to log a message, you have to get a logger:

import logging


logger = logging.getLogger(__name__)
logger.info('Informative message about module')

__name__ is the name of the module from which the logger is invoked. You can use __name__ if you have a plan to route logs from different modules and deal with them differently. However, you’re probably in a hurry, so you can simply use a default value for a logger, e.g. something like logging.getLogger('default').

There are five different methods that you can invoke to print a log. They correspond to the five log levels mentioned above:

logger.debug('Debugging value')
logger.info('Informative message about module')
logger.warning('Something bad may have happened')
logger.error('Something bad definitely happened')
logger.critical('ABANDON YOUR POSTS!')

More structured way

If you want to learn to log in a more structured way then you have to learn more about logging facility in Python. Namely, the module has four basic classes:

  • Loggers: exposes the interface from your app code
  • Handlers: send log records to various outputs (e.g. write logs to files, STDOUT, STDERR, or a service e.g. Graylog)
  • Filters: are used to filter which logs are sent to the output
  • Formaters: are used to specify the format of the logged message

There are three ways in which you can configure your logging:

  • With ini file
  • Via dict
  • Directly in the source code

See this article for more info on that.

In this article, we will use dictionary to configure our logger:

# log.py
import logging
import logging.config 


LOGGING = {
    'version': 1,
    'formatters': {
        'default': {
            'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
        },
    },
    'handlers': {
        'console':{
            'formatter': 'default',  # reference to the formatter above
            'class': 'logging.StreamHandler',  # logs to STDERR
        },
        # Comment out following lines if you want to log to a file, but make
        # sure that file exists on the path!
        # 'file': {
        #     'class': 'logging.FileHandler',  # Write logs to a file
        #     'filename': '/var/log/python/debug.log',
        # },
    },
    'loggers': {
        'default': {
            'handlers': ['console'],  # reference to the handler above
            'level': 'DEBUG',
        },
    }
}

logging.config.dictConfig(LOGGING)
logger = logging.getLogger('default')

You should put this in the main file of your application. Oftentimes I just need a simple logger to log everything, so I make a wrapper class or a module from which I’ll import the logger. For example, you can place the above code in log.py of your project, and simply log with the instance of the logger from the module. Since the module is loaded only once in Python (except when it’s not), the logger will act as singleton:

# project.py
from log import logger

logger.info(f"I'm in {__name__}")

Additionally, you can modify log level using environment variable. For example, let’s say you want to display log only above 'ERROR' level. You can define LOG_LEVEL environment variable in your environment, and use the os module to modify the logger’s behaviour:

# log.py
import os

LOGGING = {
    ...
    'loggers': {
        'default': {
            'handlers': ['console'],  # reference to the handler above
            'level': os.getenv('LOG_LEVEL', 'DEBUG'),
        },
    }
}

We're not spammers, and you can opt-out at any moment. We hate spam as much as you do.

powered by TinyLetter

See also