feat: 新增告警、成本、配额、微信模块及缓存服务
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- 新增告警模块 (alerts): 告警规则配置与触发 - 新增成本管理模块 (cost): 成本统计与分析 - 新增配额模块 (quota): 配额管理与限制 - 新增微信模块 (wechat): 微信相关功能接口 - 新增缓存服务 (cache): Redis 缓存封装 - 新增请求日志中间件 (request_logger) - 新增异常处理和链路追踪中间件 - 更新 dashboard 前端展示 - 更新 SDK stats_client 功能
This commit is contained in:
128
backend/app/middleware/exception_handler.py
Normal file
128
backend/app/middleware/exception_handler.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
统一异常处理
|
||||
|
||||
捕获所有异常,返回统一格式的错误响应,包含 TraceID。
|
||||
"""
|
||||
import logging
|
||||
import traceback
|
||||
from typing import Union
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
from .trace import get_trace_id
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ErrorCode:
|
||||
"""错误码常量"""
|
||||
BAD_REQUEST = "BAD_REQUEST"
|
||||
UNAUTHORIZED = "UNAUTHORIZED"
|
||||
FORBIDDEN = "FORBIDDEN"
|
||||
NOT_FOUND = "NOT_FOUND"
|
||||
VALIDATION_ERROR = "VALIDATION_ERROR"
|
||||
RATE_LIMITED = "RATE_LIMITED"
|
||||
INTERNAL_ERROR = "INTERNAL_ERROR"
|
||||
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE"
|
||||
GATEWAY_ERROR = "GATEWAY_ERROR"
|
||||
|
||||
|
||||
STATUS_TO_ERROR_CODE = {
|
||||
400: ErrorCode.BAD_REQUEST,
|
||||
401: ErrorCode.UNAUTHORIZED,
|
||||
403: ErrorCode.FORBIDDEN,
|
||||
404: ErrorCode.NOT_FOUND,
|
||||
422: ErrorCode.VALIDATION_ERROR,
|
||||
429: ErrorCode.RATE_LIMITED,
|
||||
500: ErrorCode.INTERNAL_ERROR,
|
||||
502: ErrorCode.GATEWAY_ERROR,
|
||||
503: ErrorCode.SERVICE_UNAVAILABLE,
|
||||
}
|
||||
|
||||
|
||||
def create_error_response(
|
||||
status_code: int,
|
||||
code: str,
|
||||
message: str,
|
||||
trace_id: str = None,
|
||||
details: dict = None
|
||||
) -> JSONResponse:
|
||||
"""创建统一格式的错误响应"""
|
||||
if trace_id is None:
|
||||
trace_id = get_trace_id()
|
||||
|
||||
error_body = {
|
||||
"code": code,
|
||||
"message": message,
|
||||
"trace_id": trace_id
|
||||
}
|
||||
|
||||
if details:
|
||||
error_body["details"] = details
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status_code,
|
||||
content={"success": False, "error": error_body},
|
||||
headers={"X-Trace-ID": trace_id}
|
||||
)
|
||||
|
||||
|
||||
async def http_exception_handler(request: Request, exc: Union[HTTPException, StarletteHTTPException]):
|
||||
"""处理 HTTP 异常"""
|
||||
trace_id = get_trace_id()
|
||||
status_code = exc.status_code
|
||||
error_code = STATUS_TO_ERROR_CODE.get(status_code, ErrorCode.INTERNAL_ERROR)
|
||||
message = exc.detail if isinstance(exc.detail, str) else str(exc.detail)
|
||||
|
||||
logger.warning(f"[{trace_id}] HTTP {status_code}: {message}")
|
||||
|
||||
return create_error_response(
|
||||
status_code=status_code,
|
||||
code=error_code,
|
||||
message=message,
|
||||
trace_id=trace_id
|
||||
)
|
||||
|
||||
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
"""处理请求验证错误"""
|
||||
trace_id = get_trace_id()
|
||||
errors = exc.errors()
|
||||
error_messages = [f"{'.'.join(str(l) for l in e['loc'])}: {e['msg']}" for e in errors]
|
||||
|
||||
logger.warning(f"[{trace_id}] 验证错误: {error_messages}")
|
||||
|
||||
return create_error_response(
|
||||
status_code=422,
|
||||
code=ErrorCode.VALIDATION_ERROR,
|
||||
message="请求参数验证失败",
|
||||
trace_id=trace_id,
|
||||
details={"validation_errors": error_messages}
|
||||
)
|
||||
|
||||
|
||||
async def generic_exception_handler(request: Request, exc: Exception):
|
||||
"""处理所有未捕获的异常"""
|
||||
trace_id = get_trace_id()
|
||||
|
||||
logger.error(f"[{trace_id}] 未捕获异常: {type(exc).__name__}: {exc}")
|
||||
logger.error(f"[{trace_id}] 堆栈:\n{traceback.format_exc()}")
|
||||
|
||||
return create_error_response(
|
||||
status_code=500,
|
||||
code=ErrorCode.INTERNAL_ERROR,
|
||||
message="服务器内部错误,请稍后重试",
|
||||
trace_id=trace_id
|
||||
)
|
||||
|
||||
|
||||
def setup_exception_handlers(app: FastAPI):
|
||||
"""配置 FastAPI 应用的异常处理器"""
|
||||
app.add_exception_handler(HTTPException, http_exception_handler)
|
||||
app.add_exception_handler(StarletteHTTPException, http_exception_handler)
|
||||
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||
app.add_exception_handler(Exception, generic_exception_handler)
|
||||
|
||||
logger.info("异常处理器已配置")
|
||||
Reference in New Issue
Block a user