Python日志记录:禁用堆栈跟踪

时间:2019-02-09 11:18:40

标签: python traceback python-logging

是否有一种简单的方法可以在HandlerFormatter中禁用Python 3中的异常堆栈跟踪记录?

我需要在另一个Handler中进行堆栈跟踪,因此不能选择在exc_info=False的调用中设置Logger。除了定义自己的Formatter之外,还有其他更简单的方法吗?

1 个答案:

答案 0 :(得分:3)

禁用每个处理程序追溯输出的最简单选择是添加一个自定义logging.Filter subclass,它会更改记录对象(而不是过滤掉记录)。

过滤器只需将记录上的exc_text设置为字符串,即可替换None的默认值:

class SetTracebackCacheFilter(logging.Filter):
    """Set the exception cache text on log records to a specific value"""
    def __init__(self, exc_text):
        self.exc_text = exc_text
    def filter(self, record):
        record.exc_text = self.exc_text
        return True

add that filter on your handler,将缓存的文本设置为空字符串:

# do not display tracebacks in messages handled with this handler,
# by setting the traceback cache to an empty string:
handler_with_no_tracebacks.addFilter(SetTracebackCacheFilter(''))

之所以可行,是因为Formatter.format() method明确地将LogRecord.exc_text作为缓存格式化的回溯的属性的文档:

  

请注意,格式化的异常信息缓存在属性exc_text中。这很有用,因为可以对异常信息进行酸洗并通过网络发送,但是如果您有多个自定义异常信息格式的Formatter子类,则应小心。在这种情况下,您必须在格式化程序完成格式化后清除缓存的值,以便下一个处理事件的格式化程序不会使用缓存的值,而是重新进行计算。

上面的过滤器使用它来防止完全生成回溯文本。每次将消息传递给处理程序时,都会调用上述过滤器,以查看处理程序是否要处理记录实例,然后我们“缓存”一个空的异常文本。

但是,处理程序不会复制日志记录,并且以后传递了相同日志记录的任何其他处理程序也会忽略格式化回溯。因此,您还需要使用上述过滤器,直接在logger.handlers列表中配置下一个处理程序之后:

idx = logger.handlers.index(handler_with_no_tracebacks)
if len(logger.handlers) >= idx:
    # clear the traceback text cache again for further handlers
    logger.handlers[idx + 1].addFilter(SetTracebackCacheFilter(None))

如果您想在任何地方禁用所有追溯输出,那么向所有处理程序或记录器添加自定义过滤器可能会变得很乏味。在这种情况下,另一个选择是向logging.setLogRecordFactory() function注册自定义记录工厂;只需将记录上的exc_text属性设置为一个空字符串即可:

record_factory = logging.getLogRecordFactory()

def clear_exc_text(*args, **kwargs):
    record = record_factory(*args, **kwargs)
    record.exc_text = ''
    return record

logging.setLogRecordFactory(clear_exc_text)

请注意,默认工厂仅为logging.LogRecord class,但以上功能可与任何已设置的自定义工厂一起使用。

过滤器和自定义记录工厂都没有清除exc_info元组,因此您仍然可以在其他地方访问它。

当然,您还可以创建自己的Handler子类,其中Handler.handle()设置并清除exc_text属性:

class NoTracebackHandler(logging.Handler):
    def handle(self, record):
        old, record.exc_text = record.exc_text, ''
        try:
            super().handle(record)
        finally:
            record.exc_text = old