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

Python多线程日志重复输出:新建线程后重复次数递增问题排查

Python多线程日志重复输出且次数递增的问题解决

问题根源

Python的logging模块中,logging.getLogger(name)返回的是全局共享的单例对象。你的代码里,每个线程执行thread_worker时,都会给同一个threadTest logger添加新的StreamHandler。随着线程不断创建,logger上绑定的handler数量越来越多,每条日志消息会被所有handler各自输出一次,所以重复次数随线程数递增。你之前只关闭了handler,但没有从logger中移除,这些handler仍然存在于logger的handlers列表中。

解决方案

以下是几种可行的修复方式,可根据你的场景选择:

方案1:检查handler是否已存在,避免重复添加

在给logger添加handler前,先判断logger是否已有handler,仅在空的时候才添加:

def thread_worker():
    name = "threadTest"
    logger = logging.getLogger(name)
    # 先检查是否已有handler,避免重复添加
    if not logger.handlers:
        log_formatter = logging.Formatter(fmt='%(threadName)s:%(thread)d:%(process)d %(asctime)s - %(name)s - %(levelname)s: %(message)s')
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(log_formatter)
        logger.addHandler(console_handler)
    print("Hello")
    logger.warning("Hello")
    time.sleep(2)
    return

方案2:在主线程中一次性配置logger

把logger的配置逻辑移到程序启动阶段(主线程),线程直接使用已配置好的logger,无需每次创建handler:

import time
import threading
import logging
import logging.config

# 全局配置logger,仅执行一次
def setup_logger():
    name = "threadTest"
    logger = logging.getLogger(name)
    log_formatter = logging.Formatter(fmt='%(threadName)s:%(thread)d:%(process)d %(asctime)s - %(name)s - %(levelname)s: %(message)s')
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(log_formatter)
    logger.addHandler(console_handler)
    logger.setLevel(logging.WARNING)  # 显式设置日志级别,避免默认级别限制

def thread_worker():
    logger = logging.getLogger("threadTest")
    print("Hello")
    logger.warning("Hello")
    time.sleep(2)
    return

def main():
    setup_logger()
    threads: dict[str, threading.Thread] = dict()
    i = 1
    while True:
        thread_test = threading.Thread(target = thread_worker, name = f"thread{str(i)}")
        threads[f"thread{str(i)}"] = thread_test
        thread_test.start()
        time.sleep(5)
        dead_threads: list[str] = list()
        for thread_name, thread in threads.items():
            if not thread.is_alive():
                print(f"{thread_name} is dead, remove from {threads = }")
                thread.join()
                dead_threads.append(thread_name)
                
        for thread_name in dead_threads:
            _ = threads.pop(thread_name)
        i += 1

if __name__ == "__main__":
    main()

方案3:线程结束时移除handler

如果必须在线程中动态添加handler,那么在线程结束时要从logger中移除handler,而非仅关闭:

def thread_worker():
    name = "threadTest"
    logger = logging.getLogger(name)
    log_formatter = logging.Formatter(fmt='%(threadName)s:%(thread)d:%(process)d %(asctime)s - %(name)s - %(levelname)s: %(message)s')
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(log_formatter)
    logger.addHandler(console_handler)
    
    try:
        print("Hello")
        logger.warning("Hello")
        time.sleep(2)
    finally:
        # 先关闭资源,再从logger中移除handler
        console_handler.close()
        logger.removeHandler(console_handler)
    return

验证效果

修改后,每个线程的日志只会输出一次,不会随着线程数量递增出现重复次数增加的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 15:47:21