Introduction

django-rich-logging outputs the current Django request in a live updating table for easy parsing.

Installation

poetry add django-rich-logging OR pip install django-rich-logging

Configuration

django-rich-logging uses the log records emitted by the django.server logger to do its magic. However, configuring logging in Django (and in Python in general) can sometimes be a little obtuse.

Minimal configuration

# settings.py

# other settings here

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "django_rich_logging": {
            "class": "django_rich_logging.logging.DjangoRequestHandler",
        },
    },
    "loggers": {
        "django.server": {"handlers": ["django_rich_logging"]},
        "django.request": {"level": "CRITICAL"},
    },
}

# other settings here
  • DjangoRequestHandler handles log messages from the django.server logger

    • the level must be INFO or below to get all requests (which it is by default)

    • there must be a handler which uses django_rich_logging.logging.DjangoRequestHandler

  • django.request should be set to CRITICAL otherwise you will see 4xx and 5xx status codes getting logged twice

Only logging when debug

Most of the time, you will only want this type of logging when in local development (i.e. DEBUG = True). The require_debug_true logging filter can be used for this purpose.

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "filters": {
        "require_debug_true": {
            "()": "django.utils.log.RequireDebugTrue",
        },
    },
    "handlers": {
        "django_rich_logging": {
            "class": "django_rich_logging.logging.DjangoRequestHandler",
            "filters": ["require_debug_true"],
        },
    },
    "loggers": {
        "django.server": {"handlers": ["django_rich_logging"]},
        "django.request": {"level": "CRITICAL"},
    },
}

Column configuration

The columns that are logged are configurable via the columns key in the django_rich_logging handler. The default column configuration is as follows.

...
"handlers": {
    "django_rich_logging": {
        "class": "django_rich_logging.logging.DjangoRequestHandler",
        "columns": [
            {"header": "Method", "format": "[white]{method}", "style": "{"},
            {"header": "Path", "format": "[white bold]{path}", "style": "{"},
            {"header": "Status", "format": "{status_code}", "style": "{"},
            {"header": "Size", "format": "[white]{size}", "style": "{"},
            {
                "header": "Time",
                "format": "[white]{created}",
                "style": "{",
                "datefmt": "%H:%M:%S",
            },
        ],
    },
},
...
  • header is the name of the column header

  • format follows the same conventions as a normal Python logging formatter which uses string interpolation to insert data from the current request

  • Similar to a logging formatter, style can be specified for the type of string interpolation to use (e.g. %, {, or $); to follow legacy Python conventions, style defaults to %

The available information that be specified in format:

  • method: HTTP method, e.g. GET, POST

  • path: The path of the request, e.g. /index

  • status_code: Status code of the request, e.g. 200, 404, 500

  • size: Length of the content

  • created: datetime of when the log was generated; can be additionally formatted into a string with datefmt

Formatted output can be colored or styled with the use of rich markup, e.g. [white bold]something here[\white bold]

More information about Django logging

Other logging approaches

Inspiration and thanks

Dependencies