Django中间件必须实现__call__方法或继承BaseMiddleware,否则日志不输出;需调用self.get_response(request)避免中断请求链;必须使用logging而非print,并在settings中配置对应logger;位置应紧邻CommonMiddleware之后、AuthenticationMiddleware之前。

Middleware 类必须继承 BaseMiddleware 或实现 __call__ 方法
Django 4.1+ 推荐用函数式中间件或继承 BaseMiddleware,旧版的 process_request 等方法在 ASGI 模式下可能不触发。直接写类但没实现 __call__,日志就完全不会打出来。
正确做法是让中间件类可调用,并在内部调用 self.get_response(request):
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
<pre class="brush:python;toolbar:false;">def __call__(self, request):
# 记录请求开始时间、路径、方法
import time
request._log_start_time = time.time()
response = self.get_response(request)
# 记录耗时、状态码等
duration = time.time() - request._log_start_time
print(f"[{request.method}] {request.path} → {response.status_code} ({duration:.3f}s)")
return response
- 别漏掉
self.get_response(request)调用,否则请求链中断,返回 500 -
request对象是线程/协程局部的,可以安全加临时属性(如_log_start_time) - 若用
BaseMiddleware,需显式调用super().__init__(get_response),且仍要重写__call__
日志写入不能只靠 print,得用 logging 配合 settings.LOGGING
print 在生产环境会被丢弃,Gunicorn/Uvicorn 不捕获 stdout;必须走 Python 标准 logging,并确保 Django 的 LOGGING 配置已启用对应 handler。
推荐在中间件里获取命名 logger:
import logging
logger = logging.getLogger('django.request.log')
<p>class RequestLoggingMiddleware:
def <strong>init</strong>(self, get_response):
self.get_response = get_response</p><pre class="brush:python;toolbar:false;">def __call__(self, request):
response = self.get_response(request)
logger.info(
'%s %s %d %0.3f',
request.method,
request.path,
response.status_code,
getattr(request, '_log_start_time', 0),
extra={'request': request, 'response': response}
)
return response
- 在
settings.py中必须定义名为'django.request.log'的 logger,否则日志静默丢失 - 避免在
extra里传复杂对象(如request.POST),可能触发懒加载或报错 - 敏感字段(如
Authorizationheader、password参数)需主动过滤,不要直接打全量request.META
中间件顺序错位会导致日志缺失或重复
比如把日志中间件放在 SecurityMiddleware 之后,但请求被其拦截(如 HTTPS 重定向),你就收不到原始请求日志;若放在 AuthenticationMiddleware 之前,request.user 就是 AnonymousUser,无法记录真实用户 ID。
- 推荐位置:紧挨着
CommonMiddleware之后、AuthenticationMiddleware之前 - 如果依赖
request.user,必须放AuthenticationMiddleware之后,但要注意 401/403 响应可能绕过它 - 调试时可在
settings.MIDDLEWARE列表中临时插入print(repr(mw))查看加载顺序
异步视图和流式响应(如 StreamingHttpResponse)会让耗时统计失真
中间件的 __call__ 在响应体真正生成前就返回了,对 StreamingHttpResponse 或异步视图,time.time() - start 只反映到响应头发出的时间,不包括流传输耗时。
- 无法准确统计流式响应总耗时,只能记录“首字节时间”
- 异步视图需用
async def __call__+await self.get_response(request),否则会阻塞事件循环 - 若需完整耗时,得在视图内手动埋点,或改用 APM 工具(如 Sentry Performance)
Django 中间件日志看似简单,但真实请求链里有重定向、认证跳转、流响应、ASGI 并发多个变量,光靠一个 call 拦截很难覆盖所有情况。最常被忽略的是:没配 LOGGING 导致日志消失,以及把中间件放错位置后死活看不到输出。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











