File size: 2,025 Bytes
dbc1e7d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Request logging middleware.

Logs every request with: method, path, status code, duration, user ID (if authenticated).
Uses structured JSON format for production observability.
"""

import logging
import time
from uuid import uuid4

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

logger = logging.getLogger("depscreen.requests")


class RequestLoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        request_id = request.headers.get("X-Request-ID", str(uuid4())[:8])
        start_time = time.time()

        # Extract user ID from JWT if present (without full auth — just peek)
        user_id = "anonymous"
        auth_header = request.headers.get("Authorization", "")
        if auth_header.startswith("Bearer ") and len(auth_header) > 20:
            user_id = "authenticated"  # Don't decode JWT here — just note it exists

        response = await call_next(request)

        duration_ms = round((time.time() - start_time) * 1000, 1)
        status = response.status_code

        # Skip health checks and static assets from logs
        path = request.url.path
        if path in ("/", "/health", "/health/live", "/health/ready", "/favicon.ico"):
            return response

        log_data = {
            "request_id": request_id,
            "method": request.method,
            "path": path,
            "status": status,
            "duration_ms": duration_ms,
            "user": user_id,
            "ip": request.client.host if request.client else "unknown",
        }

        if status >= 500:
            logger.error(f"[{request_id}] {request.method} {path}{status} ({duration_ms}ms)", extra=log_data)
        elif status >= 400:
            logger.warning(f"[{request_id}] {request.method} {path}{status} ({duration_ms}ms)", extra=log_data)
        else:
            logger.info(f"[{request_id}] {request.method} {path}{status} ({duration_ms}ms)", extra=log_data)

        return response