如何将 sys.stdout 复制到日志文件中?

16 浏览
0 Comments

如何将 sys.stdout 复制到日志文件中?

编辑:由于似乎没有解决方案,或者我正在做一些非常非标准的事情,没有人知道 - 我将修改我的问题,同时提问:当一个Python应用程序进行大量系统调用时,最佳的记录方式是什么?

我的应用程序有两种模式。在交互模式下,我希望所有输出都显示在屏幕上,并记录到日志文件中,包括来自任何系统调用的输出。在守护模式下,所有输出都记录到日志中。使用os.dup2(),守护模式运行得很好。但是我找不到在交互模式下将所有输出复制到日志中的方法,而不需要修改每个系统调用。

换句话说,我希望Python应用程序生成的任何输出,包括系统调用的输出,都具有类似于命令行'tee'的功能。

为了澄清:

要重定向所有输出,我做了类似这样的操作,效果很好:

# 打开日志文件
so = se = open("%s.log" % self.name, 'w', 0)
# 重新打开无缓冲的stdout
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# 将stdout和stderr重定向到上述打开的日志文件
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

这样做的好处是不需要从代码的其他部分进行特殊的打印调用。代码还运行了一些shell命令,因此不必单独处理每个命令的输出也很好。

简单来说,我想做同样的事情,只是复制而不是重定向。

一开始我认为只要简单地颠倒dup2的顺序就可以。为什么不行呢?以下是我的测试:

import os, sys
### 我的错误解决方案:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###
print("foo bar")
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

文件"a.log"应该与屏幕上显示的内容相同。

0
0 Comments

如何将sys.stdout复制到日志文件中?

问题的出现原因:print语句将调用分配给sys.stdout的任何对象的write()方法。

解决方法:

首先,可以创建一个Logger类来同时写入两个位置:

import sys
class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("log.dat", "a")
    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)  
sys.stdout = Logger()

然后,print语句将同时回显到屏幕和追加到日志文件中:

print "%d %d" % (1,2)

这只是一种简单粗暴的方法。还有一些需要注意的事项:

- 可能需要将日志文件名作为参数化。

- 如果在程序运行期间不会记录日志,可能需要将sys.stdout恢复为

- 可能需要能够同时写入多个日志文件,或处理不同的日志级别等。

这些都是非常直接的,我相信读者可以自行练习。关键的见解在于print语句只是调用分配给sys.stdout的“类似文件”的对象。

另外,当Logger被删除时,应该将stdout设置回默认值。

此外,Logger类还应该定义一个flush()方法,例如"def flush(): self.terminal.flush(); self.log.flush()"。

还有一个问题是,如果使用subprocess.call创建一个进程,其输出将会发送到控制台而不是log.dat文件,是否有办法解决这个问题?

对于这个问题,可以将stdout作为参数传入Logger类进行改进。

此外,还有一个问题是错误信息不会被写入日志文件,例如traceback.print_exc()。

这些问题都可以作为改进的方向。

0
0 Comments

如何将sys.stdout复制到日志文件中?

问题的出现原因:

- 需要将sys.stdout中的输出内容复制到一个日志文件中

解决方法:

- 创建一个名为Tee的类

- 在类的初始化函数中,打开一个文件,并将sys.stdout保存到另一个变量中

- 将sys.stdout重新指定为Tee类的实例

- 在类的析构函数中,将sys.stdout恢复到之前保存的值,并关闭文件

- 实现write函数,将数据写入文件并同时写入到sys.stdout

- 实现flush函数,刷新文件

- 使用Tee类来复制sys.stdout到日志文件中

代码如下:

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()
    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()

其他扩展:

- 可以将上述代码实现为contextmanager,以增加灵活性

- 在Python 3中,需要在write方法的末尾调用self.file.flush()来刷新输出

- 注意,该解决方法无法记录由print()和sys.stdout.write()以外的调用生成的输出。对于直接写入stdout fd (1)的C代码或包装在Python中的Fortran代码生成的输出,无法重定向到文件。Gabrielson提供的方法可以解决这个问题

- 注意,IO.write()返回整数,因此为了保持与stdout相同的API,最后一行应该返回self.stdout.write(data),而不是返回None

- 注意,如果没有显式调用,__del__方法将由垃圾回收器触发。对于不熟悉垃圾回收的人来说,使用contextManager可能更安全一些。

0
0 Comments

如何将sys.stdout复制到一个日志文件中?

问题出现的原因:

在这个问题中,用户希望将sys.stdout的输出内容复制到一个日志文件中。然而,他们并不熟悉如何实现这一点,所以他们寻求解决方案。

解决方法:

在这个问题中,有几种解决方法被提出来。首先,用户可以使用tee自身来完成这个任务。他们可以通过在代码中生成外部进程来调用tee命令,将sys.stdout的输出同时传递给日志文件和屏幕。这个方法使用了subprocess、os和sys等Python库来实现。

此外,用户还可以使用multiprocessing包来模拟tee命令的功能。这个包允许用户在不同的进程中执行任务,并且可以通过将输出重定向到日志文件来复制sys.stdout的内容。

最后,用户还可以在Python 3.3及以上版本中使用os.dup2()函数来实现这个任务。这个函数允许用户将一个文件描述符复制到另一个文件描述符,从而实现对sys.stdout的复制。

通过使用tee命令、multiprocessing包或os.dup2()函数,用户可以将sys.stdout的输出内容复制到一个日志文件中。这些方法提供了不同的实现方式,用户可以根据自己的需求选择最适合的方法。

0