标准库
从 Python 3.4 开始,电池的 unittest 具有 assertLogs。当不使用 logger 和 level 参数时,它会捕获所有日志记录(抑制现有处理程序)。您可以稍后从上下文管理器的records 属性访问记录的条目。文本输出字符串存储在output 列表中。
import logging
import unittest
class TestLogging(unittest.TestCase):
def test(self):
with self.assertLogs() as ctx:
logging.getLogger('foo').info('message from foo')
logging.getLogger('bar').info('message from bar')
print(ctx.records)
龙卷风
对于 Python 2,我通常使用 Tornado 的 ExpectLog。它是独立的,适用于普通的 Python 代码。它实际上是比标准库更优雅的解决方案,因为ExpectLog 不是几个类,而是一个普通的logging.Filter(一个类,source)。但是它缺少一些功能,包括访问记录的条目,所以通常我也会对其进行一些扩展,例如:
class ExpectLog(logging.Filter):
def __init__(self, logger, regex, required=True, level=None):
if isinstance(logger, basestring):
logger = logging.getLogger(logger)
self.logger = logger
self.orig_level = self.logger.level
self.level = level
self.regex = re.compile(regex)
self.formatter = logging.Formatter()
self.required = required
self.matched = []
self.logged_stack = False
def filter(self, record):
if record.exc_info:
self.logged_stack = True
message = self.formatter.format(record)
if self.regex.search(message):
self.matched.append(record)
return False
return True
def __enter__(self):
self.logger.addFilter(self)
if self.level:
self.logger.setLevel(self.level)
return self
def __exit__(self, typ, value, tb):
self.logger.removeFilter(self)
if self.level:
self.logger.setLevel(self.orig_level)
if not typ and self.required and not self.matched:
raise Exception("did not get expected log message")
然后你可以有类似的东西:
class TestLogging(unittest.TestCase):
def testTornadoself):
logging.basicConfig(level = logging.INFO)
with ExpectLog('foo', '.*', required = False) as ctxFoo:
with ExpectLog('bar', '.*', required = False) as ctxBar:
logging.getLogger('foo').info('message from foo')
logging.getLogger('bar').info('message from bar')
print(ctxFoo.matched)
print(ctxBar.matched)
但是,请注意,对于过滤器方法,当前日志记录级别很重要(可以用 level 参数覆盖),并且您还需要为每个感兴趣的记录器设置一个过滤器。您可以按照该方法制作更适合您情况的内容。
更新
另外还有一个用于 Python 2 的 unittest2 反向端口,它具有 assertLogs。