python多处理日志记录:带有RotatingFileHandler的“QueueHandler”另一个进程正在使用的文件“错误

时间:2015-08-19 15:09:54

标签: python logging multiprocessing

我正在将程序转换为多处理,并且需要能够从主进程和子进程登录到单个旋转日志。我正在尝试使用python cookbook Logging to a single file from multiple processes中的第二个示例,该示例启动logger_thread作为主进程的一部分运行,从子进程添加到的队列中拾取日志消息。该示例效果很好,如果我切换到RotatingFileHandler也可以。

但是,如果我将其更改为在子进程之前启动logger_thread(以便我也可以从主进程中记录),那么一旦日志轮换,所有后续日志记录都会生成{{1 }}

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

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()

并替换logging.config.dictConfig(d) lp = threading.Thread(target=logger_thread, args=(q,)) lp.start() workers = [] for i in range(5): wp = Process(target=worker_process, name='worker %d' % (i + 1), args=(q,)) workers.append(wp) wp.start() logging.FileHandler(使用非常小的logging.handlers.RotatingFileHandler进行测试)然后我遇到了此错误。

我正在使用Windows和python 2.7。 maxBytes不是stdlib直到python 3.2的一部分,但是我已经复制了Gist的源代码,它说这是安全的。

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

2 个答案:

答案 0 :(得分:2)

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

这有几个资源,只是google for fork和threads。有些人声称他们可以做到,但我不清楚它能否正常运作。

首先启动所有流程。

示例附加信息:

Status of mixing multiprocessing and threading in Python

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

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

一些经验法则,由评论中的fantabolous总结:

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

  • multiprocessing.Pool创建子进程和线程,因此在第一个进程之后不能创建其他进程或池。

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

  • 子流程可以创建自己的线程和流程,使用上述相同的规则。

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

答案 1 :(得分:2)

因此,您可以简单地创建自己的文件日志处理程序。我还没有看到日志从多处理中出现乱码,所以看起来文件日志轮换是个大问题。只需在您的主程序中执行此操作,您无需更改任何其他日志记录

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)

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