Python logging模块重复记录日志问题的原因与解决方案

今天早睡
今天早睡
发布时间:2026-10-13 | 4人浏览过
原创

本文深入解析Python logging模块中常见的重复记录日志问题。通过示例代码展示因未移除Handler导致的日志重复现象,并提供四种有效解决方案,包括移除Handler、判断已有Handler及优雅的处理方式,帮助开发者快速定位并修复日志异常。

日志相关概念

日志是追踪软件运行时事件的重要手段。开发人员可通过调用日志记录方法来标记特定事件的发生,这些事件通常包含可选的变量数据,并具备表示重要性的严重性级别(level)。

日志的作用

通过分析日志,用户可以掌握系统、软件或应用的运行状态;若日志内容足够丰富,还能进一步分析用户的历史操作行为、偏好类型、地域分布等更多维度信息。若应用日志划分了多个级别,则可轻松评估应用健康状况,及时发现并快速定位、解决问题,从而减少损失。

简而言之,记录和分析日志有助于判断系统或软件程序运行是否正常,并在应用出现故障时协助快速定位问题。例如,运维人员在接到报警或反馈后,通常首先查看各类日志以排查问题,大多数情况可在日志中找到答案。开发人员则可通过IDE控制台输出的日志进行调试。经验丰富的运维人员或开发者能迅速通过日志定位根源,可见日志的重要性不容忽视。其作用可总结为以下三点:

  • 程序调试
  • 监控软件程序运行状态是否正常
  • 软件程序运行故障分析与问题定位

若应用日志信息足够详细丰富,还可用于用户行为分析,如分析用户操作行为、类型喜好、地域分布及其他更多信息,进而优化业务、提升商业价值。

发现问题

近期在使用Python的logging模块记录日志时,遇到了重复记录的问题:第一条记录写一次,第二条写两次,第三条写三次……这显然不符合预期。经搜索网络,找到了原因及解决方案:

原因:未移除Handler

解决方案:在日志记录完成后调用removeHandler

修改前示例代码:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)
 logger.error(message)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

修改前输出结果:

2016-07-08 09:17:29,740 - ERROR - testlog - hi 2016-07-08 09:17:29,740 - ERROR - testlog - hi too 2016-07-08 09:17:29,740 - ERROR - testlog - hi too 2016-07-08 09:17:29,740 - ERROR - testlog - hi three 2016-07-08 09:17:29,740 - ERROR - testlog - hi three 2016-07-08 09:17:29,740 - ERROR - testlog - hi three

修改后示例代码:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)
 logger.error(message)

 # 添加下面一句,在记录日志之后移除句柄
 logger.removeHandler(streamhandler)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

修改后输出结果:

2016-07-08 09:32:28,206 - ERROR - testlog - hi 2016-07-08 09:32:28,206 - ERROR - testlog - hi too 2016-07-08 09:32:28,206 - ERROR - testlog - hi three

深度解析:

经查阅资料可知,问题在于第二次调用log时,通过getLogger(name)获取的是同一个logger实例。该logger中已存在第一次添加的handler,第二次调用又添加了一个新的handler,导致该logger中包含两个相同的handler。以此类推,调用次数越多,handler数量越多。

因此,这里有以下几种解决办法:

  • 每次创建不同name的logger,每次都是新logger,从而避免添加多个handler的问题。(注:此方法较为笨拙,但我之前曾这样使用。)
  • 如上文所示,每次记录日志后,调用removeHandler()移除该logger中的handler。
  • 在log方法中进行判断,若该logger已有handler,则不再添加新handler。
  • 与方法2类似,但使用pop方法将logger的handler列表中的handler移除。

以下是方法3与方法4的代码示例:

方法3:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 # 这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志
 if not logger.handlers:
 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)
 logger.addHandler(streamhandler)

 logger.error(message)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

方法4:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)

 logger.error(message)

 # 用pop方法把logger.handlers列表中的handler移除,注意如果你add了多个handler,这里需多次pop,或者可以直接为handlers列表赋空值
 logger.handlers.pop()
 # logger.handler = []

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

上述几种方法经测试均有效,个人觉得方法3的判断逻辑更为优雅,您怎么看?

总结

以上就是本文的全部内容,希望本文的内容对大家的学习或工作具有一定的参考借鉴价值。如有疑问欢迎留言交流,感谢大家对本站的支持。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表