python多处理日志记录:带有RotatingFileHandler的QueueHandler“文件被另一个进程使用”错误
Posted
技术标签:
【中文标题】python多处理日志记录:带有RotatingFileHandler的QueueHandler“文件被另一个进程使用”错误【英文标题】:python multiprocessing logging: QueueHandler with RotatingFileHandler "file being used by another process" error 【发布时间】:2015-11-12 23:33:56 【问题描述】:我正在将程序转换为多处理,并且需要能够从主进程和子进程记录到单个循环日志。我正在尝试使用 python 食谱Logging to a single file from multiple processes 中的第二个示例,它启动了作为主进程的一部分运行的logger_thread
,从子进程添加到的队列中提取日志消息。该示例运行良好,如果我切换到 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()
到这里:
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
(用一个非常小的maxBytes
进行测试)然后我遇到了这个错误。
我使用的是 Windows 和 python 2.7。 QueueHandler
在 python 3.2 之前不是 stdlib 的一部分,但我已经从 Gist 复制了源代码,它说这样做是安全的。
我不明白为什么首先启动侦听器会产生任何影响,我也不明白为什么除 main 之外的任何进程都会尝试访问该文件。
【问题讨论】:
【参考方案1】:因此,您可以简单地制作自己的文件日志处理程序。我还没有看到日志因多处理而出现乱码,因此文件日志轮换似乎是个大问题。只需在您的主目录中执行此操作,您无需更改任何其余的日志记录
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)
我愿意猜测,每次尝试轮换时锁定可能会减慢日志记录,但在这种情况下,我们需要牺牲性能来换取正确性。
【讨论】:
【参考方案2】:你不应该在子进程之前启动任何个线程。当 Python fork 时,线程和 IPC 状态并不总是被正确复制。
这方面有几个资源,只是谷歌fork 和threads。有些人声称他们可以做到,但我不清楚它是否可以正常工作。
首先启动所有进程。
附加信息示例:
Status of mixing multiprocessing and threading in Python
https://***.com/a/6079669/4279
在您的情况下,可能是复制的打开文件句柄是问题所在,但您仍然应该在线程之前启动子进程(以及在您打开以后要销毁的任何文件之前)。
一些经验法则,由 cmets 的 fantabolous 总结:
子进程必须始终在同一进程创建的任何线程之前启动。
multiprocessing.Pool 同时创建子进程和线程,因此不得在第一个进程或池之后创建额外的进程或池。
在创建进程或池时文件不应已打开。 (这在某些情况下是可以的,但不是,例如,如果文件稍后将被删除。)
子进程可以创建自己的线程和进程,应用与上述相同的规则。
首先启动所有进程是最简单的方法
【讨论】:
谢谢 - 不知道。我想这也意味着我不应该像现在这样从非主线程中创建进程;)您的最后一条评论“应该在...之前启动您的子进程……您打开以后要销毁的任何文件”会很棘手。这是否适用于子进程永远不会使用/触摸的文件? 只有在启动子进程时打开的文件。打开,然后立即关闭是没有问题的。但是,您也可以在子进程中关闭打开的文件句柄。也许考虑在你的主进程中少做很多,甚至将日志记录转移到子进程中。 抱歉,我不知道多处理内部机制,但是,如果它的 async processing threads 仍在运行,我不会创建额外的进程。也许主进程应该在它构建它的池之前创建一个代理进程,然后使用代理进程稍后创建任何额外的一次性进程,或者你可能将池创建本身移动到一个子进程中。 在***进程中设置“无线程”规则可能是最干净的,这意味着您不会在那里使用 multiprocessing.pool。 这是一个好的经验法则。实际上,当您启动一个进程时,您不应该运行除主线程之外的任何线程;首先启动所有进程是实现这一目标的简单方法。以上是关于python多处理日志记录:带有RotatingFileHandler的QueueHandler“文件被另一个进程使用”错误的主要内容,如果未能解决你的问题,请参考以下文章
在 Windows 中使用多处理进行 Python 日志记录
Python-logging详解(彩色日志扩展,多进程安全等)
Python-logging详解(彩色日志扩展,多进程安全等)