我想暂时更改日志级别。
我目前的策略是使用模拟。
with mock.patch(...):
my_method_which_does_log()
方法中的所有logging.info()
调用都应该被忽略,并且不会记录到控制台。
如何实现...
以使级别INFO的日志被忽略?
代码是单进程和单线程,仅在测试期间执行。
答案 0 :(得分:4)
我想暂时更改日志级别。
无需嘲笑的方法是logging.disable
class TestSomething(unittest.TestCase):
def setUp(self):
logging.disable(logging.WARNING)
def tearDown(self):
logging.disable(logging.NOTSET)
此示例仅显示WARNING
类中每个测试的级别TestSomething
及更高级别的消息。 (您可以根据需要在每个测试的开始和结束时调用disable
。这看起来更清晰。)
要取消设置此临时限制,请致电logging.disable(logging.NOTSET)
:
如果调用
logging.disable(logging.NOTSET)
,它会有效地删除此覆盖级别,以便再次记录输出取决于各个记录器的有效级别。
答案 1 :(得分:3)
我认为嘲笑不会做你想做的事。记录器可能已在此方案中实例化,level
是每个记录器(以及每个记录器具有的任何处理程序)的实例变量。
您可以创建自定义上下文管理器。这看起来像这样:
import logging
class override_logging_level():
"A context manager for temporarily setting the logging level"
def __init__(self, level, process_handlers=True):
self.saved_level = {}
self.level = level
self.process_handlers = process_handlers
def __enter__(self):
# Save the root logger
self.save_logger('', logging.getLogger())
# Iterate over the other loggers
for name, logger in logging.Logger.manager.loggerDict.items():
self.save_logger(name, logger)
def __exit__(self, exception_type, exception_value, traceback):
# Restore the root logger
self.restore_logger('', logging.getLogger())
# Iterate over the loggers
for name, logger in logging.Logger.manager.loggerDict.items():
self.restore_logger(name, logger)
def save_logger(self, name, logger):
# Save off the level
self.saved_level[name] = logger.level
# Override the level
logger.setLevel(self.level)
if not self.process_handlers:
return
# Iterate over the handlers for this logger
for handler in logger.handlers:
# No reliable name. Just use the id of the object
self.saved_level[id(handler)] = handler.level
def restore_logger(self, name, logger):
# It's possible that some intervening code added one or more loggers...
if name not in self.saved_level:
return
# Restore the level for the logger
logger.setLevel(self.saved_level[name])
if not self.process_handlers:
return
# Iterate over the handlers for this logger
for handler in logger.handlers:
# Reconstruct the key for this handler
key = id(handler)
# Again, we could have possibly added more handlers
if key not in self.saved_level:
continue
# Restore the level for the handler
handler.setLevel(self.saved_level[key])
# Setup for basic logging
logging.basicConfig(level=logging.ERROR)
# Create some loggers - the root logger and a couple others
lr = logging.getLogger()
l1 = logging.getLogger('L1')
l2 = logging.getLogger('L2')
# Won't see this message due to the level
lr.info("lr - msg 1")
l1.info("l1 - msg 1")
l2.info("l2 - msg 1")
# Temporarily override the level
with override_logging_level(logging.INFO):
# Will see
lr.info("lr - msg 2")
l1.info("l1 - msg 2")
l2.info("l2 - msg 2")
# Won't see, again...
lr.info("lr - msg 3")
l1.info("l1 - msg 3")
l2.info("l2 - msg 3")
$ python ./main.py
INFO:root:lr - msg 2
INFO:L1:l1 - msg 2
INFO:L2:l2 - msg 2
logging.Logger.manager.loggerDict
是一个共享变量,由logging
代码中的锁保护。答案 2 :(得分:1)
使用@cryptoplex使用上下文管理器的方法,这里是official version from the logging cookbook:
import logging import sys class LoggingContext(object): def __init__(self, logger, level=None, handler=None, close=True): self.logger = logger self.level = level self.handler = handler self.close = close def __enter__(self): if self.level is not None: self.old_level = self.logger.level self.logger.setLevel(self.level) if self.handler: self.logger.addHandler(self.handler) def __exit__(self, et, ev, tb): if self.level is not None: self.logger.setLevel(self.old_level) if self.handler: self.logger.removeHandler(self.handler) if self.handler and self.close: self.handler.close() # implicit return of None => don't swallow exceptions
答案 3 :(得分:0)
您可以使用dependency injection将记录器实例传递给您正在测试的方法。虽然你稍微改变一下你的方法,但它更具侵略性,但是它给你更多的灵活性。
将logger参数添加到方法签名中,类似于:
def my_method( your_other_params, logger):
pass
在您的单元测试文件中:
if __name__ == "__main__":
# define the logger you want to use:
logging.basicConfig( stream=sys.stderr )
logging.getLogger( "MyTests.test_my_method" ).setLevel( logging.DEBUG )
...
def test_my_method(self):
test_logger = logging.getLogger( "MyTests.test_my_method" )
# pass your logger to your method
my_method(your_normal_parameters, test_logger)
python logger docs:https://docs.python.org/3/library/logging.html
答案 4 :(得分:0)
我使用此模式将所有日志写入列表。它会忽略INFO级或更小的日志。
logs=[]
import logging
def my_log(logger_self, level, *args, **kwargs):
if level>logging.INFO:
logs.append((args, kwargs))
with mock.patch('logging.Logger._log', my_log):
my_method_which_does_log()