【问题标题】:Logging from an External Application从外部应用程序记录
【发布时间】:2013-03-03 19:37:54
【问题描述】:

我正在编写一个研究工具,我最近从使用“打印”语句切换到使用 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 的文件构建另一个处理程序? (也许通过将输出重定向到记录器?)或者这是不可能的,给定当前的设置?如果是后者,我该如何改进设置?

(哦,另外,如果日志记录是“实时”的,那就太好了,这样来自可执行文件的消息就会在收到时记录下来。)

感谢您的帮助! :)

【问题讨论】:

  • Lennart Regebro 的 StreamLogger class 将在您的情况下很好地工作。
  • 感谢参考!这非常适合我的情况。

标签: python logging


【解决方案1】:

您可以将PIPE 传递给PIPE,而不是通过管道将stdout 传递给文件,然后从该PIPE 读取并写入记录器。像这样的:

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)

【讨论】:

  • 感谢最后一个链接中的代码参考。读起来很有趣。
【解决方案2】:

unutbu's comment 好;你应该看看Lennart's answer

您需要的是类似于 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    相关资源
    最近更新 更多