如何在不停止程序的情况下打印完整的回溯?

chr*_*ley 696 python exception-handling

我正在编写一个程序来解析10个网站,查找数据文件,保存文件,然后解析它们以生成可以在NumPy库中使用的数据.有的错误该文件通过遇到不良链接,不好的XML,缺项,其他的事情我还没有进行分类.我最初制作这个程序来处理这样的错误:

try:
    do_stuff()
except:
    pass
Run Code Online (Sandbox Code Playgroud)

但现在我想记录错误:

try:
    do_stuff()
except Exception, err:
    print Exception, err
Run Code Online (Sandbox Code Playgroud)

请注意,这是打印到日志文件以供以后查看.这通常会打印非常无用的数据.我想要的是打印错误触发时打印的完全相同的行,没有try-except拦截异常,但我不希望它暂停我的程序,因为它嵌套在一系列for循环中,我想看完成了.

vol*_*ing 661

traceback.format_exc()或者sys.exc_info()会产生更多信息,如果这是你想要的.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])
Run Code Online (Sandbox Code Playgroud)

  • `print(traceback.format_exc())` 比 `traceback.print_tb(exc.__traceback__)` 更好。`print(sys.exc_info())` 返回整个元组,看起来像 `(<class 'UnicodeDecodeError'>, UnicodeDecodeError('utf-8', b'\x81', 0, 1, '无效起始字节') , <traceback object at 0x7f179d64ae00>)` 因此,`traceback.format_exc()` 确实非常优越,因为它会打印 `Traceback (most最近的调用最后): File "<ipython-input-15-9e3d6e01ef04>", line 2, in <module> b"\x81".decode() UnicodeDecodeError: 'utf-8' 编解码器无法解码位置 0 中的字节 0x81: 无效的起始字节` (11认同)
  • 删除它不起作用的 `exc_info()` 。 (8认同)
  • 不要使用ex...回溯包含所有信息/sf/ask/319519161/圣#4564595 (6认同)
  • `print(sys.exc_info()[0]` 打印 `<class 'Exception'>`。 (4认同)
  • 这个答案应该包含有关这些功能的更多详细信息。对于 `traceback.format_exc()`,它只是返回一个格式化字符串,与您在 stdout 中看到的完全一样。这正是我所需要的,也是很多人想要的。 (4认同)
  • `print(sys.exc_info()[2])` 产生 `<traceback object at 0x0000028A79E6B2C8>`。 (3认同)
  • 对于那些希望格式化字符串输出返回给某个客户端的人,这个答案帮助了我:/sf/answers/3450276651/ (2认同)

Syl*_*oux 515

其他一些答案已经指出了追溯模块.

请注意print_exc,在某些角落的情况下,您将无法获得您期望的结果.在Python 2.x中:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()
Run Code Online (Sandbox Code Playgroud)

...将显示最后一个异常的回溯:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!
Run Code Online (Sandbox Code Playgroud)

如果您确实需要访问原始回溯,一个解决方案是缓存从局部变量返回的异常信息exc_info并使用print_exception以下方式显示它:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info
Run Code Online (Sandbox Code Playgroud)

生产:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!
Run Code Online (Sandbox Code Playgroud)

尽管如此,很少有陷阱:

  • 来自以下文件sys_info:

    将回溯返回值分配给处理异常的函数中的局部变量将导致循环引用.这将阻止同一函数中的局部变量或回溯引用的任何内容被垃圾回收.[...] 如果你确实需要回溯,请确保在使用后删除它(最好用try ... finally语句完成)

  • 但是,来自同一个文档:

    从Python 2.2开始,当启用垃圾收集并且它们变得无法访问时,这些周期会自动回收,但是避免创建周期仍然更有效.


另一方面,通过允许您访问异常关联的回溯,Python 3产生了一个不太令人惊讶的结果:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)
Run Code Online (Sandbox Code Playgroud)

...将显示:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
Run Code Online (Sandbox Code Playgroud)


dim*_*414 223

如果您正在调试并且只想查看当前堆栈跟踪,则只需调用:

traceback.print_stack()

没有必要手动引发异常只是为了再次捕获它.

  • 回溯模块正是这样做 - 引发并捕获异常. (8认同)
  • BTW 默认输出到 STDERR。没有出现在我的日志中,因为它被重定向到其他地方。 (6认同)

Aar*_*all 86

如何在不停止程序的情况下打印完整的回溯?

如果您不想在出错时暂停程序,则需要使用try/except来处理该错误:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)
Run Code Online (Sandbox Code Playgroud)

要提取完整的回溯,我们将使用traceback标准库中的模块:

import traceback
Run Code Online (Sandbox Code Playgroud)

并创建一个相当复杂的堆栈跟踪来演示我们获得完整的堆栈跟踪:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()
Run Code Online (Sandbox Code Playgroud)

印花

打印完整的回溯,请使用以下traceback.print_exc方法:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()
Run Code Online (Sandbox Code Playgroud)

哪个印刷品:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Run Code Online (Sandbox Code Playgroud)

优于打印,记录:

