2012-11-03 147 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