当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何将sys.stdout复制到日志文件?》经验,为你挑选了10个好方法。

编辑:因为似乎没有解决方案,或者我正在做一些没人知道的非标准的东西 - 我会修改我的问题也要问:当python应用程序创建时,完成日志记录的最佳方法是什么很多系统调用?

我的应用程序有两种模式.在交互模式下,我希望所有输出都转到屏幕以及日志文件,包括来自任何系统调用的输出.在守护程序模式下,所有输出都将转到日志中.守护进程模式很好用os.dup2().我无法找到一种方法将所有输出"发送"到交互模式的日志,而无需修改每个系统调用.


换句话说,我想要命令行'tee'的功能,用于python应用程序生成的任何输出,包括系统调用输出.

澄清:

要重定向所有输出,我会做这样的事情,并且效果很好:

# open our log file
so = se = open("%s.log" % self.name, 'w', 0)

# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

关于这一点的好处是它不需要来自其余代码的特殊打印调用.该代码还运行一些shell命令,因此不必单独处理每个输出.

简单地说,我想做同样的事情,除了重复而不是重定向.

起初以为我认为简单地扭转它们dup2应该有效.为什么不呢?这是我的测试:

import os, sys

### my broken solution:
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"应与屏幕上显示的内容相同.



1> John T..:

我以前遇到过同样的问题,发现这个片段非常有用:

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()

来自:http://mail.python.org/pipermail/python-list/2007-May/438106.html


我要添加一个同花顺.例如:'self.file.flush()'
+1用于在内部处理sys.stdout重新分配,以便您可以通过删除Tee对象来结束日志记录
我不同意记录模块.非常适合一些摆弄.记录太大了.
请务必在此[后续](http://mail.python.org/pipermail/python-list/2007-May/460639.html)中注明答案中链接讨论的修订版本.
那样不行.直到执行结束才调用`__del__`.请参见http://stackoverflow.com/questions/6104535/i-dont-understand-this-python-del-behaviour
该解决方案将不会记录除print()和sys.stdout.write()之外的其他调用所生成的输出。由C代码或用Python封装的Fortran生成的输出将不会重定向到文件,因为它们直接写入stdout fd(1),而不是IOTextwrapper sys.stdout。@Jacob Gabrielson的方法可以解决这个问题,但是请查看他的答案的注释以修改其建议。

2> Triptych..:

print语句将调用write()您分配给sys.stdout的任何对象的方法.

我会一个小班,一次写两个地方......

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语句将回显到屏幕并附加到您的日志文件:

# prints "1 2" to  AND log.dat
print "%d %d" % (1,2)

这显然是快速而肮脏的.一些说明:

您可能应该对日志文件名进行参数化.

如果您不在程序的持续时间内进行日志记录,则应该将sys.stdout还原为.

您可能希望能够一次写入多个日志文件,或处理不同的日志级别等.

这些都很简单,我很乐意将它们作为练习者留给读者.这里的关键见解是print只调用分配给的"类文件对象" sys.stdout.


我太快就选择了这个答案.它适用于"打印",但不是外部命令输出.
你说`print语句将调用你赋给sys.stdout`的任何对象的write()方法.那么其他函数如何将数据发送到stdout而不使用`print`.例如,如果我使用`subprocess.call`创建一个进程,它的输出将转到控制台而不是`log.dat`文件...有没有办法解决这个问题?
Logger类还应定义flush()方法,例如"def flush():self.terminal.flush(); self.log.flush()"

3> Alexander Le..:

你真正想要的是logging来自标准库的模块.创建一个记录器并附加两个处理程序,一个将写入文件,另一个将写入stdout或stderr.

有关详细信息,请参阅 记录到多个目标


+1,没有重新发明轮子这样做
日志记录模块不会将异常和其他重要输出记录到stdout,这在分析构建服务器上的日志时很有用(例如).
`logging`模块不会重定向系统调用的输出,例如`os.write(1,b'stdout')`

4> Jacob Gabrie..:

由于您可以轻松地从代码中生成外部流程,因此您可以tee自行使用.我不知道任何Unix系统调用确切地做了什么tee.

# Note this version was written circa Python 2.6, see below for
# an updated 3.3+-compatible version.
import subprocess, os, sys

# Unbuffer output (this ensures the output is in the correct order)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

print "\nstdout"
print >>sys.stderr, "stderr"
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

您也可以tee使用多处理包进行模拟(或者如果您使用的是Python 2.5或更早版本,则使用处理).


嗯,这个答案有效,所以我会接受它.不过,这让我觉得很脏.
这不适用于非Unix系统,如Windows.
如果Python在我的一台机器上运行而解决方案没有,那么这不是一个pythonic解决方案.因此而被投票.
我刚刚发布了一个纯tethon(py2/3兼容)的python实现,可以在任何平台上运行,也可以用于不同的日志配置.http://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log-file-in-python/3423392#3423392
根据[this post](/sf/ask/17360801/#comment3235529_181654)行sys.stdout = os.fdopen(sys.stdout.fileno(),'w', 0)自python 3.3起不再有效(请参阅PEP 3116)

5> shx2..:

这是另一种解决方案,它比其他解决方案更通用 - 它支持将输出(写入sys.stdout)拆分为任意数量的类文件对象.没有要求__stdout__包含它本身.

import sys

class multifile(object):
    def __init__(self, files):
        self._files = files
    def __getattr__(self, attr, *args):
        return self._wrap(attr, *args)
    def _wrap(self, attr, *args):
        def g(*a, **kw):
            for f in self._files:
                res = getattr(f, attr, *args)(*a, **kw)
            return res
        return g

# for a tee-like behavior, use like this:
sys.stdout = multifile([ sys.stdout, open('myfile.txt', 'w') ])

# all these forms work:
print 'abc'
print >>sys.stdout, 'line2'
sys.stdout.write('line3\n')

注意:这是一个概念验证.这里的实现是不完整的,因为它仅包装方法的类文件对象(例如write),留出成员/属性/ SETATTR等.但是,它可能是对大多数人来说足够好,因为它目前为.

我喜欢它,比其共性外,就是它是干净的在这个意义上它没有任何直接调用write,flush,os.dup2,等.


我会__init__取*文件而不是文件,但不然,是的,这个.没有其他解决方案在没有尝试解决其他问题的情况下隔离了"tee"功能.如果要在输出的所有内容上添加前缀,可以将此类包装在前缀编写器类中.(如果你想在一个流上加上一个前缀,你可以将一个流包装并交给这个类.)这个还有一个优点,即multifile([])创建一个忽略所有内容的文件(比如open('/ dev) /空值')).

6> blokeley..:

如其他地方所述,也许最好的解决方案是直接使用日志记录模块:

import logging

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')
logging.info('this should to write to the log file')

但是,有一些(罕见的)你真的想要重定向stdout.当我扩展使用print的django的runserver命令时,我有这种情况:我不想破解django源但需要print语句转到文件.

这是一种使用日志记录模块将stdout和stderr重定向到远离shell的方法:

import logging, sys

class LogFile(object):
    """File-like object to log text using the `logging` module."""

    def __init__(self, name=None):
        self.logger = logging.getLogger(name)

    def write(self, msg, level=logging.INFO):
        self.logger.log(level, msg)

    def flush(self):
        for handler in self.logger.handlers:
            handler.flush()

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')

# Redirect stdout and stderr
sys.stdout = LogFile('stdout')
sys.stderr = LogFile('stderr')

print 'this should to write to the log file'

如果您真的无法直接使用日志记录模块,则应该只使用此LogFile实现.



7> sorin..:

tee()在Python中编写了一个适用于大多数情况的实现,它也适用于Windows.

https://github.com/pycontribs/tendo

此外,如果需要,您可以将它与loggingPython中的模块结合使用.



8> Atlas1j..:

(啊,只是重新阅读你的问题,看看这不太适用.)

这是一个使用python日志记录模块的示例程序.自2.3以来,此日志记录模块已在所有版本中.在此示例中,日志记录可通过命令行选项进行配置.

在完全模式下,它只会记录到文件,在正常模式下它将记录到文件和控制台.

import os
import sys
import logging
from optparse import OptionParser

def initialize_logging(options):
    """ Log information based upon users options"""

    logger = logging.getLogger('project')
    formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
    level = logging.__dict__.get(options.loglevel.upper(),logging.DEBUG)
    logger.setLevel(level)

    # Output logging information to screen
    if not options.quiet:
        hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)

    # Output logging information to file
    logfile = os.path.join(options.logdir, "project.log")
    if options.clean and os.path.isfile(logfile):
        os.remove(logfile)
    hdlr2 = logging.FileHandler(logfile)
    hdlr2.setFormatter(formatter)
    logger.addHandler(hdlr2)

    return logger

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    # Setup command line options
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("-l", "--logdir", dest="logdir", default=".", help="log DIRECTORY (default ./)")
    parser.add_option("-v", "--loglevel", dest="loglevel", default="debug", help="logging level (debug, info, error)")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="do not log to console")
    parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="remove old log file")

    # Process command line options
    (options, args) = parser.parse_args(argv)

    # Setup logger format and output locations
    logger = initialize_logging(options)

    # Examples
    logger.error("This is an error message.")
    logger.info("This is an info message.")
    logger.debug("This is a debug message.")

