Python logging 模块日志重复输出问题排查与解决方案
本文深入解析Python logging模块中因重复添加Handler导致的日志重复输出问题。通过对比修改前后的代码示例,详细说明了日志重复的根本原因,并提供了四种有效的解决方案,包括使用不同Logger名称、记录后移除Handler、添加前判断以及使用pop方法清理Handler列表,帮助开发者优化日志管理。
Python logging 模块日志重复输出问题
Python logging 模块日志重复输出问题
在使用 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,则不再重复添加。
- 方案三:与方案二类似,但采用 pop 方法将 Logger 的 handlers 列表中的 Handler 移除。
方案三与方案四代码实现
以下是方案三与方案四的代码示例:
方案三代码示例:
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')
方案四代码示例:
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')
上述几种方法均经过实测可行。个人倾向于方案三,因为其通过判断 handlers 列表是否为空来决定是否添加 Handler,逻辑更为优雅。各位开发者可根据实际场景选择合适方案。
到此,关于 Python logging 模块日志重复输出问题的解决方案介绍完毕。如需了解更多相关 Python logging 知识,请搜索本站历史文章或继续浏览后续内容。感谢大家的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









