【问题标题】:getting the stack trace from within an Exception class从异常类中获取堆栈跟踪
【发布时间】:2021-12-03 11:10:05
【问题描述】:

我正在尝试使用自定义 Exception 类并在引发时将其堆栈跟踪作为电子邮件发送。但是__traceback__ 属性是空的。

class TaskFailure(Exception):
    def __init__(self, *args: object) -> None:
        super().__init__(*args)
        self.send_error_report()

    def send_error_report(self):
        # I want to access the stack trace here
        # to send it as email
        print(self.__traceback__) # None


raise TaskFailure("could not obtain template")

【问题讨论】:

  • 我会在这里问你,但你只期望你的自定义异常的堆栈跟踪吗?
  • 运行的时候没有traceback吗?我猜你会的。您试图在初始化中获取 __traceback__ 值,尽管根据我的理解,只有在您引发异常时才会存在回溯,而且这些都是不同的时刻。这将解释初始化时的空回溯,但之后的正确回溯
  • 没错,我在控制台中看到了回溯,但我试图在 Exception 类中访问它以便将其作为电子邮件发送。我猜基于 cmets/answer 是不可能的。

标签: python error-handling


【解决方案1】:

您正在尝试在初始化中获取__traceback__ 值,尽管只有在您引发异常并且那些是不同的时刻时才存在回溯。这样,您在初始化时有一个空回溯,但在 raise 之后有一个正确的回溯。

要查看它,您可以运行:

class TaskFailure(Exception):
    def __init__(self, *args: object) -> None:
        super().__init__(*args)
        self.send_error_report()

    def send_error_report(self):
        print(self.__traceback__)   # None


try:
    # code that might raise TaskFailure goes here
    raise TaskFailure("could not obtain template")
except TaskFailure as e:
    print(e.__traceback__)
    # code to send email
    raise    # if desired

这将打印None,然后是<traceback object at xxx>,证明在引发异常后回溯对象已绑定到实例。然后,要对回溯信息采取行动,您可以简单地编写一些东西来使用来自except 块的回溯信息。如果需要,您还可以在之后重新引发异常。

此外,如果您对作为字符串的回溯感兴趣,对于打印,您可以使用来自traceback 模块的print_tb,例如:

from traceback import print_tb

class TaskFailure(Exception):
 ...

try:
    # code that might raise TaskFailure goes here
    raise TaskFailure("could not obtain template")
except TaskFailure as e:
    print_tb(e.__traceback__)

您可以在official docs 中找到有关回溯模块的更多信息。

另一个重要的事情是异常实例通常不是显式可用的。当使用raise 时,它会在“幕后”执行许多操作,以便为程序提供一个很好的停止点(如果except 妥善处理,可以防止这种情况发生),带有回溯、上下文等。正如我们在official docs 的示例中所见,所有“用户定义”异常仅使用__init__ 方法。这是有道理的,因为我们不会手动创建异常的实例并使用它,我们将简单地将其用作raise 之后的表达式。所以我建议完全从类中删除该方法。然后,TaskFailure 将只剩下一个简单地调用父级 init 的初始化,这是不必要的,因此您可以将其删除:

from traceback import print_tb


def send_error_report(info):
    # send email with info
    pass


class TaskFailure(Exception):
    pass


try:
    # code that might raise TaskFailure goes here
    raise TaskFailure("could not obtain template")
except TaskFailure as e:
    print_tb(e.__traceback__)
    send_error_report("error data")
    # raise

【讨论】:

  • 我需要未处理异常才能使我的 celery 任务失败。至少这是计划。但实际上,我猜我也不需要踪迹。因为我知道我什么时候故意抛出错误。我可以在字符串中提供更多信息。
  • 抓到后也可以再举,没问题。编辑了答案以包含该内容
  • 是否有一个方法在引发时被调用?我了解初始化,如果它首先初始化但以后没有提出或提出,这可能会出现问题。异常是否知道何时引发?
  • 我认为没有。例外不是 raise 应用某些东西来实现特定行为的可调用对象。相反,raise'警告'解释器那里有一些可疑的东西,然后为所发生的事情提供上下文。来自docs,“raise 将第一个表达式计算为异常对象 [...]。异常的类型是异常实例的类,值是实例本身。”
  • Another piece of doc 这可能很有趣,我们有:“异常由类实例标识。根据实例的类选择 except 子句:它必须引用实例或其基类”。异常类从来没有明确的对象,所以它是“不知道的”,在某种程度上,它被提出了。但是,您可以说当自动设置 __context____cause__ 和/或 __traceback__ 时,异常“知道”它正在被引发
猜你喜欢
  • 1970-01-01
  • 2023-03-22
  • 2011-06-01
  • 2010-12-20
  • 2011-01-05
  • 2015-03-17
  • 1970-01-01
  • 1970-01-01
  • 2017-08-06
相关资源
最近更新 更多