2012-11-03 16 views
23

Jak uzyskać pełne śledzenie w następującym przypadku, łącznie z połączeniami funkcji func2 i func?Uzyskaj pełne informacje zwrotne.

import traceback 

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

def func2(): 
    func() 


func2() 

gdy uruchamiam to uzyskać:

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

traceback.format_stack() nie jest to, co chcę, jak potrzeba traceback obiektu mają być przekazywane do modułu osoby trzeciej.

Jestem szczególnie zainteresowany w tym przypadku:

import logging 


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


def func2(): 
    func() 


func2() 

W tym przypadku otrzymuję:

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

możliwy duplikat [Jak zapisać wartości traceback/sys.exc \ _info() w zmiennej?] (Http://stackoverflow.com/questions/8238360/how-to-save-traceback-sys-exc- info-values-in-a-variable) – Nathan

+0

@Nathan, przeczytaj ponownie uważnie to pytanie. ** Pełny ** traceback jest potrzebny. – warvariuc

+2

Zobacz [Generowanie śledzenia stosu dla wyjątków w Pythonie.] (Http://blog.dscpl.com.au/2015/03/generating-full-stack-traces-for.html) wpis na blogu Grahama Dupletona. –

Odpowiedz

32

Jak mechmind odpowiedział ślad stos składa się wyłącznie z klatek pomiędzy miejscem, gdzie wyjątek został podniesiony i witryny bloku try. Jeśli potrzebujesz pełnego śledzenia stosu, najwyraźniej masz pecha.

Poza tym oczywiście możliwe jest wyodrębnienie wpisów stosu z najwyższego poziomu do bieżącej klatki - traceback.extract_stack zarządza nią dobrze. Problem polega na tym, że informacje uzyskane przez traceback.extract_stack pochodzą z bezpośredniej inspekcji ramek stosów bez tworzenia obiektu śledzenia w dowolnym punkcie, a interfejs API logging wymaga, aby obiekt śledzenia był odpowiedzialny za wpływanie na wynik śledzenia.

szczęście logging nie wymaga rzeczywisty Traceback obiekt, wymaga on obiekt, który może przejść do procedury formatowania modułu traceback. traceback to też nie obchodzi - wykorzystuje tylko dwa atrybuty traceback, ramkę i numer linii. Powinno być zatem możliwe stworzenie połączonej listy obiektów faux-traceback z kaczkami i przekazywanie jej jako 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 

Z tych funkcji w miejscu, kod wymaga jedynie trywialną modyfikacji:

import logging 

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

def func2(): 
    func() 

func2() 

... dać oczekiwany wynik:

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 

Należy pamiętać, że faux-traceback obiekty są w pełni użyteczne do wyświetlania zmiennych lokalnych w introspekcji lub jako argument do pdb.post_mortem() - ponieważ zawierają odniesienia do rzeczywistych ramek stosów.

+1

Znam te informacje. Jeśli spojrzysz ponownie na moje pytanie, użyjesz ['logging.exception'] (http://docs.python.org/2/library/logging.html#logging.Logger.exception), który jest skrótem do co napisałeś. – warvariuc

+0

@warwaruk Masz całkowitą rację. Punktem twojego pytania jest to, że blok try/except skraca śledzenie stosu, a chcesz mieć pełny. Nie mogę uwierzyć, że nigdy wcześniej tego nie zauważyłem. – user4815162342

+1

@warwaruk Zaktualizowałem teraz odpowiedź, aby odpowiedzieć na twoje pytanie. Jeśli jest łatwiejszy sposób, chciałbym to również poznać. – user4815162342

3

Śledzenie stosu jest zbierane, gdy pojawią się pęcherzyki wyjątku. Więc należy wydrukować traceback na górze pożądanego stosu:

import traceback 

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

def func2(): 
    func() 


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

Problem polega na tym, że zmienia to semantykę. Jeśli OP chce, aby 'func2' działał dalej, jeśli' func' nie działa (i rejestruje śledzenie), wyjątek musi być obsługiwany w 'func2', a nie poza nim. – user4815162342

-1

tam kilka informacji, które mogą być pobierane z traceback, a ja czasami wolą neater, bardziej „logiczny” informacje zamiast multi-line blob z plikami , numery linii i fragmenty kodu przydzielane przez traceback. Najlepiej, jeśli jedna linia powie wszystko, co niezbędne.

W tym celu używam następujących funkcji:

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 

To daje. i numer linii, np.:

(przykład nazwa modułu: 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" 

co może być nieco schludniej w niektórych przypadkach użytkowych.

+0

Nie sprawdziłem twojego kodu, ufam ci, że to działa. Ale w pytaniu jest napisane: "' traceback.format_stack() 'nie jest tym, czego chcę, ponieważ obiekt' traceback' musi zostać przekazany do modułu zewnętrznego. ". W każdym razie dziękuję za inny fragment kodu. – warvariuc

1

Pisałem moduł, który pisze pełniejszy Traceback

Moduł jest here dokumentacja jest docs

(również można dostać moduł z PyPI

sudo pip install pd 

)

Do wyjątków typu catch i pring wykonaj następujące czynności:

import pd 

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

Ślad stosu wygląda jak ten tutaj:

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 nie będzie wydrukować zawartość obiektu (ze złożonymi strukturami danych follow_objects może zająć dużo czasu).

+0

Plik "F: \ Python \ lib \ site-packages \ pd \ pdd.py", wiersz 18 wartość zwracana ^ TabError: niespójne użycie kart i spacji w wcięciu – user

Powiązane problemy