Django默认日志配置不分离错误日志,因handler的level仅设接收门槛,ERROR日志会被INFO级handler一并接收;需配合自定义Filter实现排他性分流,如error.log仅写入levelno==ERROR(或>=ERROR)的日志,且root logger level须设为INFO或更低并显式挂载对应handlers。

为什么Django的默认日志配置不分离错误日志
Django 默认把 ERROR 和 WARNING 都发给 console 或 file handler,但没按级别切分文件——比如你想要 error.log 只存 ERROR 及以上,info.log 存 INFO,就必须手动控制 handler 的过滤逻辑。关键在 level 只控制“接收门槛”,真正决定“写不写入”得靠 filters 或自定义 logging.Filter。
用 logging.Filter 实现 ERROR 独占文件
直接靠 level='ERROR' 不够:一个 FileHandler 设为 level='ERROR',它确实只处理 ERROR 及以上日志,但如果你同时有另一个 FileHandler 设为 level='INFO',它也会收到 ERROR 日志(因为 ERROR >= INFO),导致重复写入。必须加过滤器做“排他性分流”。
实操建议:
- 定义两个
FileHandler:一个配error.log,一个配info.log - 给
error.loghandler 加level='ERROR'+ 自定义Filter,只放行record.levelno == logging.ERROR(注意:不是>=) - 给
info.loghandler 加level='INFO'+ 另一个Filter,放行record.levelno (即INFO/WARNING) - 避免用
django.utils.log.RequireDebugFalse这类内置 filter,它不区分ERROR和CRITICAL,且无法排除WARNING
示例片段(放入 LOGGING 字典的 'filters' 和 'handlers'):
'filters': {
'only_error': {
'()': 'myproject.utils.OnlyErrorFilter',
},
'exclude_error': {
'()': 'myproject.utils.ExcludeErrorFilter',
},
},
'handlers': {
'error_file': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': '/var/log/django/error.log',
'filters': ['only_error'],
'formatter': 'verbose',
},
'info_file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/django/info.log',
'filters': ['exclude_error'],
'formatter': 'simple',
},
},
自定义 Filter 类怎么写才不踩坑
必须继承 logging.Filter 并重写 filter(self, record) 方法,返回 True 表示接受,False 拒绝。常见错误是误用 record.levelname 字符串比较(如 record.levelname == 'ERROR'),应优先用 record.levelno 数值比较,更可靠。
实操建议:
-
OnlyErrorFilter返回record.levelno == logging.ERROR,不要写成>= logging.ERROR,否则CRITICAL也会进error.log - 如果想把
CRITICAL也归入错误文件,就用record.levelno >= logging.ERROR,但需同步调整exclude_error的逻辑,否则CRITICAL会两头不落 - filter 类必须可被字符串路径导入,比如放在
myproject/utils.py,且该模块能被 Django 正常 import(检查__init__.py和 INSTALLED_APPS 路径) - 别在 filter 里 print 或 raise 异常,会导致整个日志链路中断
DEBUG=False 时 error.log 为空?检查 root logger 配置
即使 handler 配对了,如果 LOGGING['loggers']['']['handlers'] 没包含 error_file,或者 root logger 的 level 设太高(如 'WARNING'),ERROR 日志根本不会进入 handler 流程。
实操建议:
- 确保
'loggers': {'': {'handlers': ['error_file', 'info_file'], 'level': 'INFO', 'propagate': False}}——level: 'INFO'是底线,低于它连ERROR都收不到 - 不要依赖
django.request或django.security的独立 logger 来捕获 500 错误:它们默认只输出到 console,除非显式把error_file加进它们的handlers列表 - 线上环境务必设
DEBUG=False并验证:手动触发一个raise ValueError('test'),看error.log是否立即追加一行,而不是等 Web server 重启才刷盘(确认FileHandler没设delay=True)
最易忽略的是:Django 的 500.html 错误页面本身不触发 logger.error(),它只走 django.request 的 ERROR 日志;而未捕获异常(如 view 中裸 raise)才会走 root logger。两者 handler 配置要分开检查。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











