如何在运行的多进程间共享已配置的Logger?
核心问题定位方向
1. Build类初始化循环逻辑错误
你的builder.py中Build.__init__的循环逻辑完全不符合并行处理预期:每次循环都会启动一个worker进程,随即启动日志线程,等待所有已启动的worker结束后就终止日志线程。这会导致worker进程被逐个串行执行,且日志线程频繁启停,部分日志记录可能因线程生命周期问题丢失。
修正示例:
def __init__(self, q): self.queue = q self.logger = logging.getLogger('dcre') workers = [] # 先批量启动所有worker进程 for i in range(5): wp = Process(target=self.foo, args=(i,)) workers.append(wp) wp.start() # 所有worker启动完成后,启动单个日志线程统一处理 lp = threading.Thread(target=self.logger_thread) lp.start() # 等待所有worker执行完毕 for wp in workers: wp.join() # 发送终止信号给日志线程 self.queue.put(None) lp.join()
2. Windows spawn模式下的日志实例隔离
Windows多进程默认使用spawn模式,子进程会重新导入模块并初始化全新对象,主进程中配置的logger不会被继承。你当前代码中,子进程执行foo时使用的self.logger是子进程内的全新实例,没有配置QueueHandler,导致日志无法传递到主进程的队列,自然无法被FileHandler写入文件。
修正示例:在子进程内部配置QueueHandler
def foo(self, i): # 子进程单独配置QueueHandler,将日志发送到主进程传递的队列 qh = logging.handlers.QueueHandler(self.queue) logger = logging.getLogger('dcre') logger.addHandler(qh) logger.setLevel('DEBUG') # 确保级别足够低,不会过滤日志 msgs = ( (10, "This is a DEBUG message. You shouldn't see this."), (20, 'This is an INFO message. Just so you know.'), (30, 'This is a WARNING message. Be careful, yo.'), (40, 'This is an ERROR message. Man, you done messed up.'), (50, 'This is a CRITICAL message. Game over!') ) logger.log(*msgs[i])
注意:禁止直接传递logger实例给子进程,logging.Logger对象不可被pickle序列化,会触发异常。
3. 日志处理器重复添加与配置冲突
主进程中logger.configure方法多次调用(如按钮重复点击)会重复添加QueueHandler和FileHandler,导致日志重复或处理逻辑混乱;同时logging.basicConfig会给root logger添加StreamHandler,可能干扰自定义logger的级别过滤。
修正示例:添加handler存在性检查
def configure(self, q): self.q = q # 避免重复添加QueueHandler if not any(isinstance(h, logging.handlers.QueueHandler) for h in self.logger.handlers): self.qh = logging.handlers.QueueHandler(self.q) self.logger.addHandler(self.qh) # 避免重复添加FileHandler if not any(isinstance(h, logging.FileHandler) for h in self.logger.handlers): self.file = logging.FileHandler('log.log') self.file.setLevel('DEBUG') self.logger.addHandler(self.file) # 仅在root logger无handler时调用basicConfig if not logging.root.handlers: logging.basicConfig(level='INFO')
4. 队列与日志线程的生命周期冲突
GUI中使用全局队列q,每次点击按钮创建Build实例时,都会向同一个队列写入终止信号None。后续启动的日志线程会提前读取到之前的None信号并退出,导致后续worker的日志无法被处理。
建议:每个Build实例使用独立的队列,或者在每次处理前清空队列(注意多线程/进程下的队列操作安全)。
5. 日志级别过滤异常
主进程中logger设置为DEBUG级别,但子进程中的logger默认级别为WARNING,会过滤DEBUG和INFO级别的日志。需要确保子进程中的logger级别也设置为DEBUG,避免日志被过滤。
内容的提问来源于stack exchange,提问作者Sam

