Python logging日志重复问题:自定义日志类多实例导致重复记录
这是Python logging模块里非常常见的一个坑——全局Logger实例被重复添加Handler导致的日志重复输出,我来给你拆解清楚:
为什么会出现重复日志?
Python的logging模块中,通过logging.getLogger(name)获取的Logger实例是全局共享的——只要name参数相同,每次调用都会返回同一个对象。
假设你的MyLog类是这样写的(这是最容易踩坑的写法):
import logging class MyLog: def __init__(self): # 获取全局共享的Logger实例 self.logger = logging.getLogger("my_custom_logger") self.logger.setLevel(logging.DEBUG) # 每次实例化都添加控制台处理器 stream_handler = logging.StreamHandler() self.logger.addHandler(stream_handler) # 每次实例化都添加文件处理器 file_handler = logging.FileHandler("app.log") self.logger.addHandler(file_handler) # 省略格式器配置...
那么:
- 当
TestA实例化MyLog时,这个Logger会被添加2个Handler(控制台+文件),此时调用日志方法会正常输出1条记录; - 当
TestB实例化MyLog时,又会给同一个Logger再添加2个Handler,此时每条日志会被2组Handler分别处理,所以你看到2条重复记录; - 到
TestC实例化时,Logger累计了3组Handler,每条日志会被处理3次,自然就出现3条重复。
简单说:每多实例化一次MyLog,就给全局Logger多塞一组Handler,后续日志会被所有已添加的Handler重复输出。
怎么解决这个问题?
我给你三个靠谱的解决方案,按推荐优先级排序:
方案1:添加Handler前先检查,避免重复添加
在初始化Logger时,先判断是否已经存在对应类型的Handler,只有不存在时才添加:
import logging class MyLog: def __init__(self): self.logger = logging.getLogger("my_custom_logger") self.logger.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 检查是否已有控制台Handler,没有才添加 has_stream_handler = any(isinstance(hdlr, logging.StreamHandler) for hdlr in self.logger.handlers) if not has_stream_handler: stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) self.logger.addHandler(stream_handler) # 检查是否已有文件Handler,没有才添加 has_file_handler = any(isinstance(hdlr, logging.FileHandler) for hdlr in self.logger.handlers) if not has_file_handler: file_handler = logging.FileHandler("app.log") file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) # 日志方法示例 def debug(self, msg): self.logger.debug(msg) def info(self, msg): self.logger.info(msg)
方案2:用单例模式确保MyLog只实例化一次
让MyLog类成为单例,整个程序生命周期中只有一个实例,这样Handler只会被添加一次:
import logging class MyLog: _instance = None # 保存单例实例 def __new__(cls, *args, **kwargs): if not cls._instance: # 第一次实例化时才初始化Logger和Handler cls._instance = super().__new__(cls, *args, **kwargs) cls._instance.logger = logging.getLogger("my_custom_logger") cls._instance.logger.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) cls._instance.logger.addHandler(stream_handler) file_handler = logging.FileHandler("app.log") file_handler.setFormatter(formatter) cls._instance.logger.addHandler(file_handler) return cls._instance # 日志方法示例 def debug(self, msg): self.logger.debug(msg)
方案3:初始化前清空已有Handler(谨慎使用)
如果确定这个Logger只会被MyLog使用,可以在初始化时先清空所有Handler,再重新添加:
import logging class MyLog: def __init__(self): self.logger = logging.getLogger("my_custom_logger") self.logger.setLevel(logging.DEBUG) # 先清空所有已有Handler self.logger.handlers.clear() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) self.logger.addHandler(stream_handler) file_handler = logging.FileHandler("app.log") file_handler.setFormatter(formatter) self.logger.addHandler(file_handler)
⚠️ 注意:这个方案会清除该Logger的所有Handler,如果其他代码也在使用同一个Logger,会导致它们的日志配置失效,所以只适合完全由MyLog管控Logger的场景。
怎么验证问题是否解决?
你可以在MyLog的__init__方法里加一行打印,看看每次实例化后Logger的Handler数量:
print(f"当前Logger的Handler数量:{len(self.logger.handlers)}")
如果每次实例化后数量不再增加,就说明问题已经解决了。
内容的提问来源于stack exchange,提问作者Mark4Later
相关产品推荐
相关产品推荐

