File size: 2,490 Bytes
266d7bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import time

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

from src.utils.logger_util import setup_logging

logger = setup_logging()


class LoggingMiddleware(BaseHTTPMiddleware):
    """Middleware for logging incoming HTTP requests and their responses.

    Logs the request method, URL, client IP, and headers.
    Excludes sensitive headers like Authorization and Cookie.
    as well as the response status code and request duration in milliseconds.
    Exceptions raised during request processing are logged with the full traceback.

    Usage:
        Add this middleware to your FastAPI app:
            app.add_middleware(LoggingMiddleware)

    Attributes:
        logger: Configured logger from `setup_logging`.

    """

    async def dispatch(self, request: Request, call_next):
        """Process the incoming request, log its details, and measure execution time.

        Args:
            request (Request): The incoming FastAPI request.
            call_next: Callable to invoke the next middleware or route handler.

        Returns:
            Response: The HTTP response returned by the next middleware or route handler.

        Raises:
            Exception: Propagates any exceptions raised by downstream handlers after logging them.

        """
        start_time = time.time()
        client_host = request.client.host if request.client else "unknown"

        # logger.debug(f"Request headers: {request.headers}")
        # logger.debug(f"Request cookies: {request.cookies}")

        # Exclude sensitive headers from logging
        safe_headers = {
            k: v for k, v in request.headers.items() if k.lower() not in {"authorization", "cookie"}
        }

        logger.info(
            f"Incoming request: {request.method} {request.url} from {client_host} "
            f"headers={safe_headers}"
        )

        try:
            response = await call_next(request)
        except Exception:
            duration = (time.time() - start_time) * 1000
            logger.exception(
                f"Request failed: {request.method} {request.url} from {client_host} "
                f"duration={duration:.2f}ms"
            )
            raise

        duration = (time.time() - start_time) * 1000
        logger.info(
            f"Completed request: {request.method} {request.url} from {client_host} "
            f"status_code={response.status_code} duration={duration:.2f}ms"
        )
        return response