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
相关产品推荐
相关产品推荐

