Python 3.12 Logging 核心:LogRecord 机制解析与定制实战
LogRecord 是 Python logging 模块中代表一条日志事件的数据容器。简单来说,它就像一张'记录单',每当程序调用日志方法(如 logger.info())时,logging 会自动创建一个 LogRecord 对象,并把所有相关信息都装进去,包括日志消息内容、级别、时间戳、源代码位置、线程信息以及异常详情等。这个对象随后会被传递给处理器(Handler)和格式化器(Formatter),最终转换成我们看到的文本。
LogRecord 的核心机制
LogRecord 封装了日志事件的所有细节。每次记录事件时,系统都会生成一个实例。主要信息位于 msg 和 args 中,通过 % 运算符组合形成最终的消息字段,实际格式化在 getMessage() 方法中完成。
动态属性管理
LogRecord 通过 __init__ 动态存储字段,例如 msg、levelname 等。值得注意的是,像 message 这样的属性支持延迟格式化——只有在访问时才会调用 getMessage() 解析 msg 与 args。默认情况下,它包含了一系列标准字段,如 name、pathname、lineno、exc_info 等。
工厂模式创建
除了常规流程,我们还可以利用 logging.makeLogRecord(dict) 工厂方法手动创建实例,这在测试或特殊场景下很有用:
record_dict = {
"name": "demo",
"level": 20,
"pathname": "/app/main.py",
"msg": "User %s logged in",
"args": ("Alice",)
}
record = logging.makeLogRecord(record_dict)
关键方法解析
getMessage() 负责合并 msg 与 args 生成最终日志消息。如果存在 args,则使用 % 运算符进行格式化;当 args 为空元组、空字典或 None 时,直接返回消息字符串。该方法通常在 Formatter 中被调用。
def getMessage(self):
"""Return the message for this LogRecord."""
msg = str(self.msg)
if self.args:
msg = msg % self.args
return msg
生命周期与实战定制
LogRecord 的生命周期通常分为三步:由 Logger.log() 调用 Logger.makeRecord() 生成,经过 Filter 和 Formatter 处理,最后通过 Handler 写入目标(文件、网络等)。
在实际开发中,我们经常需要扩展 LogRecord 来添加业务字段。下面是一个完整的示例,演示如何自定义 LogRecord 类并注入工厂函数。
完整代码示例
这段代码展示了如何继承 LogRecord 添加自定义字段(如唯一 ID),并通过过滤器为未指定字段的记录补全默认值。
import logging
import logging.config
import time
class CustomLogRecord(logging.LogRecord):
"""扩展 LogRecord 以添加自定义字段"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 添加唯一 ID,模拟业务追踪
self.custom_id = hash(time.time())
def custom_record_factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs):
"""自定义 LogRecord 工厂函数"""
record = CustomLogRecord(name, level, fn, lno, msg, args, exc_info, func, sinfo)
return record
# 定义一个过滤器,为记录添加默认的 custom_field
class DefaultCustomFieldFilter(logging.Filter):
def __init__(self, default_value="d"):
super().__init__()
self.default_value = default_value
def filter(self, record):
if not hasattr(record, 'custom_field'):
record.custom_field = self.default_value
return True
# 配置日志系统
logging_config = {
"version": 1,
"formatters": {
"detailed": {
"format": "[%(asctime)s][%(levelname)s][%(name)s]-[ID=%(custom_id)s]-%(message)s # custom:%(custom_field)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "detailed",
"level": "DEBUG",
"filters": ["default_custom_field"]
}
},
"loggers": {
"demo": {
"handlers": ["console"],
"level": "DEBUG"
}
},
"filters": {
"default_custom_field": {
"()": DefaultCustomFieldFilter,
"default_value": ""
}
}
}
def main():
# 应用配置并设置自定义工厂
logging.config.dictConfig(logging_config)
logging.setLogRecordFactory(custom_record_factory)
logger = logging.getLogger("demo")
# 标准日志记录
logger.info("Service started")
# 添加自定义字段
logger.debug("Debug with extra", extra={"custom_field": "debug_value"})
# 异常处理示例
try:
1 / 0
except Exception as e:
logger.error("Math error occurred", exc_info=True)
if __name__ == "__main__":
main()
运行上述代码后,控制台会输出带有自定义 ID 和字段的日志格式。你可以看到 ID 字段来自 CustomLogRecord 的初始化逻辑,而 custom_field 则由过滤器补充。
性能优化建议
在编写日志逻辑时,注意避免冗余计算。特别是在 Filter 中,应尽早过滤掉低级别的日志请求,减少后续处理器的开销。
总结
LogRecord 作为日志系统的核心载体,通过灵活的属性和工厂机制支持深度定制。结合 Python 3.12 的优化特性,可以构建高性能、可扩展的日志架构。实际开发中,善用 extra 参数和自定义工厂是实现业务级日志增强的关键。


