Flask logging setup

dictConfig, user defined object

Setup a python logging configuration with dictConfig

Logging configuration can be defined on different ways like a dict, ini file, yaml or json somethimes is more flexible to do it on code with python, all depends on the use case.

This small example show how to setup the logging configuration using dictConfig method and how to add an user defined object to the logging filter.

import os
import logging
import socket
from flask import Flask
from logging.config import dictConfig

class ContextFilter(logging.Filter):
    hostname = socket.gethostname()

    def filter(self, record):
        record.hostname = ContextFilter.hostname
        return True

logging_configuration = dict(
    version=1,
    disable_existing_loggers=False,
    formatters={
        "default": {"format": "[%(hostname)s %(asctime)s] %(levelname)s in %(module)s: %(message)s"},
    },
    filters={"hostname_filter": {"()": ContextFilter}},
    handlers={
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
            "filters": ["hostname_filter"],
        }
    },
    root={"handlers": ["console"], "level": os.getenv("LOG_LEVEL", "INFO")},
)

dictConfig(logging_configuration)
app = Flask(__name__)
logger = logging.getLogger(__name__)


@app.route("/")
def home():
    logger.debug("debug message")
    logger.info("info message")
    logger.warning("warning message")
    logger.error("error message")
    logger.critical("critical error message")
    return "Hello World"


if __name__ == "__main__":
    app.run()

The filter hostname_filter use the special key "()" which means that user-defined instantiation is wanted. ContextFilter subclass logging.Filter and overrides the filter method so when key hostname is found it will get his value from the ContextFilter class using socket.gethostname() and the application output will show:

# running the flask app inside the virtualenv
 (.venv) $ flask run                                                      
 * Environment: developement
 * Debug mode: off
[test.lab 2019-05-27 18:20:05,193] INFO in _internal:  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
[test.lab 2019-05-27 18:20:08,437] DEBUG in app: debug message
[test.lab 2019-05-27 18:20:08,437] INFO in app: info message
[test.lab 2019-05-27 18:20:08,437] WARNING in app: warning message
[test.lab 2019-05-27 18:20:08,437] ERROR in app: error message
[test.lab 2019-05-27 18:20:08,437] CRITICAL in app: critical error message
[test.lab 2019-05-27 18:20:08,438] INFO in _internal: 127.0.0.1 - - [27/May/2019 18:20:08] "GET / HTTP/1.1" 200 -

# default formatter: "[%(hostname)s %(asctime)s] %(levelname)s in %(module)s: %(message)s"

reference: Python logging - user-defined-objects