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

Python两个日志记录器写入同一文件午夜滚动报错的解决办法

解决多日志记录器写入同一文件的午夜滚动权限错误问题

你的问题核心是多个独立的TimedRotatingFileHandler同时操作同一个日志文件,午夜滚动时两个handler先后尝试重命名文件,导致文件被占用触发权限错误。解决思路是让所有日志记录器共享同一个文件处理器实例,避免重复打开文件句柄。

方案1:共享全局文件处理器(推荐,无需第三方包)

步骤1:重构日志初始化逻辑

先创建一个全局的TimedRotatingFileHandler实例,再给所有需要的日志记录器添加这个实例,而不是每个记录器单独创建:

import logging
from logging.handlers import TimedRotatingFileHandler

# 自定义格式化器(保持你原有的实现)
class CLILoggerFormatter(logging.Formatter):
    # 你的格式化逻辑
    pass

class FileLoggerFormatter(logging.Formatter):
    # 你的格式化逻辑
    pass

def create_global_file_handler(log_path: str) -> TimedRotatingFileHandler:
    """创建全局共享的文件日志处理器"""
    file_handler = TimedRotatingFileHandler(
        filename=log_path,
        when="midnight",
        encoding="utf-8"
    )
    file_handler.setFormatter(FileLoggerFormatter())
    file_handler.setLevel(logging.DEBUG)
    return file_handler

def init_logger(name: str, shared_file_handler: logging.Handler):
    """初始化日志记录器,复用共享的文件处理器"""
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    # 避免重复添加控制台处理器
    has_cli_handler = any(isinstance(h, logging.StreamHandler) for h in logger.handlers)
    if not has_cli_handler:
        cli_handler = logging.StreamHandler()
        cli_handler.setFormatter(CLILoggerFormatter())
        cli_handler.setLevel(logging.INFO)
        logger.addHandler(cli_handler)

    # 避免重复添加共享文件处理器
    has_shared_file_handler = any(h is shared_file_handler for h in logger.handlers)
    if not has_shared_file_handler:
        logger.addHandler(shared_file_handler)

步骤2:修改main.py使用共享处理器

import my_lib
from your_module import init_logger, create_global_file_handler

# 创建全局共享的文件处理器(只执行一次)
global_file_handler = create_global_file_handler("logs/log.log")

# 初始化主程序日志记录器
init_logger(__name__, global_file_handler)
logger = logging.getLogger(__name__)

# 初始化my_lib的日志记录器
init_logger("my_lib", global_file_handler)

原理说明

所有日志记录器共用同一个文件处理器实例,只有一个文件句柄打开日志文件。午夜滚动时,只有这一个处理器执行文件重命名操作,不会出现多个进程/句柄争抢文件的情况。

方案2:使用第三方并发安全日志处理器

如果不想修改现有初始化逻辑,可以使用concurrent-log-handler包中的ConcurrentRotatingFileHandler,它专门解决多进程/多处理器写入同一文件的滚动问题:

  1. 安装包:
pip install concurrent-log-handler
  1. 修改init_logger中的文件处理器:
from concurrent_log_handler import ConcurrentRotatingFileHandler

# 替换原有的TimedRotatingFileHandler
file_handler = ConcurrentRotatingFileHandler(
    filename=log_path,
    when="midnight",
    encoding="utf-8",
    maxBytes=0,  # 仅按时间滚动,忽略大小限制
)

这种方式无需共享处理器,每个日志记录器可以独立创建处理器,但底层会处理文件锁和滚动冲突。

注意事项

  • 原my_lib中的NullHandler可以保留,不会影响新添加的处理器(日志会被所有有效处理器处理)。
  • 无论哪种方案,都要确保不会重复给同一个日志记录器添加相同类型的处理器(上述代码中已经加入了重复检查)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:10:28