Django Logging Setup: From Development to Production
Tutorials January 20, 2026 ยท 4 min read

Django Logging Setup: From Development to Production

Complete guide to configuring Django logging for development and production. Learn structured logging, error tracking, and centralized log management.

Django's logging system is built on Python's powerful logging module, giving you fine-grained control over what gets logged and where it goes. This guide takes you from basic development logging to a production-ready centralized setup.

Understanding Django's Logging Architecture

Django logging has four key components:

  • Loggers - Named channels for log messages
  • Handlers - Destinations for log messages (files, console, remote)
  • Filters - Rules for what messages get through
  • Formatters - How messages are displayed

Basic Development Configuration

Start with a simple setup in your settings.py:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'DEBUG',
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

Structured Logging with JSON

For production, structured logs are essential for querying:

# Install python-json-logger
# pip install python-json-logger

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'json': {
            '()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
            'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
        },
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'json_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/app.json',
            'maxBytes': 10485760,  # 10MB
            'backupCount': 5,
            'formatter': 'json',
        },
    },
    'root': {
        'handlers': ['console', 'json_file'],
        'level': 'INFO',
    },
}

Adding Request Context

Include request information in your logs with middleware:

# middleware/logging.py
import logging
import uuid

logger = logging.getLogger(__name__)

class RequestLoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Generate request ID
        request.id = str(uuid.uuid4())[:8]

        # Add context to all logs during this request
        with logging.LoggerAdapter(logger, {
            'request_id': request.id,
            'user_id': getattr(request.user, 'id', None),
            'path': request.path,
            'method': request.method,
        }):
            response = self.get_response(request)

        return response

Using Loggers in Your Code

import logging

# Get a logger for this module
logger = logging.getLogger(__name__)

class OrderService:
    def create_order(self, user, items):
        logger.info('Creating order', extra={
            'user_id': user.id,
            'item_count': len(items),
            'total': sum(item.price for item in items),
        })

        try:
            order = Order.objects.create(user=user)
            # ... create order items
            logger.info('Order created successfully', extra={
                'order_id': order.id,
            })
            return order
        except Exception as e:
            logger.exception('Failed to create order', extra={
                'user_id': user.id,
                'error': str(e),
            })
            raise

Handling Exceptions

Configure Django to log all uncaught exceptions:

LOGGING = {
    # ... other config ...
    'loggers': {
        'django.request': {
            'handlers': ['json_file', 'mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        },
    },
}

Sending Logs to a Central Service

For production, send logs to a centralized platform:

Using Syslog

LOGGING = {
    'handlers': {
        'syslog': {
            'class': 'logging.handlers.SysLogHandler',
            'address': ('logs.example.com', 514),
            'facility': 'local0',
            'formatter': 'json',
        },
    },
}

Using HTTP (for services like 401 Clicks)

# Custom handler for HTTP log shipping
import requests
import json
from logging.handlers import QueueHandler, QueueListener
from queue import Queue

class HTTPHandler(logging.Handler):
    def __init__(self, url, api_key):
        super().__init__()
        self.url = url
        self.api_key = api_key
        self.session = requests.Session()

    def emit(self, record):
        try:
            payload = {
                'timestamp': record.created,
                'level': record.levelname,
                'logger': record.name,
                'message': record.getMessage(),
                **getattr(record, 'extra', {}),
            }
            self.session.post(
                self.url,
                json=payload,
                headers={'Authorization': f'Bearer {self.api_key}'},
                timeout=5,
            )
        except Exception:
            self.handleError(record)

Environment-Specific Configuration

# settings/base.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {...},
    'handlers': {
        'console': {...},
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
}

# settings/development.py
LOGGING['root']['level'] = 'DEBUG'
LOGGING['loggers'] = {
    'django.db.backends': {
        'handlers': ['console'],
        'level': 'DEBUG',  # See SQL queries
    },
}

# settings/production.py
LOGGING['handlers']['json_file'] = {...}
LOGGING['handlers']['syslog'] = {...}
LOGGING['root']['handlers'] = ['console', 'json_file', 'syslog']

Performance Considerations

Async Logging

import logging
from logging.handlers import QueueHandler, QueueListener
from queue import Queue

# Create a queue for async logging
log_queue = Queue()

# Background listener
listener = QueueListener(
    log_queue,
    logging.handlers.RotatingFileHandler('/var/log/django/app.log'),
)
listener.start()

# Use QueueHandler in your config
LOGGING['handlers']['async'] = {
    'class': 'logging.handlers.QueueHandler',
    'queue': log_queue,
}

Sampling High-Volume Logs

import random

class SamplingFilter(logging.Filter):
    def __init__(self, rate=0.1):
        super().__init__()
        self.rate = rate

    def filter(self, record):
        return random.random() < self.rate

Celery Task Logging

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

@app.task
def process_order(order_id):
    logger.info('Processing order', extra={'order_id': order_id})
    # ... task logic

Testing Your Logging

from django.test import TestCase
import logging

class LoggingTest(TestCase):
    def test_order_creation_logs(self):
        with self.assertLogs('myapp.services', level='INFO') as logs:
            OrderService().create_order(user, items)

        self.assertTrue(
            any('Order created' in log for log in logs.output)
        )

Conclusion

Proper Django logging setup pays dividends when debugging production issues. Start with structured JSON logging, add request context, and ship logs to a centralized service like 401 Clicks for easy searching and alerting.

Remember: the logs you don't have are the ones you'll need most when things go wrong.

A

Admin

Published on January 20, 2026