获得完整的追溯

war*_*iuc 25 python traceback

如何在以下情况下获得完整的回溯,包括调用func2func函数?

import traceback

def func():
    try:
        raise Exception('Dummy')
    except:
        traceback.print_exc()

def func2():
    func()


func2()
Run Code Online (Sandbox Code Playgroud)

当我运行这个时,我得到:

Traceback (most recent call last):
  File "test.py", line 5, in func
    raise Exception('Dummy')
Exception: Dummy
Run Code Online (Sandbox Code Playgroud)

traceback.format_stack()不是我想要的,因为需要将traceback对象传递给第三方模块.

我对这个案子特别感兴趣:

import logging


def func():
    try:
        raise Exception('Dummy')
    except:
        logging.exception("Something awful happened!")


def func2():
    func()


func2()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我得到:

ERROR:root:Something awful happened!
Traceback (most recent call last):
  File "test.py", line 9, in func
    raise Exception('Dummy')
Exception: Dummy
Run Code Online (Sandbox Code Playgroud)

use*_*342 33

正如mechmind所回答的那样,堆栈跟踪仅包含引发异常的站点与try块的站点之间的帧.如果你需要完整的堆栈跟踪,显然你运气不好.

除了显然可以将堆栈条目从顶层提取到当前帧之外 - traceback.extract_stack管理它就好了.问题是获得的信息traceback.extract_stack来自直接检查堆栈帧而不在任何时候创建回溯对象,并且loggingAPI需要回溯对象来影响回溯输出.

幸运的是,logging它不需要实际的回溯对象,它需要一个可以传递给traceback模块格式化例程的对象.traceback也不关心 - 它只使用回溯的两个属性,框架和行号.因此,应该可以创建鸭型faux-traceback对象的链接列表,并将其作为回溯传递出去.

import sys

class FauxTb(object):
    def __init__(self, tb_frame, tb_lineno, tb_next):
        self.tb_frame = tb_frame
        self.tb_lineno = tb_lineno
        self.tb_next = tb_next

def current_stack(skip=0):
    try: 1/0
    except ZeroDivisionError:
        f = sys.exc_info()[2].tb_frame
    for i in xrange(skip + 2):
        f = f.f_back
    lst = []
    while f is not None:
        lst.append((f, f.f_lineno))
        f = f.f_back
    return lst

def extend_traceback(tb, stack):
    """Extend traceback with stack info."""
    head = tb
    for tb_frame, tb_lineno in stack:
        head = FauxTb(tb_frame, tb_lineno, head)
    return head

def full_exc_info():
    """Like sys.exc_info, but includes the full traceback."""
    t, v, tb = sys.exc_info()
    full_tb = extend_traceback(tb, current_stack(1))
    return t, v, full_tb
Run Code Online (Sandbox Code Playgroud)

有了这些功能,您的代码只需要一个简单的修改:

import logging

def func():
    try:
        raise Exception('Dummy')
    except:
        logging.error("Something awful happened!", exc_info=full_exc_info())

def func2():
    func()

func2()
Run Code Online (Sandbox Code Playgroud)

......给出预期的输出:

ERROR:root:Something awful happened!
Traceback (most recent call last):
  File "a.py", line 52, in <module>
    func2()
  File "a.py", line 49, in func2
    func()
  File "a.py", line 43, in func
    raise Exception('Dummy')
Exception: Dummy
Run Code Online (Sandbox Code Playgroud)

请注意,faux-traceback对象完全可用于内省显示局部变量或作为参数 - pdb.post_mortem()因为它们包含对实际堆栈帧的引用.

  • 我知道这个信息。如果您再次调查我的问题,您将使用[`logging.exception`](http://docs.python.org/2/library/logging.html#logging.Logger.exception),这是你写的 (2认同)
  • @warwaruk我现在更新了答案以实际回答您的问题。如果有更简单的方法可以做到这一点,我也想知道。 (2认同)

小智 6

这是基于 user4815162342 的答案,但更简约一点:

\n
import sys\nimport collections\n\nFauxTb = collections.namedtuple("FauxTb", ["tb_frame", "tb_lineno", "tb_next"])\n\ndef full_exc_info():\n    """Like sys.exc_info, but includes the full traceback."""\n    t, v, tb = sys.exc_info()\n    f = sys._getframe(2)\n    while f is not None:\n        tb = FauxTb(f, f.f_lineno, tb)\n        f = f.f_back\n    return t, v, tb\n
Run Code Online (Sandbox Code Playgroud)\n

它避免抛出虚拟异常,但代价是需要使用sys._getframe(). 它假设在捕获异常的子句中使用except,因为它上升了两个堆栈帧(full_exc_info调用full_exc_info\xe2\x80\x93 的函数将是调用引发代码的函数,因此已经包含在原始回溯)。

\n

这给出了与 user4815162342 的答案中的代码相同的输出。

\n

如果您不介意格式上的细微差别,您也可以使用

\n
import logging\n\ndef func():\n    try:\n        raise Exception('Dummy')\n    except:\n        logging.exception("Something awful happened!", stack_info=True)\n\ndef func2():\n    func()\n\nfunc2()\n
Run Code Online (Sandbox Code Playgroud)\n

这导致

\n
ERROR:root:Something awful happened!\nTraceback (most recent call last):\n  File "test.py", line 5, in func\n    raise Exception('Dummy')\nException: Dummy\nStack (most recent call last):\n  File "test.py", line 12, in <module>\n    func2()\n  File "test.py", line 10, in func2\n    func()\n  File "test.py", line 7, in func\n    logging.exception("Something awful happened!", stack_info=True)\n
Run Code Online (Sandbox Code Playgroud)\n

在这种情况下,您将获得从尝试到异常的跟踪,以及从根调用到日志记录调用位置的第二次跟踪。

\n