You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 19:54:05