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

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/,只是拦截异常,并且我不希望它退出程序。


当前回答

traceback.format_exception (exception_object)

如果你只有异常对象,你可以从Python 3中的任何代码点获得字符串形式的回溯:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

完整的例子:

#!/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)

输出:

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

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

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

在Python 3.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()

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

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

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

traceback.format_exception (exception_object)

如果你只有异常对象,你可以从Python 3中的任何代码点获得字符串形式的回溯:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

完整的例子:

#!/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)

输出:

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

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

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

在Python 3.9中测试

其他一些答案已经指出了回溯模块。

请注意,使用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!")

如果你正在调试,只想查看当前的堆栈跟踪,你可以简单地调用:

traceback.print_stack ()

没有必要为了再次捕获异常而手动引发异常。