同时在屏幕上打印和写入文件
我在网上找到了一些代码,基本上可以用,但我想在同一个程序里多次使用它(写不同的内容到不同的文件,同时还要一直在屏幕上打印信息)。
也就是说,当它关闭的时候,我觉得 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'
5 个回答
我知道这个问题已经很老了,最好的答案就是按照它的本意使用 logging
,但我想提一下,如果你只关心影响到 print
的调用(而不是其他与 sys.stdout
的交互),而且你只是想把几行代码粘贴到一些旧的临时脚本里,其实你可以直接把 print
这个名字重新指向一个不同的函数,这个新函数可以写入两个不同的文件,因为在 Python 3 及以上版本中,print
是一个函数。你甚至可以,天哪,使用一个带有 or
链的 lambda 表达式来实现最快、最简单的解决方案:
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()
或者如果你想要真正的“放手不管”:
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 ...
这样做是可以的,前提是程序能正常退出,但请大家不要在生产代码中使用这个,还是用 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
然后可以这样调用:
print = print_logger(print, "logs/my_log.log")
用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!')
不过要注意,有些Python模块可能也会发送INFO
级别的日志信息。
这时候,创建一个自定义的日志级别就很有用,比如可以叫它OK
,这个级别比默认的INFO
级别高5个级别,比默认的WARNING
级别低5个级别。
你正在尝试做一些事情,但其实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.')
当你运行这个代码时,在控制台上你会看到
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.
而在文件中你会看到类似这样的内容
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.
如你所见,DEBUG消息只会出现在文件中。其他消息则会同时发送到两个地方。
这个例子使用了控制台和文件处理器,但你可以根据需要使用任意数量和组合的处理器。