Python多进程日志记录:使用旋转文件处理程序的QueueHandler“文件正在被另一个进程使用”

2024-04-24 14:12:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在将一个程序转换为多进程,并且需要能够从主进程和子进程将日志记录到单个循环日志中。我尝试使用python烹饪书Logging to a single file from multiple processes中的第二个示例,它启动作为主进程一部分运行的logger_thread,从子进程添加到的队列中获取日志消息。这个例子的工作原理和is一样好,如果我切换到RotatingFileHandler也可以。在

但是,如果我将其更改为在子进程之前启动logger_thread(这样我也可以从主进程记录日志),那么一旦日志旋转,所有后续日志记录都会生成一个带有WindowsError: [Error 32] The process cannot access the file because it is being used by another process的回溯。在

换句话说,我改变了第二个例子中的代码

workers = []
for i in range(5):
    wp = Process(target=worker_process, name='worker %d' % (i + 1), args=(q,))
    workers.append(wp)
    wp.start()
logging.config.dictConfig(d)
lp = threading.Thread(target=logger_thread, args=(q,))
lp.start()

为此:

^{pr2}$

然后用logging.FileHandler替换logging.handlers.RotatingFileHandler(用一个非常小的maxBytes进行测试),然后我遇到了这个错误。在

我使用的是Windows和python2.7。QueueHandler不是stdlib til python3.2的一部分,但是我从Gist复制了源代码,它说这样做是安全的。在

我不明白为什么首先启动侦听器会有什么不同,也不明白为什么除main之外的任何进程都会尝试访问该文件。在


Tags: target进程islogging记录argsloggerprocess
2条回答

永远不要在子进程之前启动任何线程。当Python分叉时,线程和IPC状态不会总是被正确地复制。在

这方面有很多资源,只需google搜索fork和threads。有些人声称他们能做到,但我不清楚它是否能正常工作。在

先开始你所有的程序。在

附加信息示例:

Status of mixing multiprocessing and threading in Python

https://stackoverflow.com/a/6079669/4279

在您的情况下,可能是复制的打开文件句柄有问题,但是您仍然应该在线程之前(以及在打开以后要销毁的任何文件之前)启动子进程。在

fantabolous从评论中总结了一些经验法则:

  • 子进程必须在同一进程创建的任何线程之前启动。

  • 在多处理.池同时创建子进程和线程,因此不能在第一个进程或池之后创建其他进程或池。

  • 在创建进程或池时,文件不应已打开。(在某些情况下,这是可以的,但不可以,例如,如果以后要删除某个文件。)

  • 子流程可以创建自己的线程和进程,并应用上面的相同规则。

  • 首先启动所有进程是最简单的方法

因此,您可以简单地创建自己的文件日志处理程序。我还没有看到日志由于多处理而变得混乱,所以文件日志轮换似乎是个大问题。只需在主目录中执行此操作,就不必更改其他日志记录

import logging
import logging.handlers
from multiprocessing import RLock

class MultiprocessRotatingFileHandler(logging.handlers.RotatingFileHandler):
    def __init__(self, *kargs, **kwargs):
        super(MultiprocessRotatingFileHandler, self).__init__(*kargs, **kwargs)
        self.lock = RLock()

    def shouldRollover(self, record):
        with self.lock:
            super(MultiprocessRotatingFileHandler, self).shouldRollover(record)

file_log_path = os.path.join('var','log', os.path.basename(__file__) + '.log')
file_log = MultiprocessRotatingFileHandler(file_log_path,
                                           maxBytes=8*1000*1024,
                                           backupCount=5,
                                           delay=True)

logging.basicConfig(level=logging.DEBUG)
logging.addHandler(file_log)

我愿意猜测每次尝试旋转时锁定可能会减慢日志记录速度,但在这种情况下,我们需要牺牲性能来保证正确性。在

相关问题 更多 >