我正在编写一个研究工具,我最近从使用“print”语句切换到使用Python内置的记录器功能。我推断,这将允许我为用户提供将输出转储到文件的选项,除了将其转储到屏幕上。
到目前为止一切顺利。我在Python中的代码部分使用“logger.info”和“logger.error”转储到屏幕和文件。 “logger”是模块范围的记录器。这部分就像一个魅力。
但是,在几个点上,我使用“subprocess.call”通过shell运行可执行文件。所以,在整个代码中,我有像
这样的行proc = subprocess.call(command)
此命令的输出将一如既往地打印到屏幕上,但不会转储到用户指定的文件中。
一种可能的选择是打开文件管道:
proc = subprocess.call(command, stdout=f, stderr=subprocess.OUTPUT)
但那只会转储到文件而不是屏幕。
基本上,我的问题归结为:有没有办法可以利用我现有的记录器,而不必为subprocess.call专门构建文件的另一个处理程序? (也许通过将输出重定向到记录器?)或者这是不可能的,考虑到当前的设置?如果是后者,我该如何改进设置?
(哦,如果日志记录是“实时”的话会很棒,这样来自可执行文件的消息会在收到时记录下来。)
感谢您的帮助! :)
答案 0 :(得分:3)
不是将stdout
传递给文件,而是将其传递给PIPE
,然后从该PIPE读取并写入logger。像这样:
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.OUTPUT)
for line in proc.stdout:
logging.info(line)
但是,有一个更简单的答案:您必须使用带文件句柄的类文件对象,但您可以在管道上创建一个将每行传递到logging
的管道。你可以自己写这个对象,但是,正如@unutbu所说,有人已经在this question完成了它。所以:
with StreamLogger(logging.INFO) as out:
proc = subprocess.call(command, stdout=out, stderr=subprocess.OUTPUT)
当然,你也可以暂时将stdout
包裹起来写入记录器,然后通过例如this confusingly identically-named class传递输出:
with StreamLogger('stdout'):
proc = subprocess.call(command, stderr=subprocess.OUTPUT)
答案 1 :(得分:3)
unutbu
's comment很好;你应该看看Lennart's answer。
您需要的是类似tee
的功能,但subprocess
模块在OS句柄级别工作,这意味着您的Python无法看到子进程写入的数据代码,比如你写的一些类似文件的对象,记录并打印写入的任何内容。
除了使用Lennart的答案之外,你可以使用像sarge
这样的第三方库来做这种事情(披露:我是它的维护者)。它不仅适用于日志记录。假设您有一个生成输出的程序,例如:
# echotest.py
import time
for i in range(10):
print('Message %d' % (i + 1))
并且您希望在脚本中捕获它,记录并将其打印到屏幕:
#subptest.py
from sarge import capture_stdout
import logging
import sys
logging.basicConfig(filename='subptest.log', filemode='w',
level=logging.INFO)
p = capture_stdout('python echotest.py', async=True)
while True:
line = p.stdout.readline()
line = line.strip()
# depending on how the child process generates output,
# sometimes you won't see anything for a bit. Hence only print and log
# if you get something
if line:
print(line)
logging.info(line)
# Check to see when we can stop - after the child is done.
# The return code will be set to the value of the child's exit code,
# so it won't be None any more.
rc = p.commands[0].process.poll()
# if no more output and subprocess is done, break
if not line and rc is not None:
break
如果您运行上述脚本,则会打印到控制台:
$ python subptest.py
Message 1
Message 2
Message 3
Message 4
Message 5
Message 6
Message 7
Message 8
Message 9
Message 10
当我们检查日志文件时,我们看到:
$ cat subptest.log
INFO:root:Message 1
INFO:root:Message 2
INFO:root:Message 3
INFO:root:Message 4
INFO:root:Message 5
INFO:root:Message 6
INFO:root:Message 7
INFO:root:Message 8
INFO:root:Message 9
INFO:root:Message 10