【问题标题】:Redirect stdout to log file while still printing to stdout?将标准输出重定向到日志文件,同时仍打印到标准输出?
【发布时间】:2018-01-09 01:05:18
【问题描述】:

我想在 Python 脚本末尾捕获控制台输出。也就是说,我既想正常打印到控制台,又想在执行结束时将控制台输出保存到文件中。

我已经看到了各种相关的 SO 问题 123,尽管它们要么只是重定向输出而不显示它,要么使用 logging。从阅读loggingdoc 可以看出,您只能记录您编写的代码的输出。

以上所有链接的问题是您尚未编写的代码的控制台输出也打印到控制台。 我想要程序在执行结束时的整个控制台输出

我的第一直觉是这样的

logFile = open('LogFile.txt', 'w')
def print_log(msg):
    print(msg)
    logFile.write(msg)

print_log('Hello World!')

logFile.close()

但这仍然无法从其他正在使用的代码和库中捕获控制台输出。 有没有办法在执行结束时保存 python 脚本的控制台输出?我觉得应该有一种简单的方法来做到这一点,但我所有的研究都没有找到合适的解决方案。

【问题讨论】:

  • 您需要将 sys.stdout 替换为您自己的包装器,该包装器记录在那里写入的所有内容并传递到原始 sys.stdout。
  • @MaksymMarkov 是的,我认为这是真正的解决方案,这就是我在问题中使用代码 sn-p 所得到的。虽然我希望这样的东西已经编写并成熟,或者已经是 Python 的内置函数。

标签: python-3.x logging


【解决方案1】:

我在我的一个项目中使用了这个:

import io
import sys
from enum import Enum


class Tee(io.StringIO):
    class Source(Enum):
        STDOUT = 1
        STDERR = 2

    def __init__(self, clone=Source.STDOUT, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._clone = clone

        if clone == Tee.Source.STDOUT:
            self._out = sys.stdout
        elif clone == Tee.Source.STDERR:
            self._out = sys.stderr
        else:
            raise ValueError("Clone has to be STDOUT or STDERR.")

    def write(self, *args, **kwargs):
        self._out.write(*args, **kwargs)
        return super().write(*args, **kwargs)

    def __enter__(self):
        if self._clone == Tee.Source.STDOUT:
            sys.stdout = self
        else:
            sys.stderr = self
        self.seek(io.SEEK_END)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self._clone == Tee.Source.STDOUT:
            sys.stdout = self._out
        else:
            sys.stderr = self._out
        self.seek(0)
        return False

基本上,它完全按照 Maksym Markov 在评论中所说的那样做,但有一点不同。我通常不想停止任何输出,所以我写了这个Tee,它捕获了stdout(或stderr)上的所有文本,并立即将其打印并保存到缓冲区中以供以后使用。当代码退出 with 块时,它还会注意“修复”sys.stdout

使用示例:

if __name__ == "__main__":
    with Tee() as tee:
        print("Hello World!")

    print(tee.read())

有一些缺点,比如没有额外的代码你不能在 with 块内使用tee.read()。但就我而言,我总是需要处理整个块的输出。

【讨论】: