FarmerHelp / backend /app /middleware /logging_middleware.py
Gankit12's picture
All Code
b64de39
Raw
History Blame Contribute Delete
2.33 kB
"""
Request / Response Logging Middleware
Logs every HTTP request with:
- Timestamp (UTC)
- Request ID
- HTTP method and path
- Client IP
- Response status code
- Processing time in milliseconds
Errors (5xx) are logged at ERROR level; client errors (4xx) at WARNING;
everything else at INFO.
"""
import logging
import time
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response
logger = logging.getLogger("farmhelp.access")
class RequestLoggingMiddleware(BaseHTTPMiddleware):
"""Structured access logging for every request/response."""
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
start = time.perf_counter()
request_id = getattr(request.state, "request_id", "-")
client_ip = request.client.host if request.client else "unknown"
method = request.method
path = request.url.path
query = str(request.url.query) if request.url.query else ""
logger.info(
"request_start | request_id=%s method=%s path=%s query=%s client=%s",
request_id,
method,
path,
query,
client_ip,
)
try:
response = await call_next(request)
except Exception:
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
logger.exception(
"request_error | request_id=%s method=%s path=%s time_ms=%s",
request_id,
method,
path,
elapsed_ms,
)
raise
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
status_code = response.status_code
log_msg = (
"request_end | request_id=%s method=%s path=%s "
"status=%s time_ms=%s client=%s"
)
log_args = (request_id, method, path, status_code, elapsed_ms, client_ip)
if status_code >= 500:
logger.error(log_msg, *log_args)
elif status_code >= 400:
logger.warning(log_msg, *log_args)
else:
logger.info(log_msg, *log_args)
response.headers["X-Response-Time-Ms"] = str(elapsed_ms)
return response