在Python中,当使用multiprocessing.Process
类时,有没有一种方法可以记录来自给定Process
的标准输出(stdout)?
在Python中,当使用multiprocessing.Process
类时,有没有一种方法可以记录来自给定Process
的标准输出(stdout)?
最简单的方法可能是仅仅覆盖 sys.stdout
。稍微修改一下multiprocessing 手册中的一个例子:
from multiprocessing import Process
import os
import sys
def info(title):
print title
print 'module name:', __name__
print 'parent process:', os.getppid()
print 'process id:', os.getpid()
def f(name):
sys.stdout = open(str(os.getpid()) + ".out", "w")
info('function f')
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
q = Process(target=f, args=('fred',))
q.start()
p.join()
q.join()
然后运行它:
$ ls m.py $ python m.py $ ls 27493.out 27494.out m.py $ cat 27493.out function f module name: __main__ parent process: 27492 process id: 27493 hello bob $ cat 27494.out function f module name: __main__ parent process: 27492 process id: 27494 hello fred
我想对 @Mark Rushakoff 的回答补充两点。在调试时,我发现把 open()
调用的 buffering
参数设置为 0 非常有用。
There are only two things I would add to @Mark Rushakoff answer. When debugging, I found it really useful to change the buffering
parameter of my open()
calls to 0.
sys.stdout = open(str(os.getpid()) + ".out", "a", buffering=0)
否则,疯狂,因为当tail -f
输出文件时,结果可能非常间歇性。对于tail -f
来说,buffering=0
很好。
为了完整起见,请将sys.stderr
也重定向。
sys.stderr = open(str(os.getpid()) + "_error.out", "a", buffering=0)
另外,为了方便起见,如果您愿意,您可以将其转储到单独的进程类中。
class MyProc(Process):
def run(self):
# Define the logging in run(), MyProc's entry function when it is .start()-ed
# p = MyProc()
# p.start()
self.initialize_logging()
print 'Now output is captured.'
# Now do stuff...
def initialize_logging(self):
sys.stdout = open(str(os.getpid()) + ".out", "a", buffering=0)
sys.stderr = open(str(os.getpid()) + "_error.out", "a", buffering=0)
print 'stdout initialized'
sys.stdout = Logger()
,其中Logger
是一个类,它的write
方法(立即执行或累积直到检测到\n
)调用logging.info
(或您想要记录的其他方式)。在此示例中可以看到它的运行方式。
我不确定您所说的“给定”进程是什么意思(由谁指定,它与其他所有进程有何区别……?),但如果您意思是在实例化进程时知道要单独处理哪个进程,那么您可以将其target
函数(仅限该函数)或您在Process
子类中覆盖的run
方法包装到一个包装器中,以执行这种sys.stdout "重定向",而让其他进程保持不变。以下是用简单直接的方法来捕获 multiprocessing.Process 和 io.TextIOWrapper 的stdout的方式:
import app
import io
import sys
from multiprocessing import Process
def run_app(some_param):
out_file = open(sys.stdout.fileno(), 'wb', 0)
sys.stdout = io.TextIOWrapper(out_file, write_through=True)
app.run()
app_process = Process(target=run_app, args=('some_param',))
app_process.start()
# Use app_process.termninate() for python <= 3.7.
app_process.kill()
log_to_stderr()函数是最简单的解决方案。
来自PYMOTW:
multiprocessing提供了一个方便的模块级函数log_to_stderr(),用于启用日志记录。 它使用logging设置一个记录器对象,并添加处理程序,以便将日志消息发送到标准错误通道。 默认情况下,日志记录级别设置为NOTSET,因此不会产生任何消息。 将不同的级别传递给日志记录器以初始化所需的详细级别。
import logging
from multiprocessing import Process, log_to_stderr
print("Running main script...")
def my_process(my_var):
print(f"Running my_process with {my_var}...")
# Initialize logging for multiprocessing.
log_to_stderr(logging.DEBUG)
# Start the process.
my_var = 100;
process = Process(target=my_process, args=(my_var,))
process.start()
process.kill()
dst = open("stdout.log", "a")
dst_fd = dst.fileno()
stdout_fd = sys.stdout.fileno()
os.close(stdout_fd)
os.dup2(dst_fd, stdout_fd)