如何在Python中异步记录stdout / stderr?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Python中异步记录stdout / stderr?相关的知识,希望对你有一定的参考价值。

我有一个函数(我无法更改),它将数据打印到stdout / stderr。我需要记录这个函数的输出。我的第一个想法是用sys.stdout形式的缓冲区替换StringIO(),然后处理其内容。这很有效,但问题是,当函数失败时,它会输出错误消息并退出当前进程。在这种情况下,缓冲区的内容会丢失,因为函数调用后的代码永远不会执行。

所以我的想法是以某种方式异步地监视缓冲区并在有数据要读取时立即处理它的内容。我尝试使用asyncio及其add_reader方法的解决方案,但这似乎不支持StringIO(),甚至不支持常规文件。

这是我第一次尝试异步打印标准输出:

import asyncio
import sys
from io import StringIO

async def f():
    print('Some output')

def logger(buffer):
    sys.__stdout__.write(buffer.read())

buffer = StringIO()
sys.stdout = buffer

loop = asyncio.get_event_loop()
loop.add_reader(buffer, logger, buffer)
loop.run_until_complete(f())

那失败了

ValueError: Invalid file object: <_io.StringIO object at 0x7f8a93e9aa68>

有没有解决这个问题的方法?至少我需要澄清我的方法是否有意义。

更新:我发现了标准模块atexit,可以在解释器退出时调用函数。这是解决我问题的另一种方法。

答案

您可以创建io.TextIOBase的自定义子类,并将sys.stdout替换为您的自定义类的实例。只要输出发送到write(),就会调用类的sys.stdout方法。您可以选择将所有输出转发到原始标准输出:

class MyStdOut(io.TextIOBase):
    def __init__(self, orig_stdout=None):
        self.orig_stdout = orig_stdout
    def write(self, s):
        # Process output in whatever way you like
        process(s)
        # Write output to original stream, if desired
        if self.orig_stdout:
            self.orig_stdout.write(s)

sys.stdout = MyStdOut(sys.stdout)

这种方法将完全同步 - 无需线程或异步I / O.

以上是关于如何在Python中异步记录stdout / stderr?的主要内容,如果未能解决你的问题,请参考以下文章

如何异步获取子进程'stdout数据?

Python:在记录内存时获取子进程 STDOUT

如何在 PHP 中重新打开 STDOUT 以显示屏幕?

Dart 中的 stdout.write() 显然已从同步更改为异步,但我该如何使用它呢?

没有STDOUT的两个python脚本之间的进程间通信

如何使用日志记录 Python 模块写入文件?