将所有stdout / stderr全局重定向到记录器

时间:2018-01-31 18:19:33

标签: python python-2.7 logging stdout python-2.x

背景

我有一个非常大的python应用程序,它启动命令行实用程序来获取所需的数据。我目前只是将python启动程序脚本重定向到一个日志文件,该文件为我提供了所有print()输出,以及命令行实用程序的输出,即:

python -m launcher.py &> /root/out.log

问题

I've since implemented a proper logger via logging, which lets me format the logging statements more precisely, lets me limit log file size, etc.我通过调用记录器来换掉大部分print()语句。但是,我遇到了一个问题:命令行应用程序的输出都没有出现在我的日志中。它反而被转储到控制台。此外,这些程序并非都以相同的方式启动:有些是通过popen()推出的,有些是exec()推出的,有些是os.system()推出的等等。

问题

有没有办法将所有stdout / stderr文本全局重定向到我的日志记录功能,而无需重新编写/修改启动这些命令行工具的代码?我尝试在另一个问题中设置以下设置:

sys.stderr.write = lambda s: logger.error(s)

然而它失败,“sys.stderr.write是只读的”。

2 个答案:

答案 0 :(得分:1)

虽然这不是一个完整的答案,但它可能会向您显示重定向以适应您的特定情况。这是我一段时间做的。虽然我不记得为什么我这样做了,或者我试图规避的限制是什么,但以下是将stdoutstderr重定向到print()语句的类。该类随后写入屏幕并归档:

import os
import sys
import datetime

class DebugLogger():

    def __init__(self, filename):
        timestamp = datetime.datetime.strftime(datetime.datetime.utcnow(), 
                                               '%Y-%m-%d-%H-%M-%S-%f')
        #build up full path to filename
        logfile = os.path.join(os.path.dirname(sys.executable), 
                               filename + timestamp)
        self.terminal = sys.stdout
        self.log = open(logfile, 'a')

    def write(self, message):
        timestamp = datetime.datetime.strftime(datetime.datetime.utcnow(), 
                                               ' %Y-%m-%d-%H:%M:%S.%f')
        #write to screen
        self.terminal.write(message)
        #write to file
        self.log.write(timestamp + ' - ' + message)      
        self.flush()

    def flush(self):
        self.terminal.flush()
        self.log.flush()
        os.fsync(self.log.fileno())

    def close(self):
        self.log.close()


def main(debug = False):
    if debug:
        filename = 'blabla'
        sys.stdout = DebugLogger(filename)
        sys.stderr = sys.stdout
    print('test')

if __name__ == '__main__':
    main(debug = True)

答案 1 :(得分:1)

import sys
import io

class MyStream(io.IOBase):
    def write(self, s):
        logger.error(s)

sys.stderr = MyStream()

print('This is an error', stream=sys.stderr)

这使得对sys.stderr的所有呼叫都转到记录器。 原始的始终位于sys.__stderr__