打印到屏幕并同时写入文件

Mat*_*w I 41 python

我在网上发现了一些通常有效的代码,但我想在同一个程序中多次使用它(将不同的东西写入不同的文件,同时仍然一直打印到屏幕上).

也就是说,当它关闭时,我认为sys.stdout关闭,所以打印完全,并再次使用这个类失败.我尝试重新导入sys和其他愚蠢的东西,但我无法让它工作.

这是该网站,代码为groups.google.com/group/comp.lang.python/browse_thread/thread/d25a9f5608e473af/

import sys

class MyWriter:

    def __init__(self, stdout, filename):
        self.stdout = stdout
        self.logfile = file(filename, 'a')

    def write(self, text):
        self.stdout.write(text)
        self.logfile.write(text)

    def close(self):
        self.stdout.close()
        self.logfile.close()

writer = MyWriter(sys.stdout, 'log.txt')
sys.stdout = writer

print 'test' 
Run Code Online (Sandbox Code Playgroud)

Esc*_*alo 124

你试图通过Python标准库很好地复制一些非常好的东西; 请检查记录模块.

使用此模块,您可以完全按照自己的意愿行事,但需要采用更简单,标准和可扩展的方式.您可以按以下步骤操作(此示例是来自日志记录菜谱的复制/粘贴):

假设您要在不同的情况下使用不同的消息格式登录到控制台和文件.假设您要将具有DEBUG及更高级别的消息记录到文件,并将那些级别为INFO及更高级别的消息记录到控制台.我们还假设该文件应包含时间戳,但控制台消息不应该包含时间戳.以下是如何实现这一目标:

import logging

# set up logging to file - see previous section for more details
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='/temp/myapp.log',
                    filemode='w')
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')

# Now, define a couple of other loggers which might represent areas in your
# application:

logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')

logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')
Run Code Online (Sandbox Code Playgroud)

当你运行它时,你会看到在控制台上

root        : INFO     Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO     How quickly daft jumping zebras vex.
myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR    The five boxing wizards jump quickly.
Run Code Online (Sandbox Code Playgroud)

在文件中你会看到类似的东西

10-22 22:19 root         INFO     Jackdaws love my big sphinx of quartz.
10-22 22:19 myapp.area1  DEBUG    Quick zephyrs blow, vexing daft Jim.
10-22 22:19 myapp.area1  INFO     How quickly daft jumping zebras vex.
10-22 22:19 myapp.area2  WARNING  Jail zesty vixen who grabbed pay from quack.
10-22 22:19 myapp.area2  ERROR    The five boxing wizards jump quickly.
Run Code Online (Sandbox Code Playgroud)

如您所见,DEBUG消息仅显示在文件中.其他消息将发送到两个目的地.

此示例使用控制台和文件处理程序,但您可以使用您选择的任何数量和组合的处理程序.

  • +1所有SO问题中的最佳答案,用于登录屏幕和文件 (5认同)
  • 我喜欢这个答案,但我不确定这是否是原始海报所要求的。 (2认同)

Ser*_*ndt 28

Python 3.3及以上版本易于使用

从Python 3.3开始,这样做变得非常容易,因为logging.basicConfig现在接受了这个handlers =参数.

import logging

level    = logging.INFO
format   = '  %(message)s'
handlers = [logging.FileHandler('filename.log'), logging.StreamHandler()]

logging.basicConfig(level = level, format = format, handlers = handlers)
logging.info('Hey, this is working!')
Run Code Online (Sandbox Code Playgroud)

但请注意,某些Python模块也可能将日志消息发布到该INFO级别.

这就是创建自定义日志记录级别的方法,例如OK,调用级别高于默认INFO级别5级,低于默认WARNING级别5 级.


Tho*_*mas 6

我知道这是一个老问题,最好的答案只是用于logging其预期目的,但我只是想指出,如果您只关心影响专门针对的调用print(而不是与 的其他交互sys.stdout),并且您只是想将几行粘贴到一些旧的一次性脚本中,没有什么可以阻止您简单地将名称重新分配给写入两个不同文件的不同函数,因为print是 Python 3+ 中的函数。你甚至可以(上帝禁止)使用带有链的 lambdaor来获得最快、最脏的解决方案:

old_print = print
log_file = open("logfile.log", "a")
print = lambda *args, **kw: old_print(*args, **kw) or old_print(*args, file=log_file, **kw)
print("Hello console and log file")
# ... more calls to print() ...
log_file.close()
Run Code Online (Sandbox Code Playgroud)

或者真正的一劳永逸:

import atexit
old_print = print
log_file = open("logfile.log", "a")
atexit.register(log_file.close)
print = lambda *args, **kw: old_print(*args, **kw) or old_print(*args, file=log_file, **kw)
# ... do calls to print(), and you don't even have to close the file afterwards ...
Run Code Online (Sandbox Code Playgroud)

假设程序正常退出,它可以正常工作,但请不要在生产代码中使用它,只需使用logging:)

编辑:如果您重视某种形式的结构并希望实时写入日志文件,请考虑以下内容:

from typing import Callable

def print_logger(
    old_print: Callable, 
    file_name: str,
) -> Callable:
    """Returns a function which calls `old_print` twice, specifying a `file=` on the second call.
    
    Arguments:
        old_print: The `print` function to call twice.
        file_name: The name to give the log file.
    """
    def log_print(*args, **kwargs):
        old_print(*args, **kwargs)
        with open(file_name, "a") as log_file:
            old_print(*args, file=log_file, **kwargs)
    return log_print
Run Code Online (Sandbox Code Playgroud)

然后调用如下:

print = print_logger(print, "logs/my_log.log")
Run Code Online (Sandbox Code Playgroud)