Python自定义Logging子类无法正确获取实例的问题排查
问题原因与解决方案
问题根源
- 你直接手动实例化
CustomLogger的方式,不会将该实例注册到logging模块内部维护的logger注册表中。logging模块的logging.getLogger(name)方法只会从自身的内部字典中查找对应名称的logger实例,你手动创建的实例不在这个字典里。 - 后续调用
logging.setLoggerClass(CustomLogger)后,getLogger会创建一个全新的CustomLogger实例并注册到注册表,和你之前手动创建的完全是两个独立对象,自然没有之前添加的handler和extra属性。
正确实现方式
要让getLogger能获取到同一个配置好的实例,必须遵循logging模块的规范:先设置自定义Logger类,再通过getLogger创建/获取实例,同时修复代码中的可变默认参数坑。
修正后的代码
import logging from logging import Formatter class CustomLogger(logging.Logger): def __init__(self, name, extra=None): super().__init__(name) # 修复可变默认参数的坑:避免多个实例共享同一个字典 self.extra = extra if extra is not None else {} def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False, **kwargs): # 安全合并extra:传入的extra优先,再合并实例自带的extra combined_extra = {} if extra is not None: combined_extra.update(extra) combined_extra.update(self.extra) super()._log(level, msg, args, exc_info, combined_extra, stack_info, **kwargs) # 先设置自定义Logger类,再通过getLogger获取实例 logging.setLoggerClass(CustomLogger) logger = logging.getLogger("test-logger") # 给实例绑定extra属性 logger.extra = {"instance_name": "name", "instance_id": 12} # 配置handler和formatter console_handler = logging.StreamHandler() formatter = Formatter( "%(asctime)s - %(levelname)s - instance name: %(instance_name)s - instance id: %(instance_id)i - %(message)s" ) console_handler.setFormatter(formatter) logger.addHandler(console_handler)
验证效果
logger.info("Hello world") # 输出:2024-xx-xx xx:xx:xx,xxx - INFO - instance name: name - instance id: 12 - Hello world # 再次获取同一个实例 retrieved_logger = logging.getLogger("test-logger") print(logger is retrieved_logger) # 输出:True retrieved_logger.info("Hello again") # 正常输出日志 print(retrieved_logger.handlers) # 输出包含之前添加的StreamHandler
关键细节补充
- 可变默认参数
extra={}的问题:Python中函数默认参数在定义时就会创建,多个实例会共享同一个字典,导致意外的属性污染,用extra=None然后在内部初始化是正确做法。 - 合并extra时,先复制传入的extra(如果存在)再合并实例的extra,避免修改外部传入的可变字典对象。
内容的提问来源于stack exchange,提问作者mrbolichi
相关产品推荐
相关产品推荐

