Python Logging 自定义Formatter如何支持extra传入自定义字段
Python logging自定义JSON Formatter无法输出自定义字段的解决方案
问题根因
你的判断准确,问题出在原有CustomFormatter的formatMessage方法硬编码了固定的序列化字段列表:无论你通过重写LogRecordFactory添加全局自定义字段,还是通过extra参数传入单次日志的自定义字段,这些字段虽然已经正确挂载到LogRecord实例上,但序列化阶段没有被读取,自然不会出现在最终的JSON输出中。
改造方案
调整Formatter的序列化逻辑:先输出约定的固定基础字段,再自动遍历LogRecord实例上的所有属性,过滤掉原生内置的冗余内部属性后,把所有自定义字段自动追加到输出结构中,同时兼容「全局默认自定义字段」和「extra传参的单次自定义字段」两种场景。
改造后的Formatter代码如下:
import json import logging from typing import Optional # LogRecord原生内置属性集合,用于过滤内部冗余字段 _LOG_RECORD_BUILTIN_ATTRS = { "args", "asctime", "created", "exc_info", "exc_text", "filename", "funcName", "levelname", "levelno", "lineno", "module", "msecs", "message", "msg", "name", "pathname", "process", "processName", "relativeCreated", "stack_info", "thread", "threadName", } class CustomFormatter(logging.Formatter): def __init__( self, fmt: Optional[str] = "%(asctime)s", datefmt: Optional[str] = None, style: Optional[str] = "%", confidentiality: Optional[str] = "C3", ) -> None: self.confidentiality = confidentiality super().__init__(fmt=fmt, datefmt=datefmt, style=style) def formatMessage(self, record: logging.LogRecord, *args, **kwargs) -> str: super().formatMessage(record) # 组装固定要求的基础日志字段 log_payload = { "asctime": record.asctime, "level": record.levelname, "name": record.name, "message": record.message, "timeMillis": int(record.created * 1000), "pathName": record.pathname, "funcName": record.funcName, "lineNumber": record.lineno, "confidentiality": self.confidentiality, } # 自动追加所有自定义字段 for attr, value in record.__dict__.items(): if attr not in _LOG_RECORD_BUILTIN_ATTRS and attr not in log_payload: log_payload[attr] = value return json.dumps(log_payload, ensure_ascii=False)
使用说明
- 原有通过重写LogRecordFactory添加全局自定义字段的逻辑无需修改,改造后全局字段会自动输出。
- 原生
extra传参的调用方式完全兼容,示例:
logger.info("test", extra={"custom_attribute": "my-attr", "user_id": 10086})
运行后输出的JSON会自动追加传入的自定义字段,不需要提前在Formatter中做字段声明。
注意事项
- 序列化时加了
ensure_ascii=False参数,避免中文等非ASCII字符被转义为Unicode编码,提升日志可读性,如有强制ASCII输出要求可删除该参数。 - 内置属性过滤逻辑已经排除了LogRecord的所有内部冗余字段,不会将异常对象、线程锁这类不可序列化的内部属性输出到日志中。如果需要输出异常栈信息,可以单独在
log_payload中添加"exc_text": record.exc_text映射即可。
内容的提问来源于stack exchange,提问作者Data Mastery
相关产品推荐
相关产品推荐

