Django日志KeyError异常:自定义extra字段无法匹配日志格式
解决Django日志自定义字段触发KeyError的问题
你的问题核心很明确:自定义日志格式里用了%(classname)s、%(token)s这类自定义字段,但并非所有日志调用都会携带这些字段的上下文——比如你报错栈里提到的Django内置HTTP服务器日志(basehttp.py中的log_message),它只会传递默认的日志记录字段,没有你定义的这些自定义字段,所以格式化这类日志时就会触发KeyError。
下面是两种可行的解决方案:
方案1:给自定义字段设置默认值(推荐)
通过自定义Formatter类,在格式化日志前自动给缺失的自定义字段补上默认值,这样不管是你主动添加extra参数的业务日志,还是Django内置的无自定义字段的系统日志,都能正常输出。
步骤1:创建自定义Formatter
在项目中新建utils/logging.py(或其他你习惯的路径),写入以下代码:
import logging class CustomFormatter(logging.Formatter): def formatMessage(self, record): # 为每个自定义字段设置默认值,不存在则用'NA'填充 record.classname = getattr(record, 'classname', 'NA') record.token = getattr(record, 'token', 'NA') record.router_macid = getattr(record, 'router_macid', 'NA') record.user_email = getattr(record, 'user_email', 'NA') record.device_macid = getattr(record, 'device_macid', 'NA') # 调用父类的格式化方法完成后续处理 return super().formatMessage(record)
步骤2:修改settings.py的LOGGING配置
把原来的formatter替换成自定义的Formatter:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'formatter': { # 指定自定义Formatter的路径,替换成你实际的项目路径 '()': 'your_project.utils.logging.CustomFormatter', 'format' : '[%(asctime)s] %(levelname)s [%(pathname)s:%(lineno)s:%(classname)s()] %(token)s %(router_macid)s %(user_email)s %(device_macid)s %(message)s', 'datefmt' : "%d/%b/%Y %H:%M:%S" }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, 'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': 'my_django.log', 'formatter': 'formatter' }, }, 'loggers': { 'django': { 'handlers':['file'], 'propagate': True, 'level':'DEBUG', }, 'backend': { 'handlers': ['file'], 'level': 'DEBUG', 'propagate': True, }, } }
方案2:分离自定义日志和Django默认日志
如果你不想修改Formatter,可以把业务日志(backend logger)和Django内置系统日志(django logger)分开,给它们分配不同的handler和日志格式:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'custom_formatter': { 'format' : '[%(asctime)s] %(levelname)s [%(pathname)s:%(lineno)s:%(classname)s()] %(token)s %(router_macid)s %(user_email)s %(device_macid)s %(message)s', 'datefmt' : "%d/%b/%Y %H:%M:%S" }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, 'handlers': { 'custom_file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': 'backend_custom.log', 'formatter': 'custom_formatter' }, 'django_file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': 'django_default.log', 'formatter': 'simple' }, }, 'loggers': { 'django': { 'handlers':['django_file'], 'propagate': True, 'level':'DEBUG', }, 'backend': { 'handlers': ['custom_file'], 'level': 'DEBUG', 'propagate': False, # 关闭传播,避免日志被django logger重复处理 }, } }
额外注意点
- 移除
views.py里的logging.basicConfig(level=logging.INFO),因为Django的LOGGING配置会覆盖它,而且可能导致日志层级冲突。 - 确保你的业务日志调用都正确传入了
extra参数(像你在MyView.post里做的那样),这样自定义字段会显示你设置的值,否则会用Formatter里的默认值。
内容的提问来源于stack exchange,提问作者Niraj Panchasara
相关产品推荐
相关产品推荐

