You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免Python logging库出现重复输出问题?

解决Python Logging重复输出的问题

你猜的没错,问题确实出在每次调用set_logger时都会给根logger(logging.getLogger(''))添加一个新的StreamHandler,导致handler不断累积,每多运行一次就多一份输出。下面是几种成熟的解决思路,你可以根据自己的场景选择:

方法一:检查已有Handler,避免重复添加

在添加新的console handler前,先遍历根logger的现有handler,判断是否已经存在StreamHandler实例(或者更精确地判断是否是我们要添加的那种console handler),如果存在就不再重复添加。修改你的set_logger函数如下:

import logging

def set_logger(file_name):
    '''sets up output via loggers and a output file.'''
    output = ('%(asctime)-20s %(filename)-15s %(funcName)-15s %(levelname)-8s %(message)s')
    # 配置文件日志
    logging.basicConfig(level=logging.DEBUG, format=output, filename=file_name, filemode='w')
    
    # 检查是否已有console handler
    root_logger = logging.getLogger('')
    console_handler_exists = False
    for handler in root_logger.handlers:
        if isinstance(handler, logging.StreamHandler):
            console_handler_exists = True
            break
    
    if not console_handler_exists:
        # 添加console handler
        console = logging.StreamHandler()
        console.setLevel(logging.INFO)
        formatter = logging.Formatter('%(message)s')
        console.setFormatter(formatter)
        root_logger.addHandler(console)

这个方法的好处是保留已有的handler配置,不会破坏其他地方可能添加的handler。如果你需要更精确的判断(比如确保是我们自己定义的那种只输出%(message)s的console handler),可以额外检查handler的formatter是否匹配:

# 在判断时增加formatter检查
if isinstance(handler, logging.StreamHandler):
    if handler.formatter._fmt == '%(message)s' and handler.level == logging.INFO:
        console_handler_exists = True
        break

方法二:重置根Logger的Handler(适合需要完全重置的场景)

如果你每次调用set_logger都希望完全重置logger的配置(移除所有旧handler,重新添加),可以先清空根logger的handlers:

def set_logger(file_name):
    '''sets up output via loggers and a output file.'''
    output = ('%(asctime)-20s %(filename)-15s %(funcName)-15s %(levelname)-8s %(message)s')
    
    root_logger = logging.getLogger('')
    # 移除所有已存在的handler
    root_logger.handlers.clear()
    
    # 重新配置文件日志
    logging.basicConfig(level=logging.DEBUG, format=output, filename=file_name, filemode='w')
    
    # 添加console handler
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(message)s')
    console.setFormatter(formatter)
    root_logger.addHandler(console)

注意:这个方法会移除根logger上所有的handler,包括其他模块可能添加的,所以如果你的项目里有其他地方也在配置logger,要谨慎使用。

方法三:使用单例模式确保Logger只被配置一次

如果你的set_logger只需要被调用一次,也可以用单例模式来保证配置只执行一次:

import logging

def set_logger(file_name):
    # 用一个函数属性标记是否已经配置过
    if hasattr(set_logger, '_configured') and set_logger._configured:
        return
    
    output = ('%(asctime)-20s %(filename)-15s %(funcName)-15s %(levelname)-8s %(message)s')
    logging.basicConfig(level=logging.DEBUG, format=output, filename=file_name, filemode='w')
    
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    
    # 标记已配置
    set_logger._configured = True

这种方法简单直接,适合只需要初始化一次logger的场景,比如在项目启动时调用一次就够了。

额外提示

在交互式环境(比如Jupyter Notebook)或者长期运行的进程中,logger的状态会被保留,所以这类重复添加handler的问题会更常见。上面的方法都能很好地解决这个问题,你可以根据自己的项目规模和需求选择最合适的方案。

内容的提问来源于stack exchange,提问作者Will

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:33:23