Django中间件的process_exception仅捕获视图未处理异常,按注册倒序执行,需配合脱敏日志、正确LOGGING配置及ASGI下改用async_process_exception。

中间件里用 process_exception 捕获异常就行
Django 中间件的 process_exception 方法专为异常兜底设计,它只在视图函数抛出未捕获异常时被调用,且仅对该请求生命周期内生效。这不是“全局钩子”,而是按请求链路触发的、有明确作用域的回调。
常见误区是把它当成类似 Flask 的 @app.errorhandler 或系统级 panic 捕获器——它不处理 404、403 这类 HTTP 状态码异常(这些走的是正常响应流程),也不捕获中间件自身抛出的异常(比如你在 process_request 里写错逻辑崩了,process_exception 不会触发)。
实操建议:
-
process_exception必须定义在中间件类中,且方法签名固定:def process_exception(self, request, exception): - 它只对当前中间件之后、视图之前抛出的异常生效;如果多个中间件都实现了该方法,Django 按注册顺序倒序调用(即离视图最近的那个最先执行)
- 不要在该方法里
raise新异常,否则可能掩盖原始错误,或导致二次 500 - 推荐只做日志记录、报警通知等副作用操作,避免修改
request或尝试“修复”异常
日志记录要带 request 上下文才实用
光记 exception.__class__.__name__ 和 str(exception) 没用,线上排查时你根本不知道这个异常发生在哪个 URL、哪个用户、什么时间、带了什么参数。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 用
logging.getLogger(__name__)获取 logger,别用logging.basicConfig,否则和 Django 自带日志配置冲突 - 务必提取
request.path、request.method、getattr(request, 'user', None)(注意匿名用户场景)、request.META.get('HTTP_USER_AGENT')等字段 - 敏感字段如
request.POST或request.GET要脱敏,避免日志泄露密码、token —— 可用{k: '***' if 'password' in k.lower() else v for k, v in dict(request.POST).items()}简单过滤 - 用
logger.error(..., exc_info=True),这样会自动附加 traceback,比手动traceback.format_exc()更可靠
别忘了配置 Django 的 LOGGING 字典
即使中间件写了日志,如果 LOGGING 配置没把对应 logger 名称包含进去,或者 handler 写错了路径,日志照样不会落地。
常见错误现象:process_exception 执行了,但文件里没日志、控制台也没输出、django.request 的日志能打出来但你的中间件日志没了。
实操建议:
- 在
settings.py的LOGGING字典里,显式添加你的中间件 logger 名称(比如'myapp.middleware'),并确保其level是'ERROR'或更低 - handler 推荐用
'rotating'类型('class': 'logging.handlers.RotatingFileHandler'),避免单个日志文件无限增长 - 检查
LOGGING['loggers']下是否设置了'propagate': False—— 如果设了,父 logger(如 root)就不会再处理这条日志,要确认你配的 handler 是否真能覆盖到 - 开发时可临时加一个
consolehandler 并设'level': 'DEBUG',快速验证日志是否发出
异步视图和 ASGI 场景下 process_exception 不生效
Django 4.1+ 支持异步视图(async def my_view),但标准中间件是同步的。process_exception 在 ASGI 模式下不会被调用,因为异常发生在 async context 中,而同步中间件无法拦截。
这时候必须改用异步中间件(继承 AsyncMiddlewareMixin)并实现 async_process_exception 方法,且整个调用链必须保持 async —— 包括你用的 logger 也要支持异步写入(标准 logging 不行,得换 aiologger 或用线程池包装)。
容易被忽略的地方:本地调试用 runserver(WSGI)没问题,一上生产用 uvicorn(ASGI)就丢日志,却以为是配置问题。先确认 ASGI_APPLICATION 是否启用,再决定中间件类型。


