if __name__ == "__main__":
    sys.exit(main())



9> 小智..:

要完成John T的回答:https://stackoverflow.com/a/616686/395687

我添加__enter____exit__方法来使用它作为与上下文管理with的关键字,这给这个代码

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 __enter__(self):
        pass

    def __exit__(self, _type, _value, _traceback):
        pass

它可以用作

with Tee('outfile.log', 'w'):
    print('I am written to both stdout and outfile.log')



10> Status..:

我知道这个问题已经反复回答,但为此我从John T的答案中得到了主要答案,并对其进行了修改,使其包含建议的同花顺并遵循其链接的修订版本.我还添加了在cladmi的答案中提到的enter和exit,用于with语句.此外,文档提到使用os.fsync()这样刷新文件,所以我也添加了它.我不知道你是否真的需要它,但它在那里.

import sys, os

class Logger(object):
    "Lumberjack class - duplicates sys.stdout to a log file and it's okay"
    #source: /sf/ask/17360801/
    def __init__(self, filename="Red.Wood", mode="a", buff=0):
        self.stdout = sys.stdout
        self.file = open(filename, mode, buff)
        sys.stdout = self

    def __del__(self):
        self.close()

    def __enter__(self):
        pass

    def __exit__(self, *args):
        self.close()

    def write(self, message):
        self.stdout.write(message)
        self.file.write(message)

    def flush(self):
        self.stdout.flush()
        self.file.flush()
        os.fsync(self.file.fileno())

    def close(self):
        if self.stdout != None:
            sys.stdout = self.stdout
            self.stdout = None

        if self.file != None:
            self.file.close()
            self.file = None

然后你可以使用它

with Logger('My_best_girlie_by_my.side'):
    print("we'd sing sing sing")

要么

Log=Logger('Sleeps_all.night')
print('works all day')
Log.close()

推荐阅读
围脖上的博博_771
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有