如何在Python中获取完整的异常堆栈跟踪

44
以下代码片段:
import traceback

def a():
    b()

def b():
    try:
        c()
    except:
        traceback.print_exc()

def c():
    assert False

a()
产生以下输出:
Traceback (most recent call last):
  File "test.py", line 8, in b
    c()
  File "test.py", line 13, in c
    assert False
AssertionError

如果我想获取包括调用a的完整堆栈跟踪信息,应该使用什么?

如果有用的话,我使用的是Python 2.6.6。

编辑:我想要获取的是与去掉try except并让异常传播到顶层时相同的信息。例如,以下代码片段:

def a():
    b()

def b():
    c()

def c():
    assert False

a()

会产生以下输出:

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    a()
  File "test.py", line 2, in a
    b()
  File "test.py", line 5, in b
    c()
  File "test.py", line 8, in c
    assert False
AssertionError

2
我现在注意到了这个问题。昨天我回答了一个非常相似的问题,这个答案在这里似乎也适用。它有一个额外的优点,就是它生成了一个真正的回溯,你不仅可以使用traceback函数打印它,还可以将其传递给logging模块或其他地方。 - user4815162342
4个回答

38

这是一个基于这个答案的函数。即使没有异常,它也能正常运行:

def full_stack():
    import traceback, sys
    exc = sys.exc_info()[0]
    stack = traceback.extract_stack()[:-1]  # last one would be full_stack()
    if exc is not None:  # i.e. an exception is present
        del stack[-1]       # remove call of full_stack, the printed exception
                            # will contain the caught exception caller instead
    trc = 'Traceback (most recent call last):\n'
    stackstr = trc + ''.join(traceback.format_list(stack))
    if exc is not None:
         stackstr += '  ' + traceback.format_exc().lstrip(trc)
    return stackstr

print full_stack() 将打印完整的堆栈跟踪信息,包括例如 IPython 的 interactiveshell.py 调用,因为(据我所知)无法知道谁会捕获异常。但在任何情况下都不值得去研究。

如果在 except 块内调用 print full_stack()full_stack 将包括堆栈跟踪信息直到 raise。在标准的 Python 解释器中,这将与未捕获异常时收到的消息相同(这就是为什么要有 del stack[-1],因为您不关心 except 块而是关心 try 块)。


啊,我意识到这与被接受的答案非常相似,只是它更清晰地处理了非except块。 - Tobias Kienzler
1
我稍微更喜欢你的方法。切片结构良好的调用堆栈比切片无结构的行列表更加清晰。感谢提供 Pythonic 的替代方案! - Cecil Curry
1
优雅的解决方案!但是我认为您误用了 lstrip(),因为它会删除任何存在于 trc 中的字符,这可能会导致意外截断堆栈。更好的方式是例如 if stackstr.startswith(trc): stackstr = stackstr.replace(trc, '', 1) - Delgan
1
@Delgan 谢谢,说得好 - 不过既然 exc is not None,我想我们甚至可以只写成 stackstr += ' ' + traceback.format_exc()[len(trc):],因为它总是以 trc 开头。 - Tobias Kienzler

26

我不知道是否有更好的方法,但这是我所做的:

import traceback
import sys

def format_exception(e):
    exception_list = traceback.format_stack()
    exception_list = exception_list[:-2]
    exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
    exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))

    exception_str = "Traceback (most recent call last):\n"
    exception_str += "".join(exception_list)
    # Removing the last \n
    exception_str = exception_str[:-1]

    return exception_str

def main1():
    main2()

def main2():
    try:
        main3()
    except Exception as e:
        print "Printing only the traceback above the current stack frame"
        print "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
        print
        print "Printing the full traceback as if we had not caught it here..."
        print format_exception(e)

def main3():
    raise Exception()

if __name__ == '__main__':
    main1()

这是我获得的输出:

Printing only the traceback above the current stack frame
Traceback (most recent call last):
  File "exc.py", line 22, in main2
    main3()
  File "exc.py", line 31, in main3
    raise Exception()
Exception


Printing the full traceback as if we had not caught it here...
Traceback (most recent call last):
  File "exc.py", line 34, in <module>
    main1()
  File "exc.py", line 18, in main1
    main2()
  File "exc.py", line 22, in main2
    main3()
  File "exc.py", line 31, in main3
    raise Exception()
Exception

1
我接受这个答案,因为它实际上解决了问题。不过我仍然感到遗憾的是,没有库调用可以解决这个问题。 - Gordon Wrigley
+1 - 我发布了一个类似的答案,它也适用于except块之外。 - Tobias Kienzler

12

那么这提供了另一半,有没有办法将整个东西作为一个整体获取? - Gordon Wrigley
1
调用你自己的代码吧,没人会阻止你自己调用print_stack()和print_exc(),对吧? - user2665694
1
traceback.print_exception(*sys.exc_info()) - Keith
@Keith 这与 traceback.print_exc() 有相同的效果。 - Gordon Wrigley
4
我可以同时调用它们并合并输出,但标准库应该已经涵盖了这一点。实质上,我只想获得与将异常传播到顶层相同的信息。 - Gordon Wrigley

6


这是Tobias Kienzler答案的更好的变体。其功能相同,但可以在异常块中以外的某个位置调用。 换句话说,当像这样调用时,此变体将打印相同的堆栈:

try:
   ...
except Exception:
    print full_stack()

或者

def print_full_stack():
    print full_stack()

try:
   ...
except Exception:
    print_full_stack()

这里是代码:
def full_stack():
    import traceback, sys
    exc = sys.exc_info()[0]
    if exc is not None:
        f = sys.exc_info()[-1].tb_frame.f_back
        stack = traceback.extract_stack(f)
    else:
        stack = traceback.extract_stack()[:-1]  # last one would be full_stack()
    trc = 'Traceback (most recent call last):\n'
    stackstr = trc + ''.join(traceback.format_list(stack))
    if exc is not None:
        stackstr += '  ' + traceback.format_exc().lstrip(trc)
    return stackstr

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