我有一个可能长时间运行的程序,当前有 4 个进程,但可以配置为有更多进程。我研究过logging from multiple processes使用 python 的 logging
并使用讨论的 SocketHandler 方法 here 。我从来没有遇到过使用单个记录器(没有套接字)的任何问题,但从我读到的内容来看,我被告知它最终会意外失败。据我了解,当您尝试同时写入同一个文件时,未知会发生什么。我的代码基本上执行以下操作:
import logging
log = logging.getLogger(__name__)
def monitor(...):
# Spawn child processes with os.fork()
# os.wait() and act accordingly
def main():
log_server_pid = os.fork()
if log_server_pid == 0:
# Create a LogRecordSocketServer (daemon)
...
sys.exit(0)
# Add SocketHandler to root logger
...
monitor(<configuration stuff>)
if __name__ == "__main__":
main()
所以我的问题是:我是否需要在每个 os.fork()
之后创建一个新的 log
对象?现有的全局 log
对象会发生什么?
按照我的方式做事,我是否可以解决我试图避免的问题(多个打开的文件/套接字)?这会失败吗?为什么会失败(我希望能够知道 future 类似的实现是否会失败)?
此外,从多个进程记录到一个文件的“正常”(一个 log=
表达式)方法会以何种方式失败?它会引发 IOError/OSError 吗?或者它只是没有完全将数据写入文件?
如果有人可以提供答案或链接来帮助我,那就太好了。谢谢。
仅供引用: 我正在 Mac OS X Lion 上进行测试,代码最终可能会在 Windows 计算机上的 CentOS 6 VM 上运行(如果这很重要的话)。无论我使用什么解决方案都不需要在 Windows 上工作,但应该在基于 Unix 的系统上工作。
更新:这个问题已经开始不再记录特定行为,而更多地涉及 Linux 在 fork 期间如何处理文件描述符的领域。我拿出一本大学教科书,似乎如果您从两个进程(而不是在 fork 之前)以附加模式打开一个文件,只要您的写入不超过,它们都将能够正确写入该文件实际的内核缓冲区(尽管可能需要使用行缓冲,但仍不确定)。这将创建 2 个文件表条目和 1 个 v 节点表条目。打开文件然后 fork 不应该起作用,但只要您不像以前那样超出内核缓冲区,它似乎就可以起作用(我在之前的程序中已经这样做了)。
所以我想,如果你想要独立于平台的多处理日志记录,你可以使用套接字并在每个 fork 后创建一个新的 SocketHandler 以确保安全,如下 Vinay 建议的那样(这应该在任何地方都有效)。对我来说,由于我对运行软件的操作系统有很强的控制权,我想我将使用一个带有 FileHandler 的全局 log
对象(默认以附加模式打开,并且行在大多数操作系统上进行缓冲)。 open
的文档说“负缓冲意味着使用系统默认值,通常对 tty 设备是行缓冲,对其他文件是完全缓冲。如果省略,则使用系统默认值。”或者我可以创建自己的日志流以确保行缓冲。需要明确的是,我同意:
# Process A
a_file.write("A\n")
a_file.write("A\n")
# Process B
a_file.write("B\n")
生产...
A\n
B\n
A\n
只要它不产生...
AB\n
\n
A\n
Vinay(或其他人),我错了多少?让我知道。感谢您提供更多的清晰度/确定性。
最佳答案
Do I need to create a new log object after each os.fork()? What happens to the existing global log object?
据我所知,全局日志对象仍然指向父进程和子进程中的同一个记录器。所以你不需要创建一个新的。但是,我认为您应该在 monitor()
中的 fork()
之后创建并添加 SocketHandler
,以便套接字服务器有四个不同的连接,每个子进程一个。如果不这样做,那么在 Monitor() 中 fork 的子进程将从其父进程继承 SocketHandler 及其套接字句柄,但我不确定它是否会出现异常行为。该行为可能取决于操作系统,并且您在 OSX 上可能很幸运。
With doing things the way I am, am I even getting around the problem that I'm trying to avoid (multiple open files/sockets)? Will this fail and why will it fail (I'd like to be able to tell if future similar implementations will fail)?
如果按照我上面的建议在最后一个 fork()
之后创建到套接字服务器的套接字连接,我不会期望失败,但我不确定该行为在任何其他情况。您引用了多个打开的文件,但我在伪代码片段中没有看到打开文件的引用,只是打开套接字。
Also, in what way does the "normal" (one log= expression) method of logging to one file from multiple processes fail? Does it raise an IOError/OSError? Or does it just not completely write data to the file?
我认为该行为没有明确定义,但人们会期望故障模式以来自文件中不同进程的散布日志消息的形式呈现,例如
Process A writes first part of its message
Process B writes its message
Process A writes second part of its message
更新:如果您按照评论中描述的方式使用 FileHandler
,由于我上面描述的场景,事情不会那么好:进程 A 和 B 都开始指向文件末尾(由于追加模式),但此后事情可能会变得不同步,因为(例如,在多处理器上,但甚至可能在单处理器上),一个进程可以(抢占另一个进程) )在另一个进程完成之前写入共享文件句柄。
关于来自多个进程的 Python 日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8403839/