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

  • Create a middleware folder in your project directory.

    mkdir middleware
    touch middleware/__init__.py
    
  • Create a RequestLogMiddleware class in /middleware/request_log.py, see below:

    
    """
    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
    
            # request passes on to controller
            response = self.get_response(request)
    
            # add runtime to our log_data
            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
    

    Django allows us to write pre and post api-processing logic via __call__ method. Here we are logging only requests with /api/ in its path.

Activate the middleware

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

MIDDLEWARE = [
    ...
    "django.contrib.messages.middleware.MessageMiddleware",
    # Request Logger
    "<application>.middleware.request_log.RequestLogMiddleware",
]

Replace <application> with your application name.