Python多个进程的日志记录
我有一个可能会长时间运行的程序,目前有4个进程,但可以配置更多。我研究了如何使用Python的多进程记录日志,并采用了SocketHandler的方法。我之前使用单个日志记录器(没有使用套接字)时从未遇到过问题,但根据我所了解的,最终可能会出现意外的失败。根据我的理解,当多个进程同时尝试写入同一个文件时,结果是未知的。我的代码基本上是这样做的:
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
对象会发生什么?
按照我现在的做法,我是否真的解决了我想避免的问题(多个打开的文件/套接字)?这会失败吗,为什么会失败(我想知道将来类似的实现是否会失败)?
另外,从多个进程向一个文件记录日志的“正常”(一个log=
表达式)方法是如何失败的?会引发IOError/OSError吗?还是说只是没有完全写入数据到文件?
如果有人能提供答案或相关链接帮助我,那就太好了。谢谢。
附注:我在Mac OS X Lion上进行测试,代码可能最终会在Windows机器上的CentOS 6虚拟机上运行(如果这有影响的话)。我使用的任何解决方案不需要在Windows上工作,但应该能在Unix系统上运行。
更新:这个问题已经开始偏离日志记录的具体行为,更接近于Linux在进程分叉时如何处理文件描述符。我翻出了我的一本大学教材,似乎如果你在两个进程中以追加模式打开一个文件(而不是在分叉之前),它们都能正确写入文件,只要你的写入不超过实际的内核缓冲区(尽管可能需要使用行缓冲,这一点我还不确定)。这会创建两个文件表条目和一个v-node表条目。打开文件后再分叉本来是不应该工作的,但似乎只要不超过内核缓冲区就可以(我在之前的程序中做过)。
所以我想,如果你想要跨平台的多进程日志记录,你可以使用套接字,并在每次分叉后创建一个新的SocketHandler,以确保安全,正如Vinay在下面建议的那样(这应该在任何地方都能工作)。对我来说,由于我对软件运行的操作系统有很强的控制,我想我会选择一个全局的log
对象,使用FileHandler(默认以追加模式打开,并在大多数操作系统上使用行缓冲)。关于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(或其他人),我错得有多离谱?请告诉我。谢谢你能提供的任何进一步的清晰度或确定性。
1 个回答
我每次调用os.fork()都需要创建一个新的日志对象吗?现有的全局日志对象会发生什么?
据我所知,全局日志对象在父进程和子进程中都是指向同一个记录器的。所以你不需要创建一个新的。不过,我觉得你应该在fork()
之后,在monitor()
中创建并添加SocketHandler
,这样每个子进程就能有四个不同的连接。如果不这样做,那么在monitor()
中创建的子进程会继承父进程的SocketHandler和它的socket句柄,但我不确定这样会不会出问题。这个行为可能会依赖于操作系统,你在OSX上可能会比较幸运。
按照我现在的做法,我真的能避免我想要避免的问题(多个打开的文件/套接字)吗?这会失败吗,为什么会失败(我想知道未来类似的实现是否会失败)?
如果你在最后一次fork()
之后创建与socket服务器的连接,我不认为会失败,但在其他情况下我不确定行为是否明确。你提到多个打开的文件,但我在你的伪代码片段中没有看到打开文件的相关内容,只有打开套接字。
另外,从多个进程向一个文件记录日志的“正常”方法(一个log=表达式)是怎么失败的?会引发IOError/OSError吗?还是说只是没有完全写入数据到文件?
我觉得这个行为并不明确,但可以预期失败的情况是不同进程的日志信息会混在一起,比如:
Process A writes first part of its message
Process B writes its message
Process A writes second part of its message
更新:如果你像你评论中描述的那样使用FileHandler
,情况就不太好了,因为我上面描述的场景:进程A和B都开始指向文件末尾(因为是追加模式),但之后可能会出现不同步的情况,因为(例如在多处理器上,甚至在单处理器上)一个进程可能会在另一个进程完成之前就写入共享的文件句柄。