Django Middleware: log requests and responses

2-minute read

From Django’s context:

Middleware is a framework of hooks into Django’s request/response processing.
It’s a light, low-level “plugin” system for globally altering Django’s input or output.

Middleware is like a layer which processes every request and response. Instead of logging requests and responses in resp. views, its better to do at middleware layer which will log every incoming request. Why better?

  • it will log unhandled requests/views
  • one time job, no need to configure for every request/view

Here, we are using Django’s middleware semantics to construct a middleware which will log all requests and corresponding responses.

  • Create a middleware file

    We will create a RequestLogMiddleware class in /middleware/request_log.py in your <application> folder, like:

    
    """
    Middleware to log `*/api/*` requests and responses.
    """
    import socket
    import time
    import json
    import logging
    
    request_logger = logging.getLogger(__name__)
    
    class RequestLogMiddleware:
        """Request Logging Middleware."""
    
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            start_time = time.time()
            log_data = {
                "remote_address": request.META["REMOTE_ADDR"],
                "server_hostname": socket.gethostname(),
                "request_method": request.method,
                "request_path": request.get_full_path(),
            }
    
            # Only logging "*/api/*" patterns
            if "/api/" in str(request.get_full_path()):
                req_body = json.loads(request.body.decode("utf-8")) if request.body else {}
                log_data["request_body"] = req_body
    
            response = self.get_response(request)
    
            if response and response["content-type"] == "application/json":
                response_body = json.loads(response.content.decode("utf-8"))
                log_data["response_body"] = response_body
            log_data["run_time"] = time.time() - start_time
    
            request_logger.info(msg=log_data)
    
            return response
    
        # Log unhandled exceptions as well
        def process_exception(self, request, exception):
            try:
                raise exception
            except Exception as e:
                request_logger.exception("Unhandled Exception: " + str(e))
            return exception
    

    Here we are logging only requests with /api/ in its path. All the processing is done in __call__ method.
    Note: Do add __init__.py in middleware folder.

  • Activate the middleware

    Activate the middleware by adding its path in MIDDLEWARE list in your Django’s application settings.py (here: last value):

    MIDDLEWARE = [
        "django.middleware.security.SecurityMiddleware",
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        # Request Logger
        "<application>.middleware.request_log.RequestLogMiddleware",
    ]
    

    Replace <application> with your application name.