服务器端Tornado自定义日志与内置日志统一输出实现方案
问题根因
- 服务器运行时触发了Tornado多进程模式(
server.start(0)仅在非Windows环境执行),你调用tornado.options.parse_command_line()时默认会触发Tornado的日志自动初始化逻辑,直接覆盖了你之前通过logging.basicConfig配置的根日志设置 - 你通过
os.chdir修改工作目录的方式不可靠,多进程fork后子进程的工作目录可能发生变化,导致后续日志写入路径不符合预期 - 多进程场景下,基础的
logging.basicConfig配置在子进程中会失效,只有主进程初始化阶段的日志能正常写入
解决方案
步骤1:关闭Tornado自动日志配置
调用tornado.options.parse_command_line时显式指定logging=None,禁止Tornado修改已有的日志配置。
步骤2:调整日志初始化逻辑
直接构造日志文件的绝对路径,避免修改工作目录;显式给根日志添加FileHandler,同时保证Tornado三类日志的传播属性开启,默认情况下Tornado日志的传播属性为开启,会自动把日志上报到根日志,写入同一个文件。
修改后的代码示例
自定义日志模块
import os import logging def create_logger(): """ 创建全局日志配置,所有日志(含Tornado内置日志)都会写入同一个文件 :return: bool - 初始化成功返回True """ # 直接构造日志文件绝对路径,避免修改工作目录 base_dir = os.path.dirname(os.path.abspath(__file__)) log_dir = os.path.normpath(os.path.join(base_dir, os.pardir, os.pardir, 'logs')) # 自动创建不存在的日志目录 os.makedirs(log_dir, exist_ok=True) log_path = os.path.join(log_dir, "PythonLogger.log") # 获取根日志 root_logger = logging.getLogger() # 清空原有Handler,避免配置冲突 for handler in root_logger.handlers[:]: root_logger.removeHandler(handler) # 显式配置文件Handler file_handler = logging.FileHandler(log_path, mode='a', encoding='utf-8') formatter = logging.Formatter('%(asctime)s %(message)s') file_handler.setFormatter(formatter) root_logger.addHandler(file_handler) # 设置全局日志级别 root_logger.setLevel(logging.NOTSET) # 显式配置Tornado三类日志的级别和传播属性 for log_name in ["tornado.access", "tornado.application", "tornado.general"]: tornado_log = logging.getLogger(log_name) tornado_log.setLevel(logging.NOTSET) tornado_log.propagate = True root_logger.info('El logger está inicializado') return True def log_info_message(msg): """ 打印info级别日志 :param msg: 日志内容 :return: None """ logging.getLogger().info(msg)
启动代码修改
if __name__ == '__main__': # 日志初始化要放在所有逻辑最前面 create_logger() log_info_message('Initiating Python application') # 关键配置:禁止Tornado自动覆盖日志配置 tornado.options.parse_command_line(logging=None) server = tornado.httpserver.HTTPServer(test.app) server.listen(options.port) try: if 'Windows_NT' not in os.environ.values(): server.start(0) tornado.ioloop.IOLoop.instance().start() except KeyboardInterrupt: tornado.ioloop.IOLoop.instance().stop()
注意事项
多进程场景下多个进程同时写同一个日志文件可能出现日志乱序、内容截断的问题,如果对日志完整性要求很高,建议改为单进程采集日志,或使用支持多进程安全的日志Handler,或者给每个进程分配独立的日志文件。
内容的提问来源于stack exchange,提问作者MartinHN
相关产品推荐
相关产品推荐

