Files
2026-03-13 00:39:46 +08:00

61 lines
2.0 KiB
Python

from __future__ import annotations
import time
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from app.core.logging import get_logger, set_request_id
logger = get_logger(__name__)
class RequestLoggingMiddleware(BaseHTTPMiddleware):
"""请求日志中间件,记录请求追踪信息"""
async def dispatch(self, request: Request, call_next) -> Response:
# 设置请求追踪 ID(优先使用客户端传入的)
client_request_id = request.headers.get("X-Request-ID")
request_id = set_request_id(client_request_id)
start_time = time.time()
method = request.method
path = request.url.path
try:
response = await call_next(request)
duration_ms = (time.time() - start_time) * 1000
status_code = response.status_code
# 根据状态码选择日志级别
if status_code >= 500:
logger.error(
"%s %s -> %d (%.1fms)",
method, path, status_code, duration_ms
)
elif status_code >= 400:
logger.warning(
"%s %s -> %d (%.1fms)",
method, path, status_code, duration_ms
)
else:
# 跳过健康检查和静态资源的成功日志
if not (path == "/health" or path.startswith("/assets")):
logger.info(
"%s %s -> %d (%.1fms)",
method, path, status_code, duration_ms
)
# 在响应头中返回请求 ID
response.headers["X-Request-ID"] = request_id
return response
except Exception as e:
duration_ms = (time.time() - start_time) * 1000
logger.exception(
"%s %s -> 500 (%.1fms) error: %s",
method, path, duration_ms, str(e)
)
raise