| """ |
| 响应中间件 |
| Response Middleware |
| |
| 用于记录请求日志、生成 TraceID 和计算请求耗时 |
| """ |
|
|
| import time |
| import uuid |
| from starlette.middleware.base import BaseHTTPMiddleware |
| from starlette.requests import Request |
|
|
| from app.core.logger import logger |
|
|
|
|
| class ResponseLoggerMiddleware(BaseHTTPMiddleware): |
| """ |
| 请求日志/响应追踪中间件 |
| Request Logging and Response Tracking Middleware |
| """ |
|
|
| async def dispatch(self, request: Request, call_next): |
| |
| trace_id = str(uuid.uuid4()) |
| request.state.trace_id = trace_id |
|
|
| start_time = time.time() |
| path = request.url.path |
|
|
| if path.startswith("/static/") or path in ( |
| "/", |
| "/login", |
| "/imagine", |
| "/voice", |
| "/admin", |
| "/admin/login", |
| "/admin/config", |
| "/admin/cache", |
| "/admin/token", |
| ): |
| return await call_next(request) |
|
|
| |
| logger.info( |
| f"Request: {request.method} {request.url.path}", |
| extra={ |
| "traceID": trace_id, |
| "method": request.method, |
| "path": request.url.path, |
| }, |
| ) |
|
|
| try: |
| response = await call_next(request) |
|
|
| |
| duration = (time.time() - start_time) * 1000 |
|
|
| |
| logger.info( |
| f"Response: {request.method} {request.url.path} - {response.status_code} ({duration:.2f}ms)", |
| extra={ |
| "traceID": trace_id, |
| "method": request.method, |
| "path": request.url.path, |
| "status": response.status_code, |
| "duration_ms": round(duration, 2), |
| }, |
| ) |
|
|
| return response |
|
|
| except Exception as e: |
| duration = (time.time() - start_time) * 1000 |
| logger.error( |
| f"Response Error: {request.method} {request.url.path} - {str(e)} ({duration:.2f}ms)", |
| extra={ |
| "traceID": trace_id, |
| "method": request.method, |
| "path": request.url.path, |
| "duration_ms": round(duration, 2), |
| "error": str(e), |
| }, |
| ) |
| raise e |
|
|