但是,最佳做法是为模块设置记录器.它将知道模块的名称并能够更改级别(以及其他属性,例如处理程序)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将需要该logger.exception功能:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)
Run Code Online (Sandbox Code Playgroud)

哪些日志:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Run Code Online (Sandbox Code Playgroud)

或者你可能只想要字符串,在这种情况下,你会想要这个traceback.format_exc函数:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())
Run Code Online (Sandbox Code Playgroud)

哪些日志:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Run Code Online (Sandbox Code Playgroud)

结论

对于所有三个选项,我们看到输出与出错时相同:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Run Code Online (Sandbox Code Playgroud)

  • 如上所述,对我来说,`traceback.print_exc()`只返回最后一次调用:你如何成功返回堆栈的几个级别(可能所有的levele s?) (2认同)
  • @geekobi 的意思是,如果你捕获并重新加注,traceback.print_exc() 将只返回重新加注堆栈,而不是原始堆栈。 (2认同)

sou*_*ole 23

在 python3(适用于 3.9)中,我们可以定义一个函数,并可以在我们想要打印详细信息的地方使用它。

import traceback

def get_traceback(e):
    lines = traceback.format_exception(type(e), e, e.__traceback__)
    return ''.join(lines)

try:
    1/0
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

try:
    spam(1,2)
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')
Run Code Online (Sandbox Code Playgroud)

输出如下:

bash-3.2$ python3 /Users/soumyabratakole/PycharmProjects/pythonProject/main.py
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 26, in <module>
    1/0
ZeroDivisionError: division by zero

------End--------
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 33, in <module>
    spam(1,2)
NameError: name 'spam' is not defined

------End--------
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 19

traceback.format_exception(exception_object)

如果您只有异常对象,则可以使用以下命令从 Python 3 中的任何代码点以字符串形式获取回溯:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
Run Code Online (Sandbox Code Playgroud)

完整示例:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc_obj = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)
Run Code Online (Sandbox Code Playgroud)

输出:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf
Run Code Online (Sandbox Code Playgroud)

文档:https : //docs.python.org/3.9/library/traceback.html#traceback.format_exception

另请参阅:从异常对象中提取回溯信息

在 Python 3.9 中测试

  • 为什么在语言中执行此操作的唯一方法涉及传递相同的信息两次(“exc_obj”和“exc_obj.__traceback__”),以及不相关的第三个参数“None”? (9认同)

bgd*_*nlp 17

我在其他任何答案中都没有看到这一点。如果您出于任何原因传递 Exception 对象...

在 Python 3.5+ 中,您可以使用traceback.TracebackException.from_exception()从 Exception 对象获取跟踪。例如:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))
Run Code Online (Sandbox Code Playgroud)

但是,上面的代码导致:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')
Run Code Online (Sandbox Code Playgroud)

这只是堆栈的两个级别,而不是在屏幕上打印的内容,如果异常被引发stack_lvl_2()而不被拦截(取消注释该# raise行)。

据我了解,这是因为stack_lvl_3()在这种情况下,异常仅记录引发时堆栈的当前级别。当它通过堆栈向上传递时,更多的级别被添加到它的__traceback__. 但是我们在 中截获了它stack_lvl_2(),这意味着它要记录的只是级别 3 和 2。要获得打印在标准输出上的完整跟踪,我们必须在最高(最低?)级别捕获它:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))
Run Code Online (Sandbox Code Playgroud)

结果是:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Run Code Online (Sandbox Code Playgroud)

请注意,堆栈打印不同,缺少第一行和最后一行。因为它是不同的format()

尽可能远离引发异常的点拦截异常可以使代码更简单,同时也提供更多信息。


tos*_*osh 16

首先,不要用printS表示日志记录,有非稳态,证明和深思熟虑的STDLIB模块,这样做:logging。您绝对应该改用它。

其次,当存在本机且简单的方法时,不要试图将无关的工具弄得一团糟。这里是:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')
Run Code Online (Sandbox Code Playgroud)

而已。现在完成了。

对任何对引擎盖如何工作感兴趣的人的解释

什么log.exception是真正做只是为了通话log.error(即记录与级别的事件ERROR,并打印回溯然后。

为什么会更好?

好,这是一些注意事项:

  • 这是正确的 ;
  • 这很简单;
  • 很简单。

为什么没有人可以使用traceback记录仪exc_info=True或与其通话,或者弄脏记录仪sys.exc_info

好吧,只是因为!它们全都出于不同的目的而存在。例如,traceback.print_exc的输出与解释器本身产生的回溯有些不同。如果您使用它,将会使任何人阅读您的日志感到困惑,他们会将头撞向他们。

传递exc_info=True日志呼叫是不合适的。但是,当捕获可恢复的错误并且您还希望使用INFO回溯记录它们(使用例如级别)时,它很有用,因为它log.exception只会生成一个级别的日志- ERROR

而且,您绝对应该尽可能避免混乱sys.exc_info。它不是一个公共接口,而是一个内部接口- 如果您完全知道自己在做什么,就可以使用它。它不仅仅用于打印例外。

  • 我觉得这个答案有点可笑。它充满了“做/不做这只是因为”而没有解释原因。您在“为什么更好?”中的观点 几乎所有人都在说同一件事:“因为它是。” 我觉得这没有帮助。你至少解释了一点。 (6认同)
  • 它也不能按原样工作。不是这个。我现在还没有完成:这个答案只是浪费时间。 (3认同)

Mar*_*ald 9

除了@Aaron霍尔的答案,如果你正在登录,但不希望使用logging.exception()(因为它记录在错误级别),你可以用一个较低的水平,并通过exc_info=True.例如

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)
Run Code Online (Sandbox Code Playgroud)


