我想在不退出的情况下捕获和记录异常,例如,

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:
    do_stuff()
except Exception, err:
    print(Exception, err)
    raise err

要获得精确的堆栈跟踪(作为字符串),如果没有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模块的文档。

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

当你不想因为一个错误而停止你的程序时,你需要用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)

日志级别和输出可以调整,这样就可以很容易地关闭,而不需要修改代码。通常做直接需要的是最有效的方法。

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()

在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--------