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

12

有没有一种简单的方法可以在Python 3中禁用异常堆栈跟踪的日志记录,无论是在Handler还是Formatter中?

我需要在另一个Handler中使用堆栈跟踪,因此在调用Logger时设置exc_info=False不是一个选项。除了定义自己的Formatter之外,是否有更简单的方法?

4个回答

14

禁用每个处理程序的回溯输出最简单的方法是添加一个自定义的logging.Filter子类,该子类修改记录对象(而不是过滤记录)。

过滤器只需将记录上的exc_info设置为None即可:

class TracebackInfoFilter(logging.Filter):
    """Clear or restore the exception on log records"""
    def __init__(self, clear=True):
        self.clear = clear
    def filter(self, record):
        if self.clear:
            record._exc_info_hidden, record.exc_info = record.exc_info, None
            # clear the exception traceback text cache, if created.
            record.exc_text = None
        elif hasattr(record, "_exc_info_hidden"):
            record.exc_info = record._exc_info_hidden
            del record._exc_info_hidden
        return True

并且在你的处理程序上添加该过滤器

# do not display tracebacks in messages handled with this handler,
# by setting the traceback cache to a non-empty string:
handler_with_no_tracebacks.addFilter(TracebackInfoFilter())

然而,处理程序不会复制日志记录,后续传递相同日志记录的任何其他处理程序也将忽略格式化回溯信息。因此,您还需要配置任何其他处理程序以再次恢复信息:
for handler in logger.handlers:
    if not any(isinstance(f, TracebackInfoFilter) for f in handler.filters):
        handler.addFilter(TracebackInfoFilter(clear=False))

如果有人想要在所有地方禁用所有的回溯输出,那么向所有处理程序或记录器添加自定义过滤器可能会变得繁琐。在这种情况下,另一种选择是使用logging.setLogRecordFactory()函数注册自定义记录工厂;只需无条件地将记录的exc_info属性设置为None即可。
record_factory = logging.getLogRecordFactory()

def clear_exc_text(*args, **kwargs):
    record = record_factory(*args, **kwargs)
    record.exc_info = None
    return record

logging.setLogRecordFactory(clear_exc_text)

请注意,logging.LogRecord是默认的工厂,但上述函数将尽力与任何已设置的自定义工厂配合使用。
当然,您也可以创建自己的Handler子类,在Handler.handle()中设置和清除exc_info属性。
class NoTracebackHandler(logging.Handler):
    def handle(self, record):
        info, cache = record.exc_info, record.exc_text
        record.exc_info, record.exc_text = None, None
        try:
            super().handle(record)
        finally:
            record.exc_info = info
            record.exc_text = cache

1
我使用了两个处理程序,一个用于控制台,另一个用于文件。我不想在控制台上输出堆栈跟踪,所以我采用了上述方法,但是堆栈跟踪会同时打印在控制台和文件中。以下是代码:"""""""fileHandler = logging.FileHandler("{0}/{1}".format(logPath, fileName)) fileHandler.setFormatter(logFormatter) Logger.addHandler(fileHandler)consoleHandler = logging.StreamHandler() consoleHandler.setFormatter(logFormatter) consoleHandler.addFilter(SetTracebackCacheFilter('')) Logger.addHandler(consoleHandler)"""""""" - charanReddy
@ragireddysricharanreddy:我知道出了什么问题了,一个空字符串仍然被视为一个空缓存。我会重新修改这个答案。 - Martijn Pieters
我解决了这个问题......我们需要在筛选函数中添加 record.exc_info = False。 - charanReddy
1
@ragireddysricharanreddy:我将其设置为“None”。请注意,如果稍后运行其他处理程序,这将影响它们。 - Martijn Pieters
不错的解决方案,但需要注意一点(这并非@Martijn Pieters的错)。如果您的处理程序位于QueueHandler后面,则必须覆盖QueueHandler.prepare()函数,以便它只返回记录。否则,它会修改记录,使得此解决方案无法正常工作。 - Martin Riddar

2

这项工作甚至可以比 Digoya的回答 更容易实现:

class NoTracebackFormatter(logging.Formatter):
    def formatException(self, ei):
        return ""
    def formatStack(self, stack_info):
        return ""

(测试于Python 3.9)

这很简单明了,但它也意味着异常的类型和参数都不会被输出,所以我在此基础上进行了改进。https://stackoverflow.com/a/77581890/1450294 - undefined

0

Johothon的回答很好而且直接,但它排除了所有的异常信息,而不仅仅是堆栈。异常的类型和参数对于开发运维人员和最终用户都是有用的,所以我修改了他们的代码以包含这些信息:

class NoTracebackFormatter(logging.Formatter):
    def formatException(self, exc_info):
        return '%s: %s' % (exc_info[0].__name__, exc_info[1])
    def formatStack(self, stack_info):
        return ""

然后只需要 handler.setFormatter(NoTracebackFormatter())
这在 Python 3.11 中对我来说效果很好。

0

回复晚了,但对其他人可能有用。您不需要烦恼 HandlerFilter。您只需要继承自己的 Formatter 并跳过格式化异常的部分。这是我使用的代码片段:

class EnhancedFormatter(logging.Formatter):
    def __init__(self, fmt=None, datefmt=None, style='%', validate=True, skip_traceback=False):
        self.skip_traceback = skip_traceback
        super(EnhancedFormatter, self).__init__(fmt, datefmt, style, validate)

    def format(self, record) -> str:
        record.message = record.getMessage()
        if self.usesTime():
            record.asctime = self.formatTime(record, self.datefmt)
        s = self.formatMessage(record)
        if not self.skip_traceback: # check here do you need to format traceback
            if record.exc_info:
                if not record.exc_text:
                    record.exc_text = self.formatException(record.exc_info)
            if record.exc_text:
                if s[-1:] != "\n":
                    s = s + "\n"
                s = s + record.exc_text
            if record.stack_info:
                if s[-1:] != "\n":
                    s = s + "\n"
                s = s + self.formatStack(record.stack_info)
        return s

只需在实例化格式化程序类时设置skip_traceback参数,然后使用它来确定是否需要格式化回溯信息。


在现实世界的情境中,你会如何使用这个函数?我是Python的新手,正在研究异常处理,这个函数看起来非常适合。举个例子,如果我想在传递的文件不存在时引发一个错误,我该如何使用你的函数?我希望隐藏堆栈跟踪并只输出我的错误。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接