如何设置 Python 登录单元测试
How setup Python logging in unit tests
如何在 Python 包和支持的单元测试中设置日志记录,以便我得到一个日志文件,以便在出现问题时查看?
目前包日志记录似乎被 nose/unittest 捕获,如果测试失败则抛出到控制台;只有单元测试日志才能将其写入文件。
在包和单元测试源文件中,我目前正在使用以下方法获取记录器:
import logging
import package_under_test
log = logging.getLogger(__name__)
在单元测试脚本中,我一直在尝试使用基本的 FileHandler 设置日志处理程序,直接内联或通过 setUp()/setUpClass() TestCase 方法
以及日志记录配置,当前在单元测试脚本 setUp() 方法中设置。
root, ext = os.path.splitext(__file__)
log_filename = root + '.log'
log_format = (
'%(asctime)8.8s %(filename)-12.12s %(lineno)5.5s:'
' %(funcName)-32.32s %(message)s')
datefmt = "%H:%M:%S"
log_fmt = logging.Formatter(log_format, datefmt)
log_handler = logging.FileHandler(log_filename, mode='w')
log_handler.setFormatter(log_fmt)
log.addHandler(log_handler)
log_format = '%(message)s'
log.setLevel(logging.DEBUG)
log.debug('test logging enabled: %s' % log_filename)
最后一行中的日志确实在文件中结束,但此配置显然不会过滤回正在测试的导入包中。
记录对象在层次结构中运行,记录消息 'bubble up' 层次结构链并沿途传递给任何处理程序(前提是消息的日志级别处于或超过记录器的最小阈值您正在登录的对象)。忽略过滤和全局日志级别配置,在伪代码中是这样的:
if record.level < current_logger.level:
return
for logger_object in (current_logger + current_logger.parents_reversed):
for handler in logger_object.handlers:
if record.level >= handler.level:
handler.handle(record)
if not logger_object.propagate:
# propagation disabled, the buck stops here.
break
处理程序实际上负责将日志消息放入文件或将其写入控制台等。
您遇到的问题是您将日志处理程序添加到 __name__
记录器,其中 __name__
是当前包标识符。包名中的 .
分隔符是层级分隔符,所以如果你 运行 这个在 acme.tests
中,那么 只有 acme.tests
中的记录器和包含模块 正在发送到此处理程序。 acme.tests
之外的任何代码都不会到达这些处理程序。
您的日志对象层次结构与此类似:
- acme
- frobnars
- tests
# logger object
- test1
- test2
- widgets
那么只有 test1
和 test2
中的日志对象会看到相同的处理程序。
您可以使用 logging.root
或 logger.getLogger()
(没有名称参数或名称设置为 None
)将您的记录器移动到根记录器。所有记录器都是根记录器的子节点,只要它们不将 propagate
attribute 设置为 False
,日志消息就会到达根处理程序。
其他选项是使用 logging.getLogger('acme')
显式获取 acme
记录器对象,或者始终在整个代码中使用一个单独的显式记录器名称,该名称在您的测试和您的代码中是相同的图书馆。
如何在 Python 包和支持的单元测试中设置日志记录,以便我得到一个日志文件,以便在出现问题时查看?
目前包日志记录似乎被 nose/unittest 捕获,如果测试失败则抛出到控制台;只有单元测试日志才能将其写入文件。
在包和单元测试源文件中,我目前正在使用以下方法获取记录器:
import logging
import package_under_test
log = logging.getLogger(__name__)
在单元测试脚本中,我一直在尝试使用基本的 FileHandler 设置日志处理程序,直接内联或通过 setUp()/setUpClass() TestCase 方法
以及日志记录配置,当前在单元测试脚本 setUp() 方法中设置。
root, ext = os.path.splitext(__file__)
log_filename = root + '.log'
log_format = (
'%(asctime)8.8s %(filename)-12.12s %(lineno)5.5s:'
' %(funcName)-32.32s %(message)s')
datefmt = "%H:%M:%S"
log_fmt = logging.Formatter(log_format, datefmt)
log_handler = logging.FileHandler(log_filename, mode='w')
log_handler.setFormatter(log_fmt)
log.addHandler(log_handler)
log_format = '%(message)s'
log.setLevel(logging.DEBUG)
log.debug('test logging enabled: %s' % log_filename)
最后一行中的日志确实在文件中结束,但此配置显然不会过滤回正在测试的导入包中。
记录对象在层次结构中运行,记录消息 'bubble up' 层次结构链并沿途传递给任何处理程序(前提是消息的日志级别处于或超过记录器的最小阈值您正在登录的对象)。忽略过滤和全局日志级别配置,在伪代码中是这样的:
if record.level < current_logger.level:
return
for logger_object in (current_logger + current_logger.parents_reversed):
for handler in logger_object.handlers:
if record.level >= handler.level:
handler.handle(record)
if not logger_object.propagate:
# propagation disabled, the buck stops here.
break
处理程序实际上负责将日志消息放入文件或将其写入控制台等。
您遇到的问题是您将日志处理程序添加到 __name__
记录器,其中 __name__
是当前包标识符。包名中的 .
分隔符是层级分隔符,所以如果你 运行 这个在 acme.tests
中,那么 只有 acme.tests
中的记录器和包含模块 正在发送到此处理程序。 acme.tests
之外的任何代码都不会到达这些处理程序。
您的日志对象层次结构与此类似:
- acme
- frobnars
- tests
# logger object
- test1
- test2
- widgets
那么只有 test1
和 test2
中的日志对象会看到相同的处理程序。
您可以使用 logging.root
或 logger.getLogger()
(没有名称参数或名称设置为 None
)将您的记录器移动到根记录器。所有记录器都是根记录器的子节点,只要它们不将 propagate
attribute 设置为 False
,日志消息就会到达根处理程序。
其他选项是使用 logging.getLogger('acme')
显式获取 acme
记录器对象,或者始终在整个代码中使用一个单独的显式记录器名称,该名称在您的测试和您的代码中是相同的图书馆。