需设propagate_exceptions=false后,@app.errorhandler(exception)才能捕获所有未处理异常;它应置于末尾,避免覆盖404等http异常处理器,并返回元组格式响应。

Flask 中如何用 errorhandler 捕获所有未处理异常
默认情况下,Flask 只对 HTTPException(如 404、500)触发 errorhandler,普通 Python 异常(比如 ValueError、KeyError)会直接崩掉请求并返回 500 页面,但不会进你写的 @app.errorhandler(Exception) —— 除非你显式启用 PROPAGATE_EXCEPTIONS = False。
实操建议:
- 在应用初始化后、启动前设置
app.config['PROPAGATE_EXCEPTIONS'] = False,否则@app.errorhandler(Exception)不生效 - 注册全局异常处理器时,优先捕获
Exception,再单独处理HTTPException,避免重复记录 404/405 等预期错误 - 别在 handler 里 raise 新异常,否则会绕过日志记录直接 500;如果要重抛,用
current_app.logger.exception("...")先留痕
示例:
app.config['PROPAGATE_EXCEPTIONS'] = False
@app.errorhandler(Exception)
def handle_unexpected_error(e):
current_app.logger.exception("Unhandled exception: %s", str(e))
return {"error": "Internal server error"}, 500
Django 项目中覆盖 process_exception 的正确姿势
Django 的中间件 process_exception 是捕获视图异常的主入口,但它只对当前中间件栈「之后」的视图和中间件抛出的异常有效。如果你把自定义异常中间件放在 MIDDLEWARE 列表太靠前(比如在 SecurityMiddleware 前),部分底层异常(如 URL 解析失败)就捕获不到。
实操建议:
- 把自定义异常中间件放在
MIDDLEWARE配置的倒数第二位,紧挨着CommonMiddleware或ExceptionHandlerMiddleware(如果有)之后 - 不要在
process_exception里返回HttpResponse同时又调用logger.exception()—— Django 默认已记录一次,重复记录会让日志冗余且掩盖原始 traceback 位置 - 若需区分开发/生产行为(比如开发时显示详细错误页),检查
settings.DEBUG,而不是依赖request.is_ajax()这类已弃用逻辑
日志格式里必须包含 request_id 和 trace_id 才算真正可追踪
单纯用 %(asctime)s %(levelname)s %(message)s 记录异常,线上出问题时根本无法关联同一请求的多条日志。Web 请求是并发的,没有唯一上下文标识,日志就只是碎片。
实操建议:
- 用
flask.g.request_id(Flask)或request.META.get('HTTP_X_REQUEST_ID')(Django)提取或生成请求 ID,并注入到LoggerAdapter或Filter中 - Python 标准库的
logging.Filter是最轻量方案:重写filter(record),往record.request_id动态赋值,然后在 format 字符串里用%(request_id)s - 避免用线程局部变量(
threading.local)存 request_id —— ASGI(如 Uvicorn)下协程切换会导致丢失;改用contextvars.ContextVar(Python 3.7+)才安全
异步视图(FastAPI / Starlette)中异常捕获失效的常见原因
FastAPI 的 @app.exception_handler() 默认只捕获同步异常。一旦你在路径函数里用了 await,而 await 的对象内部抛了异常(比如数据库连接超时),这个异常可能被 asyncio 丢弃,或者被封装成 CancelledError,导致你的 handler 完全不触发。
实操建议:
- 所有
async def路径函数的顶层 try/except 必须包住整个await块,不能只包某一行;否则异常在 await 返回前就被协程调度器吞掉了 - 注册 handler 时,明确指定异常类型:用
@app.exception_handler(StarletteHTTPException)处理 HTTP 错误,@app.exception_handler(Exception)处理其他,且确保后者在配置中排在更后面(避免被前者拦截) - Uvicorn 启动时加
--log-level warning,否则它的 access log 会刷屏,掩盖你自己的 error log;同时在LOGGING配置里把uvicorn.error的 level 设为ERROR,避免 info 级日志污染
真正难的不是配通日志,而是让每条 ERROR 日志都能反向定位到具体用户、具体请求、具体代码行——这要求从中间件、日志 filter、异步上下文、到部署侧的 Nginx request_id 透传,全部对齐。漏一环,排查成本就翻倍。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