Ale*_*kin 9

import io
import traceback

try:
    call_code_that_fails()
except:

    errors = io.StringIO()
    traceback.print_exc(file=errors)  # Instead of printing directly to stdout, the result can be further processed
    contents = str(errors.getvalue())
    print(contents)
    errors.close()
Run Code Online (Sandbox Code Playgroud)

  • 2 条评论:前面的答案中已经讨论了 `traceback.print_exc()` 的使用。更重要的是,当最后五行完全等同于 `traceback.print_exc()` 时,为什么要对 `io.StringIO` 搞那么多事情呢? (6认同)
  • @joanis我相信如果您想访问错误正文并且**不仅仅是**打印它,那么这些行很有用。我个人发现它很有用,因为我正在将堆栈跟踪记录到数据库中。 (4认同)

Edw*_*ell 7

为了得到精确的堆栈跟踪,作为一个字符串,在没有尝试/除非在那里跨过它,只需将这种在不同的块捕获违规的异常已经提出.

desired_trace = traceback.format_exc(sys.exc_info())
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它(假设flaky_func已定义,并log调用您最喜欢的日志记录系统):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)
Run Code Online (Sandbox Code Playgroud)

捕获并重新引发KeyboardInterrupts 是个好主意,这样你仍然可以使用Ctrl-C终止程序.记录超出了问题的范围,但一个很好的选择是记录.systraceback模块的文档.

  • 这在Python 3中不起作用,需要更改为`desired_trace = traceback.format_exc()`。传递sys.exc_info()作为参数从来都不是正确的选择,但是在Python 2中却被默默地忽略了,但在Python 3中却被忽略(无论如何还是3.6.4)。 (4认同)
  • `KeyboardInterrupt` 不是(直接或间接)从 `Exception` 派生的。(两者都是从`BaseException` 派生的。)这意味着`except Exception:` 永远不会捕捉到`KeyboardInterrupt`,因此`except KeyboardInterrupt: raise` 是完全没有必要的。 (4认同)

Ivo*_*ijk 6

您需要将try/except放在可能发生错误的最内圈内,即

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e
Run Code Online (Sandbox Code Playgroud)

... 等等

换句话说,您需要在尽可能具体的情况下尽可能地包装try/except中可能失败的语句.


Bas*_*asj 6

关于此答案的评论:print(traceback.format_exc())与相比,对我来说做得更好traceback.print_exc()。对于后者,hello有时会奇怪地将其与回溯文本“混合”,例如,如果两者都想同时写入stdout或stderr,则会产生奇怪的输出(至少在文本编辑器内部进行构建并在“构建结果”面板)。

追溯(最近一次通话):
文件“ C:\ Users \ User \ Desktop \ test.py”,第7行,在
地狱 do_stuff()
文件“ C:\ Users \ User \ Desktop \ test.py”,第4行,在do_stuff
1/0
ZeroDivisionError中:整数除或以零为模的
o
[在0.1s内完成]

所以我用:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
Run Code Online (Sandbox Code Playgroud)


nup*_*ick 6

如果您已经有一个 Error 对象,并且您想打印整个内容,则需要进行这个有点尴尬的调用:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)
Run Code Online (Sandbox Code Playgroud)

没错,print_exception需要三个位置参数:异常的类型、实际的异常对象和异常自己的内部回溯属性。

在 python 3.5 或更高版本中, thetype(err)是可选的...但它是一个位置参数,因此您仍然必须在其位置显式传递 None 。

traceback.print_exception(None, err, err.__traceback__)
Run Code Online (Sandbox Code Playgroud)

我不知道为什么所有这些不只是traceback.print_exception(err). 为什么你会想要打印出一个错误,以及一个不是属于那个错误的回溯,这超出了我的理解。


Acu*_*nus 6

此答案中的这种用法是自 Python 3.10 以来的新用法,并且尚未被任何先前的答案涵盖。要打印回溯,可以将异常赋予traceback.print_exception.

例子:

import traceback

try:
    object.bad_attr
except Exception as exc:
    traceback.print_exception(exc)
Run Code Online (Sandbox Code Playgroud)

输出回溯:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: type object 'object' has no attribute 'bad_attr'
Run Code Online (Sandbox Code Playgroud)

提醒一下,这个答案需要 Python 3.10 或更高版本才能起作用。

要将引用捕获为字符串,请参阅此答案


归档时间:

查看次数:

606991 次

最近记录:

5 年,11 月 前