我想在不退出的情况下捕获和记录异常,例如,
try:
do_stuff()
except Exception as err:
print(Exception, err)
# I want to print the entire traceback here,
# not just the exception name and details
我想打印与抛出异常时打印的完全相同的输出,而不使用try/,只是拦截异常,并且我不希望它退出程序。
我想在不退出的情况下捕获和记录异常,例如,
try:
do_stuff()
except Exception as err:
print(Exception, err)
# I want to print the entire traceback here,
# not just the exception name and details
我想打印与抛出异常时打印的完全相同的输出,而不使用try/,只是拦截异常,并且我不希望它退出程序。
当前回答
如果你已经有了一个Error对象,并且你想打印整个东西,你需要做这个有点尴尬的调用:
import traceback
traceback.print_exception(type(err), err, err.__traceback__)
没错,print_exception接受三个位置参数:异常的类型、实际的异常对象和异常自身的内部回溯属性。
在python 3.5或更高版本中,类型(err)是可选的…但它是一个位置参数,所以你仍然需要显式地在它的位置传递None。
traceback.print_exception(None, err, err.__traceback__)
我不知道为什么所有这些不只是traceback.print_exception(err)。为什么你会想打印出一个错误,以及一个回溯,而不是属于该错误的,这超出了我的理解。
其他回答
如何在不停止程序的情况下打印完整的回溯?
当你不想因为一个错误而停止你的程序时,你需要用try/except来处理这个错误:
try:
do_something_that_might_error()
except Exception as error:
handle_the_error(error)
要提取完整的回溯,我们将使用标准库中的traceback模块:
import traceback
并创建一个相当复杂的stacktrace来演示我们得到完整的stacktrace:
def raise_error():
raise RuntimeError('something bad happened!')
def do_something_that_might_error():
raise_error()
印刷
要打印完整的回溯,请使用traceback。print_exc方法:
try:
do_something_that_might_error()
except Exception as error:
traceback.print_exc()
打印:
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!
优于打印、日志:
但是,最佳实践是为您的模块设置一个记录器。它将知道模块的名称,并且能够更改级别(在其他属性中,例如处理程序)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
在这种情况下,您将需要记录器。异常函数改为:
try:
do_something_that_might_error()
except Exception as error:
logger.exception(error)
日志:
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!
或者,您可能只想要字符串,在这种情况下,您将需要回溯。Format_exc函数改为:
try:
do_something_that_might_error()
except Exception as error:
logger.debug(traceback.format_exc())
日志:
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!
结论
对于这三个选项,我们看到我们得到的输出和我们有错误时是一样的:
>>> 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!
使用哪种
性能问题在这里并不重要,因为IO通常占主导地位。我更喜欢,因为它精确地以向前兼容的方式执行请求:
logger.exception(error)
日志级别和输出可以调整,这样就可以很容易地关闭,而不需要修改代码。通常做直接需要的是最有效的方法。
其他一些答案已经指出了回溯模块。
请注意,使用print_exc,在某些极端情况下,您将无法获得您所期望的结果。在Python 2.x中:
import traceback
try:
raise TypeError("Oups!")
except Exception, err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_exc()
...将显示上一个异常的回溯:
Traceback (most recent call last):
File "e.py", line 7, in <module>
raise TypeError("Again !?!")
TypeError: Again !?!
如果你真的需要访问原始的回溯,一个解决方案是将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
生产:
Traceback (most recent call last):
File "t.py", line 6, in <module>
raise TypeError("Oups!")
TypeError: Oups!
但这也有一些陷阱:
From the doc of sys_info: Assigning the traceback return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced by a local variable in the same function or by the traceback from being garbage collected. [...] If you do need the traceback, make sure to delete it after use (best done with a try ... finally statement) but, from the same doc: Beginning with Python 2.2, such cycles are automatically reclaimed when garbage collection is enabled and they become unreachable, but it remains more efficient to avoid creating cycles.
另一方面,通过允许你访问与异常相关的回溯,Python 3产生了一个不那么令人惊讶的结果:
import traceback
try:
raise TypeError("Oups!")
except Exception as err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_tb(err.__traceback__)
... 将显示:
File "e3.py", line 4, in <module>
raise TypeError("Oups!")
要获得精确的堆栈跟踪(作为字符串),如果没有try/except进行跨步处理,则会引发该字符串,只需将其放置在捕获违规异常的except块中。
desired_trace = traceback.format_exc(sys.exc_info())
下面是如何使用它(假设定义了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)
捕获并重新引发KeyboardInterrupts是个好主意,这样您仍然可以使用Ctrl-C终止程序。日志记录不在这个问题的范围之内,但是日志记录是一个很好的选择。sys和traceback模块的文档。
我在其他答案中没有看到这个。如果你出于某种原因传递一个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()))
然而,上面的代码导致:
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')
这只是堆栈的两层,而不是在stack_lvl_2()中引发异常而没有被拦截(取消注释# raise行)时在屏幕上打印的内容。
根据我的理解,这是因为异常在被引发时只记录堆栈的当前级别,在本例中是stack_lvl_3()。当它在堆栈中往回传递时,更多的层被添加到它的__traceback__中。但是我们在stack_lvl_2()中拦截了它,这意味着它只能记录级别3和2。要获得打印在stdout上的完整跟踪,我们必须在最高(最低?)级别捕获它:
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()))
结果是:
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')
注意,堆栈打印是不同的,第一行和最后一行都不见了。因为它是不同的格式()。
在尽可能远离异常引发点的地方拦截异常,可以简化代码,同时提供更多信息。
在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--------')
输出如下所示:
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--------