mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2025-08-26 04:33:09 +08:00
Optimize exception info opera log record (#413)
This commit is contained in:
@ -47,7 +47,7 @@ class LoginLogService:
|
||||
)
|
||||
await login_log_dao.create(db, obj_in)
|
||||
except Exception as e:
|
||||
log.exception(f'登录日志创建失败: {e}')
|
||||
log.error(f'登录日志创建失败: {e}')
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
|
@ -9,7 +9,6 @@ from starlette.middleware.cors import CORSMiddleware
|
||||
from uvicorn.protocols.http.h11_impl import STATUS_PHRASES
|
||||
|
||||
from backend.common.exception.errors import BaseExceptionMixin
|
||||
from backend.common.log import log
|
||||
from backend.common.response.response_code import CustomResponseCode, StandardResponseCode
|
||||
from backend.common.response.response_schema import response_base
|
||||
from backend.common.schema import (
|
||||
@ -78,9 +77,9 @@ async def _validation_exception_handler(request: Request, e: RequestValidationEr
|
||||
'code': StandardResponseCode.HTTP_422,
|
||||
'msg': msg,
|
||||
'data': data,
|
||||
'trace_id': get_request_trace_id(request),
|
||||
}
|
||||
request.state.__request_validation_exception__ = content # 用于在中间件中获取异常信息
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(status_code=422, content=content)
|
||||
|
||||
|
||||
@ -103,10 +102,11 @@ def register_exception(app: FastAPI):
|
||||
else:
|
||||
res = response_base.fail(res=CustomResponseCode.HTTP_400)
|
||||
content = res.model_dump()
|
||||
request.state.__request_http_exception__ = content # 用于在中间件中获取异常信息
|
||||
request.state.__request_http_exception__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=_get_exception_code(exc.status_code),
|
||||
content=content.update(trace_id=get_request_trace_id(request)),
|
||||
content=content,
|
||||
headers=exc.headers,
|
||||
)
|
||||
|
||||
@ -141,14 +141,16 @@ def register_exception(app: FastAPI):
|
||||
:param exc:
|
||||
:return:
|
||||
"""
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=StandardResponseCode.HTTP_500,
|
||||
content={
|
||||
content = {
|
||||
'code': StandardResponseCode.HTTP_500,
|
||||
'msg': CUSTOM_USAGE_ERROR_MESSAGES.get(exc.code),
|
||||
'data': None,
|
||||
'trace_id': get_request_trace_id(request),
|
||||
},
|
||||
}
|
||||
request.state.__request_pydantic_user_error__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=StandardResponseCode.HTTP_500,
|
||||
content=content,
|
||||
)
|
||||
|
||||
@app.exception_handler(AssertionError)
|
||||
@ -169,44 +171,44 @@ def register_exception(app: FastAPI):
|
||||
else:
|
||||
res = response_base.fail(res=CustomResponseCode.HTTP_500)
|
||||
content = res.model_dump()
|
||||
request.state.__request_assertion_error__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=StandardResponseCode.HTTP_500,
|
||||
content=content.update(trace_id=get_request_trace_id(request)),
|
||||
content=content,
|
||||
)
|
||||
|
||||
@app.exception_handler(BaseExceptionMixin)
|
||||
async def custom_exception_handler(request: Request, exc: BaseExceptionMixin):
|
||||
"""
|
||||
全局异常处理
|
||||
全局自定义异常处理
|
||||
|
||||
:param request:
|
||||
:param exc:
|
||||
:return:
|
||||
"""
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=_get_exception_code(exc.code),
|
||||
content={
|
||||
content = {
|
||||
'code': exc.code,
|
||||
'msg': str(exc.msg),
|
||||
'data': exc.data if exc.data else None,
|
||||
'trace_id': get_request_trace_id(request),
|
||||
},
|
||||
}
|
||||
request.state.__request_custom_exception__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=_get_exception_code(exc.code),
|
||||
content=content,
|
||||
background=exc.background,
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def all_exception_handler(request: Request, exc: Exception):
|
||||
async def all_unknown_exception_handler(request: Request, exc: Exception):
|
||||
"""
|
||||
全局异常处理
|
||||
全局未知异常处理
|
||||
|
||||
:param request:
|
||||
:param exc:
|
||||
:return:
|
||||
"""
|
||||
import traceback
|
||||
|
||||
log.error(f'未知异常: {exc}')
|
||||
log.error(traceback.format_exc())
|
||||
if settings.ENVIRONMENT == 'dev':
|
||||
content = {
|
||||
'code': StandardResponseCode.HTTP_500,
|
||||
@ -216,9 +218,11 @@ def register_exception(app: FastAPI):
|
||||
else:
|
||||
res = response_base.fail(res=CustomResponseCode.HTTP_500)
|
||||
content = res.model_dump()
|
||||
request.state.__request_all_unknown_exception__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
return MsgSpecJSONResponse(
|
||||
status_code=StandardResponseCode.HTTP_500,
|
||||
content=content.update(trace_id=get_request_trace_id(request)),
|
||||
content=content,
|
||||
)
|
||||
|
||||
if settings.MIDDLEWARE_CORS:
|
||||
@ -251,9 +255,11 @@ def register_exception(app: FastAPI):
|
||||
else:
|
||||
res = response_base.fail(res=CustomResponseCode.HTTP_500)
|
||||
content = res.model_dump()
|
||||
request.state.__request_cors_500_exception__ = content
|
||||
content.update(trace_id=get_request_trace_id(request))
|
||||
response = MsgSpecJSONResponse(
|
||||
status_code=exc.code if isinstance(exc, BaseExceptionMixin) else StandardResponseCode.HTTP_500,
|
||||
content=content.update(trace_id=get_request_trace_id(request)),
|
||||
content=content,
|
||||
background=exc.background if isinstance(exc, BaseExceptionMixin) else None,
|
||||
)
|
||||
origin = request.headers.get('origin')
|
||||
|
@ -53,7 +53,7 @@ class OperaLogMiddleware(BaseHTTPMiddleware):
|
||||
|
||||
# 执行请求
|
||||
start_time = timezone.now()
|
||||
res = await self.execute_request(request, call_next)
|
||||
request_next = await self.execute_request(request, call_next)
|
||||
end_time = timezone.now()
|
||||
cost_time = (end_time - start_time).total_seconds() * 1000.0
|
||||
|
||||
@ -77,20 +77,20 @@ class OperaLogMiddleware(BaseHTTPMiddleware):
|
||||
browser=request.state.browser,
|
||||
device=request.state.device,
|
||||
args=args,
|
||||
status=res.status,
|
||||
code=res.code,
|
||||
msg=res.msg,
|
||||
status=request_next.status,
|
||||
code=request_next.code,
|
||||
msg=request_next.msg,
|
||||
cost_time=cost_time,
|
||||
opera_time=start_time,
|
||||
)
|
||||
create_task(OperaLogService.create(obj_in=opera_log_in)) # noqa: ignore
|
||||
|
||||
# 错误抛出
|
||||
err = res.err
|
||||
err = request_next.err
|
||||
if err:
|
||||
raise err from None
|
||||
|
||||
return res.response
|
||||
return request_next.response
|
||||
|
||||
async def execute_request(self, request: Request, call_next) -> RequestCallNext:
|
||||
"""执行请求"""
|
||||
@ -101,7 +101,7 @@ class OperaLogMiddleware(BaseHTTPMiddleware):
|
||||
response = None
|
||||
try:
|
||||
response = await call_next(request)
|
||||
code, msg = self.validation_exception_handler(request, code, msg)
|
||||
code, msg = self.request_exception_handler(request, code, msg)
|
||||
except Exception as e:
|
||||
log.error(f'请求异常: {e}')
|
||||
# code 处理包含 SQLAlchemy 和 Pydantic
|
||||
@ -113,22 +113,24 @@ class OperaLogMiddleware(BaseHTTPMiddleware):
|
||||
return RequestCallNext(code=str(code), msg=msg, status=status, err=err, response=response)
|
||||
|
||||
@staticmethod
|
||||
def validation_exception_handler(request: Request, code: int, msg: str) -> tuple[str, str]:
|
||||
def request_exception_handler(request: Request, code: int, msg: str) -> tuple[str, str]:
|
||||
"""请求异常处理器"""
|
||||
try:
|
||||
http_exception = request.state.__request_http_exception__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
code = http_exception.get('code', 500)
|
||||
msg = http_exception.get('msg', 'Internal Server Error')
|
||||
try:
|
||||
validation_exception = request.state.__request_validation_exception__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
code = validation_exception.get('code', 400)
|
||||
msg = validation_exception.get('msg', 'Bad Request')
|
||||
exception_states = [
|
||||
'__request_http_exception__',
|
||||
'__request_validation_exception__',
|
||||
'__request_pydantic_user_error__',
|
||||
'__request_assertion_error__',
|
||||
'__request_custom_exception__',
|
||||
'__request_all_unknown_exception__',
|
||||
'__request_cors_500_exception__',
|
||||
]
|
||||
for state in exception_states:
|
||||
exception = getattr(request.state, state, None)
|
||||
if exception:
|
||||
code = exception.get('code')
|
||||
msg = exception.get('msg')
|
||||
break
|
||||
log.error(f'请求异常: {msg}')
|
||||
return code, msg
|
||||
|
||||
@staticmethod
|
||||
|
Reference in New Issue
Block a user