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

多进程Tornado应用如何实现所有子进程日志统一写入?

Tornado多进程模式统一写日志的可行方案

你遇到的多日志文件问题,本质是server.start(4)走prefork多进程模型时,每个子进程独立初始化了TimedRotatingFileHandler、各自持有独立文件句柄导致的。如果强行让多个进程直接写同一个日志文件,会遇到轮转冲突、内容交错、日志丢失的问题,以下是两类经过验证的解决方案:


方案1:无额外依赖,原生实现单文件日志写入

注意:不要尝试让多个子进程直接持有同一个TimedRotatingFileHandler写同一份文件,日志轮转时多进程同时操作文件会导致日志丢失、内容错乱。

用Python标准库logging自带的多进程安全日志方案,核心思路是只有一个进程/线程持有日志文件的写句柄,所有子进程通过跨进程队列推送日志,完全避免多进程写文件冲突:

  1. 调整初始化顺序:在调用server.start(4) fork子进程之前,就在主进程初始化跨进程安全的日志队列、以及实际写文件的TimedRotatingFileHandler
  2. 主进程启动QueueListener独立线程,专门消费队列里的日志写入目标文件
  3. 所有fork出来的子进程,只配置QueueHandler把日志推送到共享队列,不直接操作日志文件

参考代码结构:

import logging
import logging.handlers
import multiprocessing
import os
import tornado.web
import tornado.httpserver
import tornado.ioloop
from logging.handlers import QueueHandler, QueueListener, TimedRotatingFileHandler

# --------------- 主进程提前初始化日志组件 ---------------
log_queue = multiprocessing.Queue(-1)
dirname = "./logs"
logname = "service"
os.makedirs(dirname, exist_ok=True)

# 实际写文件的handler仅在主进程初始化一次
file_handler = TimedRotatingFileHandler(
    filename=os.path.join(dirname, logname + '.log'),
    when='MIDNIGHT',
    interval=1,
    encoding='utf-8'
)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
# 启动队列监听线程,统一消费日志写文件
queue_listener = QueueListener(log_queue, file_handler)
queue_listener.start()

def init_subprocess_logger():
    """子进程调用,仅配置队列handler,不直接写文件"""
    queue_handler = QueueHandler(log_queue)
    root_logger = logging.getLogger()
    root_logger.addHandler(queue_handler)
    root_logger.setLevel(logging.INFO)

if __name__ == "__main__":
    app = tornado.web.Application()
    server = tornado.httpserver.HTTPServer(app)
    server.bind(8000)
    server.start(4) # 启动4个子进程
    # 每个子进程初始化日志
    init_subprocess_logger()
    # 正常启动IO循环即可
    tornado.ioloop.IOLoop.current().start()

方案2:生产环境推荐,用日志收集/聚合方案

如果是线上生产环境,不推荐自己在应用层处理多进程写文件的逻辑,维护成本高、容易踩坑,更推荐以下两种轻量化做法:

  • 单节点部署场景:去掉所有本地文件日志handler,所有日志直接输出到stdout/stderr,交给进程管理工具(systemd、supervisor)统一收集所有子进程的输出,自动汇总到同一个日志流,日志轮转、切割也交给这些工具完成,不需要在代码里配置TimedRotatingFileHandler。比如用systemd管理服务时,直接通过journalctl -u 服务名就能查看所有子进程按时间排序的汇总日志,零额外配置。
  • 多节点部署场景:在每个节点部署轻量日志采集agent,把所有服务进程的日志统一采集到中心化日志存储系统,排查问题时直接全局检索即可,不需要登录单台机器翻日志文件。

选型参考

  • 小规模单节点服务、不想引入额外组件:选方案1即可
  • 生产环境、长期维护的服务:优先选方案2,稳定性更高、维护成本更低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 12:48:09