从外部应用程序记录日志

5
我正在编写一款研究工具,最近我从使用“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构建另一个文件处理程序?(也许通过重定向输出到记录器?)或者,鉴于当前的设置,这是不可能的吗?如果是后者,我该如何改进设置?
(哦,还有,如果日志记录是“实时”的话,那就太好了,这样从可执行文件接收到的消息就会被记录。)
感谢任何帮助! :)

1
Lennart Regebro的StreamLogger类在你的情况下会很好用。 - unutbu
谢谢提供参考!这对我的情况非常有效。 - Jon Kotker
2个回答

3

不要将stdout导入文件,而是可以将其导入一个PIPE,然后从该PIPE读取并写入日志记录器。类似以下代码:

proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.OUTPUT)
for line in proc.stdout:
    logging.info(line)

然而,还有一种更简单的方法:您必须使用带有文件句柄的类似文件的对象,但是您可以在管道上创建一个该对象,将每行传递给logging。您可以自己编写此对象,但正如@unutbu所说,在这个问题中,已经有人做了。因此:

with StreamLogger(logging.INFO) as out:
    proc = subprocess.call(command, stdout=out, stderr=subprocess.OUTPUT)

当然,你也可以暂时性地包装 stdout 以写入日志记录器,只需通过使用这个名称混淆相同的类来传递输出即可:

with StreamLogger('stdout'):
    proc = subprocess.call(command, stderr=subprocess.OUTPUT)

感谢您提供上一个链接中的代码参考。阅读起来很有趣。 - Jon Kotker

3

unutbu的评论很好,你应该看看Lennart的回答

你需要的是类似于tee功能的东西,但是subprocess模块在操作系统句柄级别工作,这意味着子进程写入的数据无法被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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接