Skip to content

Logging

Overview

SpeedPy uses django-structlog for structured logging. It provides consistent, machine-parseable log output with automatic request context binding.

Configuration

The logging setup in project/settings.py includes three formatters:

  • plain_console — human-readable console output (used by default)
  • json_formatter — JSON output for log aggregation services
  • key_value — key=value format
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "json_formatter": {
            "()": structlog.stdlib.ProcessorFormatter,
            "processor": structlog.processors.JSONRenderer(),
        },
        "plain_console": {
            "()": structlog.stdlib.ProcessorFormatter,
            "processor": structlog.dev.ConsoleRenderer(),
        },
    },
    "handlers": {
        "console": {"class": "logging.StreamHandler", "formatter": "plain_console"}
    },
    "loggers": {"": {"handlers": ["console"], "level": "DEBUG"}},
}

Middleware

The structlog request middleware is included to automatically bind request data (user, IP, request ID) to log entries:

MIDDLEWARE = [
    ...
    "django_structlog.middlewares.RequestMiddleware",
]

DJANGO_STRUCTLOG_CELERY_ENABLED = True
DJANGO_STRUCTLOG_COMMAND_LOGGING_ENABLED = True

Usage

import structlog

logger = structlog.get_logger(__name__)

def my_view(request):
    logger.info("processing_request", user_id=request.user.id)
    try:
        result = do_something()
        logger.info("processing_complete", result=result)
    except Exception:
        logger.exception("processing_failed")

Key practices: - Use structlog.get_logger(__name__) at module level - Pass context as keyword arguments, not in the message string - Use snake_case for event names - Use logger.exception() for errors (automatically includes traceback)

Celery Integration

Structlog is also configured for Celery workers in project/celeryapp.py via DjangoStructLogInitStep. This ensures Celery tasks get the same structured logging format.

from django_structlog.celery.steps import DjangoStructLogInitStep

app.steps["worker"].add(DjangoStructLogInitStep)

Switching to JSON in Production

To output JSON logs (for services like Datadog, Elastic, etc.), change the handler formatter:

"handlers": {
    "console": {"class": "logging.StreamHandler", "formatter": "json_formatter"}
},