Python 日志会刷新每个日志吗?

当我使用标准模块 伐木将日志写入文件时,是否将每个日志分别刷新到磁盘? 例如,下面的代码是否将日志刷新10倍?

logging.basicConfig(level=logging.DEBUG, filename='debug.log')
for i in xrange(10):
logging.debug("test")

如果是这样,它会减速吗?

36474 次浏览

是的,它确实在每次调用时刷新输出。你可以在 StreamHandler的源代码中看到这一点:

def flush(self):
"""
Flushes the stream.
"""
self.acquire()
try:
if self.stream and hasattr(self.stream, "flush"):
self.stream.flush()
finally:
self.release()


def emit(self, record):
"""
Emit a record.


If a formatter is specified, it is used to format the record.
The record is then written to the stream with a trailing newline.  If
exception information is present, it is formatted using
traceback.print_exception and appended to the stream.  If the stream
has an 'encoding' attribute, it is used to determine how to do the
output to the stream.
"""
try:
msg = self.format(record)
stream = self.stream
stream.write(msg)
stream.write(self.terminator)
self.flush()   # <---
except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)

我并不介意日志记录的性能,至少在分析并发现它是一个瓶颈之前是这样的。无论如何,您总是可以创建一个 Handler子类,它不会在每次调用 emit时执行 flush(即使如果发生错误异常/解释器崩溃,您可能会丢失大量日志)。

为了使用 缓冲区日志记录消息,并有条件地输出它们,可以使用 记忆处理程序来装饰目标处理程序(即 FileHandler 或 StreamHandler)。签名为 logging.handlers.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True),参数为 capacity,指定缓冲区大小(缓冲的记录数)。

file_handler = logging.FileHandler('test.log', mode='a')
memory_handler = MemoryHandler(capacity, flushLevel=logging.ERROR, target=file_handler, flushOnClose=True)
logger.addHandler(memory_handler)

你可以查看 MemoryHandler的源代码:

def shouldFlush(self, record):
"""
Check for buffer full or a record at the flushLevel or higher.
"""
return (len(self.buffer) >= self.capacity) or \
(record.levelno >= self.flushLevel)


def flush(self):
"""
For a MemoryHandler, flushing means just sending the buffered
records to the target, if there is one. Override if you want
different behaviour.


The record buffer is also cleared by this operation.
"""
self.acquire()
try:
if self.target:
for record in self.buffer:
self.target.handle(record)
self.buffer = []
finally:
self.release()


def close(self):
"""
Flush, if appropriately configured, set the target to None and lose the
buffer.
"""
try:
if self.flushOnClose:
self.flush()
finally:
self.acquire()
try:
self.target = None
BufferingHandler.close(self)
finally:
self.release()

有关更多细节,请参见 Python 日志烹饪书的相应部分