2012-11-03 145 views
23

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

import traceback 

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

def func2(): 
    func() 


func2() 

当我运行它,我得到:

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

traceback.format_stack()不是我想要什么,需要traceback对象传递给第三方模块。

我在此特别感兴趣的是:

import logging 


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


def func2(): 
    func() 


func2() 

在这种情况下,我得到:

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

[如何将traceback/sys.exc \ _info()值保存在变量中?](http://stackoverflow.com/questions/8238360/how-to-save-traceback-sys-exc- info-values-in-a-variable) – Nathan

+0

@Nathan,请仔细阅读这个问题。 **完整**追踪是必要的。 – warvariuc

+2

请参阅[在Python中为异常生成完整堆栈跟踪](http://blog.dscpl.com.au/2015/03/generating-full-stack-traces-for.html)由Graham Dupleton撰写的博客文章。 –

回答

32

由于mechmind回答,堆栈跟踪仅由所在部位的异常之间的帧被提出和try块的网站。如果你需要完整的堆栈跟踪,显然你运气不好。

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

幸运的是,logging不需要实际追踪的对象,它需要一个对象,它可以传递给traceback模块的格式化程序。 traceback也不在乎 - 它只使用追踪,帧和行号的两个属性。所以,应该有可能创建一个duck-typed人造追踪对象的链表并将其作为追踪传递给它。

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 

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

import logging 

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

def func2(): 
    func() 

func2() 

...得到所需要的输出:

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 

注意的是,人造回溯对象完全可用于自省 - 显示局部变量或作为参数pdb.post_mortem(),因为它们包含对真实堆栈帧的引用。

+1

中追踪此功能添加到Python 3中我知道此信息。如果您再次查看我的问题,您将使用['logging.exception'](http://docs.python.org/2/library/logging.html#logging.Logger.exception)这是一个快捷方式你写的是什么。 – warvariuc

+0

@warwaruk你说得很对。你的问题的关键是try/except块会缩短堆栈跟踪,并且你想要一个完整的。我无法相信我从未注意过它。 – user4815162342

+1

@warwaruk我现在已经更新了答案,以实际回答你的问题。如果有更简单的方法来做到这一点,我也想知道。 – user4815162342

3

当异常冒泡时收集堆栈跟踪。所以,你应该对所需的堆栈的顶部打印回溯:

import traceback 

def func(): 
    raise Exception('Dummy') 

def func2(): 
    func() 


try: 
    func2() 
except: 
    traceback.print_exc() 
+2

问题是这改变了语义。如果操作系统希望'func2'在func失败的情况下继续运行(并记录回溯),则该异常必须在'func2'中处理,而不是在其外面。 – user4815162342

-1

有一些可能会从回溯中提取更多的信息,我有时喜欢整洁,更“逻辑”的信息,而不是与文件的多行的blob ,行号和代码片断由traceback提供。最好一行应该说所有的要领。

要做到这一点我用下面的函数:

def raising_code_info(): 
    code_info = '' 
    try:  
     frames = inspect.trace() 
     if(len(frames)): 
      full_method_name = frames[0][4][0].rstrip('\n\r').strip() 
      line_number  = frames[1][2] 
      module_name  = frames[0][0].f_globals['__name__'] 
      if(module_name == '__main__'): 
       module_name = os.path.basename(sys.argv[0]).replace('.py','') 
      class_name = '' 
      obj_name_dot_method = full_method_name.split('.', 1) 
      if len(obj_name_dot_method) > 1: 
       obj_name, full_method_name = obj_name_dot_method 
       try: 
        class_name = frames[0][0].f_locals[obj_name].__class__.__name__ 
       except: 
        pass 
      method_name = module_name + '.' 
      if len(class_name) > 0: 
       method_name += class_name + '.' 
      method_name += full_method_name 
      code_info = '%s, line %d' % (method_name, line_number) 
    finally: 
     del frames 
     sys.exc_clear() 
    return code_info 

它给。和行号,例如:

(例如模块名称:test.py):

(line 73:) 
def function1(): 
    print 1/0 

class AClass(object):  
    def method2(self): 
     a = [] 
     a[3] = 1 

def try_it_out(): 
    # try it with a function 
    try: 
     function1() 
    except Exception, what: 
     print '%s: \"%s\"' % (raising_code_info(), what) 

    # try it with a method 
    try: 
     my_obj_name = AClass() 
     my_obj_name.method2()  
    except Exception, what: 
     print '%s: \"%s\"' % (raising_code_info(), what) 

if __name__ == '__main__': 
    try_it_out() 


test.function1(), line 75: "integer division or modulo by zero" 
test.AClass.method2(), line 80: "list assignment index out of range" 

这在某些使用情况稍微整洁。

+0

我还没有检查过你的代码,我相信你的工作。但是在这个问题中提到:“traceback.format_stack()'不是我想要的,因为需要将'traceback'对象传递给第三方模块。”无论如何,谢谢你的另一个代码片段。 – warvariuc

1

我写了写了一个更完整的追溯模块

该模块是here文档docs

(你也可以得到一封来自PyPI

sudo pip install pd 

模块)

为了捕捉和排除异常,请执行以下操作:

import pd 

try: 
    <python code> 
except BaseException:  
    pd.print_exception_ex(follow_objects = 1) 

堆栈跟踪看起来像这样一个位置:

Exception: got it 

#1 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 1) at  t test_pd.py:29 
Calls next frame at: 
    raise Exception('got it') at: test_pd.py:29 

#2 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 2) at test_pd.py:28 
Calls next frame at: 
    self.kuku2(depth - 1) at: test_pd.py:28 

#3 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 3) at test_pd.py:28 
Calls next frame at: 
    self.kuku2(depth - 1) at: test_pd.py:28 

#4 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 4) at test_pd.py:28 
Calls next frame at: 
    self.kuku2(depth - 1) at: test_pd.py:28 

#5 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 5) at  test_pd.py:28 
Calls next frame at: 
    self.kuku2(depth - 1) at: test_pd.py:28 

#6 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 6) at test_pd.py:28 
Calls next frame at: 
    self.kuku2(depth - 1) at: test_pd.py:28 

#7 def main() at test_pd.py:44 
Local variables: 
n = {'a': 42, 'b': [1, 2, 3, 4]} 
Calls next frame at: 
    pd.print_exception_ex(follow_objects = 1) at: test_pd.py:44 

follow_objects = 0将不打印出对象内容(与复杂的数据结构follow_objects可以采取很多的时间)。

+0

文件“F:\ Python \ lib \ site-packages \ pd \ pdd.py”,第18行 返回值 ^ TabError:缩进中制表符和空格的使用不一致 – user