使用模拟获取所有日志记录输出 [英] Get all logging output with mock
问题描述
我想使用模拟获取所有日志记录输出.我搜索了,但是 只找到了显式模拟logging.info或logging.warn的方法.
I want to get all logging output with mock. I searched, but only found ways to mock explicitly logging.info or logging.warn.
无论设置了什么日志级别,我都需要所有输出.
I need all output, whatever logging level was set.
def test_foo():
def my_log(...):
logs.append(...)
with mock.patch('logging.???', my_log):
...
在我们的库中,我们使用以下代码:
In our libraries we use this:
import logging
logger=logging.getLogger(__name__)
def foo():
logger.info(...)
推荐答案
stdlib
从Python 3.4开始,电池的unittest
具有 assertLogs
.当不带logger
和level
参数使用时,它将捕获所有日志记录(禁止现有处理程序).您以后可以从上下文管理器的records
属性访问记录的条目.文本输出字符串存储在output
列表中.
stdlib
Since Python 3.4 the batteries' unittest
has assertLogs
. When used without logger
and level
arguments, it catches all logging (suppresses existing handlers). You can later access recorded entries from the context manager's records
attribute. Text output strings are stored in output
list.
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代码.实际上,它是比stdlib更为优雅的解决方案,因为ExpectLog
而不是几个类,只是普通的 logging.Filter
(一个类,
Tornado
For Python 2 I usually take Tornado's ExpectLog
. It's self-contained and works for normal Python code. It's actually more elegant solution then stdlib's, because instead of several class, ExpectLog
is just a normal logging.Filter
(a class, source). But it lacks a couple of features, including access to recorded entries, so usually I also extend it a bit, like:
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")
然后您可以拥有类似的内容:
Then you can have something like:
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
参数覆盖),并且每个感兴趣的记录器都需要一个过滤器.您可以按照此方法进行操作,并使其更适合您的情况.
However, note that for the filter approach current logging level is important (can be overridden with level
argument), and also you need a filter per logger of interest. You can follow the approach and make something that fits your case better.
或者,对于具有assertLogs
的Python 2,存在 unittest2 移植.
Alternatively there's unittest2 backport for Python 2 which has assertLogs
.
这篇关于使用模拟获取所有日志记录输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!