如何将捕获的异常(其描述和堆栈跟踪)转换为外部使用的str ?

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print(complete_exception_description(e))

当前回答

>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

您可以使用sys.exc_info()收集信息,并使用traceback模块中的函数对其进行格式化。 下面是一些格式化的例子。

整个异常字符串位于:

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']

其他回答

在Python 3中,以下代码将格式化Exception对象,与使用traceback.format_exc()获得的结果完全相同:

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

这样做的好处是只需要Exception对象(多亏了记录的__traceback__属性),因此可以更容易地将其作为参数传递给另一个函数进行进一步处理。

如果你的目标是让异常和stacktrace消息看起来与python抛出错误时完全一样,那么以下代码在python 2+3中都适用:

import sys, traceback


def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# EXAMPLE BELOW...

def a():
    b()


def b():
    c()


def c():
    d()


def d():
    assert False, "Noooh don't do it."


print("THIS IS THE FORMATTED STRING")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("THIS IS HOW PYTHON DOES IT")
print("==========================\n")
a()

它的工作原理是从堆栈中删除最后一个format_stacktrace()调用,并加入其余的调用。当运行时,上面的示例给出以下输出:

THIS IS THE FORMATTED STRING
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

THIS IS HOW PYTHON DOES IT
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

让我们创建一个相当复杂的stacktrace,以演示我们得到完整的stacktrace:

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

def do_something_that_might_error():
    raise_error()

记录完整的堆栈跟踪

最佳实践是为您的模块设置一个记录器。它将知道模块的名称,并且能够更改级别(在其他属性中,例如处理程序)

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!

所以我们得到的输出和我们有错误时是一样的:

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

只获取字符串

如果你真的只想要字符串,使用回溯。取而代之的是Format_exc函数,在这里演示了记录字符串:

import traceback
try:
    do_something_that_might_error()
except Exception as error:
    just_the_string = traceback.format_exc()
    logger.debug(just_the_string)

日志:

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!
>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

您可以使用sys.exc_info()收集信息,并使用traceback模块中的函数对其进行格式化。 下面是一些格式化的例子。

整个异常字符串位于:

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']

如果你想将你的回溯转换为dict的列表(适用于python > 3.5):

from traceback import TracebackException


def list_traceback(exc_value: BaseException):
    result = list()

    # get previous fails, so errors are appended by order of execution
    if exc_value.__context__:
        result += list_traceback(exc_value.__context__)

    # convert Exception into TracebackException
    tbe = TracebackException.from_exception(exc_value)

    # get stacktrace (cascade methods calls)
    error_lines = list()
    for frame_summary in tbe.stack:
        summary_details = {
            'filename': frame_summary.filename,
            'method'  : frame_summary.name,
            'lineno'  : frame_summary.lineno,
            'code'    : frame_summary.line
        }
        error_lines.append(summary_details)

    # append error, by order of execution
    result.append({"error_lines": error_lines,
                   "type"       : tbe.exc_type.__name__,
                   "message"    : str(tbe)})

    return result

这将是结果(一个例子):

[
   {
      "error_lines": [
         {
            "filename": "/home/demo/file2.py",
            "method": "do_error_2",
            "lineno": 18,
            "code": "a=1/0"
         }
      ],
      "type": "ZeroDivisionError",
      "message": "division by zero"
   },
   {
      "error_lines": [
         {
            "filename": "/home/demo/file_main.py",
            "method": "demo2",
            "lineno": 50,
            "code": "file2.DEMO().do_error_2()"
         },
         {
            "filename": "/home/demo/file2.py",
            "method": "do_error_2",
            "lineno": 20,
            "code": "raise AssertionError(\"Raised inside the except, after division by zero\")"
         }
      ],
      "type": "AssertionError",
      "message": "Raised inside the except, after division by zero"
   }
]