多进程下Queue+WSGI+RotatingFileHandler写日志无输出问题
问题根因
日志无法写入文件由3个配置错误直接导致:
QueueListener初始化参数要求传入已实例化的日志处理器对象,代码中传入的是配置内的处理器名称字符串,监听器无法识别字符串类型的处理器,完全无法执行日志输出逻辑xyz日志器同时绑定了WSGI流处理器、文件切分处理器和队列处理器,会导致日志重复输出;队列模式下日志输出全链路应为「业务代码→日志器→队列处理器→内存/跨进程队列→监听器线程→实际输出处理器」,日志器本身不应直接挂载实际输出处理器- 多进程场景下使用的
queue.Queue是进程内内存队列,跨进程时数据不互通,子进程投递的日志无法到达主进程的监听器,自然不会写入文件
修复方案
核心调整逻辑
- 日志配置阶段不给业务logger直接挂载文件、WSGI流这类实际输出处理器,所有实际输出动作统一交给QueueListener调度
- 配置加载完成后,从logging全局管理器中取出已经实例化好的处理器对象,传入QueueListener,禁止直接传配置里的处理器名称字符串
- 多进程部署场景将内存队列替换为跨进程队列,保证不同worker进程的日志能正常投递到监听器
- 监听器必须在主进程启动,保证只有一个线程操作日志文件,从根源避免RotatingFileHandler多进程抢锁、切分异常的问题
可直接运行的修复后代码
import logging import logging.config import logging.handlers as handlers from logging.handlers import QueueHandler, QueueListener import queue # 多进程部署时替换为跨进程队列 # from multiprocessing import Queue config = { 'version': 1, 'disable_existing_loggers': False, 'formatters': {'default': { 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' }}, 'handlers':{ "fileHandler": { "class": "logging.handlers.RotatingFileHandler", "formatter": 'default', "filename": "testinglog.log", "maxBytes": 1024000, "backupCount": 5 }, 'wsgi':{ 'class': 'logging.StreamHandler', 'stream': 'ext://flask.logging.wsgi_errors_stream', 'formatter': 'default' } }, 'loggers':{ "xyz": { "handlers": [], "level": "INFO", 'propagate': False, }, } } # 加载日志配置 logging.config.dictConfig(config) # 初始化队列:单进程用内存队列,多进程用跨进程队列 log_queue = queue.Queue(-1) # log_queue = Queue(-1) # 多进程部署时启用该行 # 给业务logger挂载队列处理器 target_logger = logging.getLogger('xyz') queue_handler = QueueHandler(log_queue) target_logger.addHandler(queue_handler) # 取出已实例化的实际输出处理器 file_handler = logging.getHandlerByName('fileHandler') wsgi_handler = logging.getHandlerByName('wsgi') # 启动队列监听器 listener = QueueListener(log_queue, file_handler, wsgi_handler, respect_handler_level=True) listener.start() # 业务侧日志测试 if __name__ == '__main__': target_logger.info("测试日志写入,该条日志会同时输出到WSGI错误流和本地日志文件")
额外注意事项
- 程序退出前必须调用
listener.stop(),将队列中残留的日志全部刷写到存储介质,避免日志丢失 - 加上
respect_handler_level=True参数,保证监听器会尊重每个处理器单独配置的日志级别,不会出现低级别日志被意外丢弃的问题 - 队列模式下只有监听器线程会操作
RotatingFileHandler,不存在多进程/多线程同时抢占文件句柄的问题,日志大小达到阈值时滚动切分可正常执行,不会出现文件损坏、切分失败的问题
内容的提问来源于stack exchange,提问作者dummy
相关产品推荐
相关产品推荐

