如何记录 Python 3 异常,但没有其堆栈跟踪?

Posted

技术标签:

【中文标题】如何记录 Python 3 异常,但没有其堆栈跟踪?【英文标题】:How to log a Python 3 exception, but without its stack trace? 【发布时间】:2021-10-02 13:40:55 【问题描述】:

当我想记录一些特定的异常,但要忽略它时,我可以这样做:

import logging

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError:
    logger.error("It failed with:", exc_info=True)

(这实际上是一个MRE,因为something_that_may_fail 尚未定义,所以try 块将引发NameError 并带有消息name 'something_that_may_fail' is not defined。??????)

不过,这也会记录堆栈跟踪:

It failed with:
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'something_that_may_fail' is not defined

有时这不是我想要的:在某些情况下,我已经知道异常类型和异常消息(连同我的自定义日志消息)就足够了,并且不想使用不支持的堆栈跟踪来扩展日志不要告诉我任何新的东西。所以我想要一个简单的日志条目

It failed with:
NameError: name 'something_that_may_fail' is not defined

我可以通过传递一个三元组作为exc_info 来实现这一点,并将堆栈跟踪组件替换为None

import logging
import sys

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError:
    exc_type, exc_value, _trace = sys.exc_info()
    logger.error("It failed with:", exc_info=(exc_type, exc_value, None))

但我不确定这有多可靠。 (The documentation 没有提到元组可能会或可能不会偏离 sys.exc_info() 返回的元组。)

自己检查异常

...
except NameError as e:
    ...

有它自己的问题:

f"type(e)" 给出字符串 &lt;class 'NameError'&gt; 而不仅仅是字符串 NameError 获得完全限定类型名称(包括包/模块但没有builtin.)的正确解决方案相当笨拙,并且不是我在异常处理代码中想要的东西。请参阅 the currently accepted answer 至 Get fully qualified class name of an object in Python。 我可以相信消息总是e.args[0] 吗? (除了NameError 之外,我可能还会使用其他异常(具有更多子类型),我在这里仅将其用作示例。)

那么在没有堆栈跟踪的情况下记录异常类型和消息的正确方法是什么?有没有比我上面的 make-the-trace-None hack 更清洁的方法?

【问题讨论】:

我可能没有完全理解这个问题,但是这个怎么样:logger.error(f"It failed with: e.__class__.__name__: e") 异常的内部结构不规范。您不能对任意异常做出太多假设,但如果您正在捕获特定的种类异常,您可以对特定类型的异常做出假设。 @tituszban e.__class__.__name__ 不会告诉我异常来自哪个模块。 (这可能很重要,例如,如果它来自第三方库。) @chepner,我正在寻找一种解决方案,当我不知道我可能在那里处理的所有类型的异常时也可以使用,例如在except Exception: 甚至except: 块中。 (是的,我知道通常不应该使用这些,尤其是后者。) 你可以使用e.__class__.__qualname__,而不是e.__class__.__name__,它也包含模块。 【参考方案1】:

traceback.format_exception_only 可用于:

import logging
import sys
import traceback

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError:
    exc_type, exc_value, _trace = sys.exc_info()
    exc_desc_lines = traceback.format_exception_only(exc_type, exc_value)
    exc_desc = ''.join(exc_desc_lines).rstrip()
    logger.error(f"It failed with:\nexc_desc")

或者没有sys:

import logging
import traceback

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError as e:
    exc_desc_lines = traceback.format_exception_only(type(e), e)
    exc_desc = ''.join(exc_desc_lines).rstrip()
    logger.error(f"It failed with:\nexc_desc")

(通过查看how the logging module actually extracts and formats information from exc_info 找到了这个。那里有traceback.print_exception is being used,所以我查看了traceback 模块中还有什么可用的。)

【讨论】:

以上是关于如何记录 Python 3 异常,但没有其堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Cloudera 中查看完整的异常/错误堆栈跟踪

Python:异常装饰器。如何保留堆栈跟踪

如何在python中获取嵌套异常的堆栈跟踪?

python中的堆栈跟踪和异常处理

AWS Lambda在Python 3.8中不显示原因 异常堆栈跟踪

来自未处理异常的 C++ 堆栈跟踪?