Piszę program, który analizuje 10 stron internetowych, lokalizuje pliki danych, zapisuje pliki, a następnie analizuje je, aby utworzyć dane, które można łatwo wykorzystać w bibliotece NumPy. Istnieje mnóstwo błędów, które ten plik napotyka przez złe linki, źle sformułowany XML, brakujące wpisy i inne rzeczy, które jeszcze nie skategoryzowałem. Początkowo stworzyłem ten program do obsługi takich błędów:
try:
do_stuff()
except:
pass
Ale teraz chcę rejestrować błędy:
try:
do_stuff()
except Exception, err:
print Exception, err
Uwaga: drukuje się do pliku dziennika w celu późniejszego przejrzenia. Zwykle drukuje to bardzo bezużyteczne dane. Chcę wydrukować dokładnie te same wiersze, które zostaną wydrukowane, gdy błąd zostanie uruchomiony bez przechwytywania wyjątku przez try-wyjątkiem, ale nie chcę, aby zatrzymał mój program, ponieważ jest zagnieżdżony w szeregu pętli for, które chciałbym do końca.
źródło
print(sys.exc_info()[0]
odciski<class 'Exception'>
.Jeśli debugujesz i po prostu chcesz zobaczyć bieżący ślad stosu, możesz po prostu wywołać:
traceback.print_stack()
Nie ma potrzeby ręcznego podnoszenia wyjątku, aby go ponownie złapać.
źródło
Jeśli nie chcesz zatrzymać programu z powodu błędu, musisz obsłużyć ten błąd za pomocą try / wyjątkiem:
Aby wyodrębnić pełny traceback, użyjemy
traceback
modułu ze standardowej biblioteki:I aby stworzyć dość skomplikowane śledzenie stosu, aby pokazać, że otrzymujemy pełny stos śledzenia:
Druk
Aby wydrukować pełny ślad, użyj
traceback.print_exc
metody:Które wydruki:
Lepsze niż drukowanie, logowanie:
Jednak najlepszą praktyką jest skonfigurowanie rejestratora dla modułu. Będzie znał nazwę modułu i będzie mógł zmieniać poziomy (między innymi atrybutami, takimi jak moduły obsługi)
W takim przypadku będziesz potrzebować
logger.exception
funkcji:Które dzienniki:
A może po prostu chcesz ciąg, w którym to przypadku będziesz potrzebować
traceback.format_exc
funkcji:Które dzienniki:
Wniosek
I dla wszystkich trzech opcji widzimy, że otrzymujemy takie same dane wyjściowe, jak w przypadku błędu:
źródło
traceback.print_exc()
zwraca tylko ostatnie wywołanie: jak udało ci się zwrócić kilka poziomów stosu (i ewentualnie wszystkie poziomy?)raise
łańcuchy typu „goły” lub „wyjątkowy”, czy ukrywasz pierwotny ślad? patrz stackoverflow.com/questions/2052390/…Po pierwsze, nie używaj
print
s do logowania, istnieje astabilny, sprawdzone i dobrze przemyślane moduł stdlib do zrobienia, że:logging
. Zdecydowanie powinieneś go użyć zamiast tego.Po drugie, nie ulegaj pokusie zrobienia bałaganu z niepowiązanymi narzędziami, gdy istnieje natywne i proste podejście. Oto on:
Otóż to. Już skończyłeś.
Wyjaśnienie dla każdego, kto jest zainteresowany tym, jak rzeczy działają pod maską
To, co
log.exception
faktycznie robi, to po prostu wywołanielog.error
(tj. Rejestrowanie zdarzenia z poziomemERROR
) i wydrukowanie śledzenia.Dlaczego to jest lepsze?
Oto kilka uwag:
Dlaczego nikt nie powinien używać
traceback
rejestratora ani do niego dzwonićexc_info=True
ani brudzić sobie rąksys.exc_info
?Tylko dlatego! Wszystkie istnieją do różnych celów. Na przykład
traceback.print_exc
dane wyjściowe nieco różnią się od tracebacków wytwarzanych przez samego tłumacza. Jeśli go użyjesz, zdezorientujesz każdego, kto czyta twoje dzienniki, będą walić w nie głowami.Przekazywanie
exc_info=True
do rejestrowania połączeń jest po prostu nieodpowiednie. Ale , to jest przydatne podczas połowu błędy możliwe do odzyskania i chcesz je log (przy użyciu npINFO
poziom) z tracebacks jak dobrze, ponieważlog.exception
produkuje dzienniki tylko na jednym poziomie -ERROR
.I zdecydowanie powinieneś unikać bałaganu,
sys.exc_info
na ile możesz. Po prostu nie jest to interfejs publiczny, lecz wewnętrzny - możesz go użyć, jeśli na pewno wiesz, co robisz. Nie jest przeznaczony do drukowania wyjątków.źródło
logging.exception()
. Nie musisz tworzyć wystąpienia dziennika, chyba że masz specjalne wymagania.Oprócz odpowiedzi @Aarona Halla, jeśli logujesz się, ale nie chcesz używać
logging.exception()
(ponieważ loguje się na poziomie ERROR), możesz użyć niższego poziomu i przekazaćexc_info=True
. na przykładźródło
Aby uzyskać dokładny ślad stosu, jako ciąg znaków, który zostałby podniesiony, gdyby nie próbowano go przekroczyć, po prostu umieść go w bloku wyjątku, który przechwytuje wyjątek.
Oto jak z niego korzystać (zakładając, że
flaky_func
jest zdefiniowane ilog
wywołuje twój ulubiony system rejestrowania):Dobrym pomysłem jest łapanie i przebijanie
KeyboardInterrupt
, abyś mógł nadal zabijać program za pomocą Ctrl-C. Logowanie jest poza zakresem pytania, ale dobrą opcją jest logowanie . Dokumentacja dla sys i modułów śledzenia .źródło
desired_trace = traceback.format_exc()
. Przekazywaniesys.exc_info()
jako argument nigdy nie było właściwą rzeczą, ale jest dyskretnie ignorowane w Pythonie 2 - ale nie w Pythonie 3 (w każdym razie 3.6.4).KeyboardInterrupt
nie pochodzi (bezpośrednio ani pośrednio) zException
. (Oba pochodzą odBaseException
.) Oznacza to,except Exception:
że nigdy nie złapieKeyboardInterrupt
, a zatemexcept KeyboardInterrupt: raise
jest całkowicie niepotrzebne.traceback.format_exc(sys.exc_info())
nie działa dla mnie z python 3.6.10Będziesz musiał umieścić try / oprócz wewnątrz najbardziej wewnętrznej pętli, w której może wystąpić błąd, tj
... i tak dalej
Innymi słowy, będziesz musiał zawinąć instrukcje, które mogą się nie powieść w try / oprócz możliwie najbardziej szczegółowych, w najbardziej wewnętrznej pętli, jak to możliwe.
źródło
Uwaga na temat komentarzy do tej odpowiedzi :
print(traceback.format_exc())
robi dla mnie lepszą robotę niżtraceback.print_exc()
. W tym drugim przypadkuhello
jest on dziwnie „mieszany” z tekstem śledzenia, tak jakby obaj chcieli jednocześnie pisać na stdout lub stderr, wytwarzając dziwne dane wyjściowe (przynajmniej podczas budowania z edytora tekstu i przeglądania wyników w Panel „Buduj wyniki”).Więc używam:
źródło
Nie widzę tego w żadnej innej odpowiedzi. Jeśli omijasz obiekt wyjątku z jakiegokolwiek powodu ...
W Pythonie 3.5+ można uzyskać śledzenie z obiektu wyjątku za pomocą traceback.TracebackException.from_exception () . Na przykład:
Powyższy kod powoduje jednak:
To tylko dwa poziomy stosu, w przeciwieństwie do tego, co wydrukowano by na ekranie, gdyby wyjątek został podniesiony
stack_lvl_2()
i nie został przechwycony (odkomentowanie# raise
linii).Jak rozumiem,
stack_lvl_3()
dzieje się tak, ponieważ wyjątek rejestruje tylko bieżący poziom stosu, gdy jest podnoszony, w tym przypadku. Gdy jest on przekazywany z powrotem przez stos, dodaje się do niego kolejne poziomy__traceback__
. Ale przechwyciliśmy gostack_lvl_2()
, co oznacza, że wszystko, co udało mu się nagrać, to poziomy 3 i 2. Aby uzyskać pełny ślad wydrukowany na standardowym wyjściu, musielibyśmy go złapać na najwyższym (najniższym?) Poziomie:Co skutkuje w:
Zauważ, że wydruk na stosie jest inny, brakuje pierwszego i ostatniego wiersza. Ponieważ jest inaczej
format()
.Przechwytywanie wyjątku jak najdalej od miejsca, w którym został zgłoszony, upraszcza kod, jednocześnie zapewniając więcej informacji.
źródło
Chcesz moduł śledzenia wstecznego . Pozwala wydrukować zrzuty stosów, tak jak zwykle robi to Python. W szczególności funkcja print_last wydrukuje ostatni wyjątek i ślad stosu.
źródło
Uzyskaj pełny traceback jako ciąg znaków od obiektu wyjątku za pomocą
traceback.format_exception
Jeśli masz tylko obiekt wyjątku, możesz uzyskać traceback jako ciąg znaków z dowolnego punktu kodu w Pythonie 3 za pomocą:
Pełny przykład:
Wynik:
Dokumentacja: https://docs.python.org/3.7/library/traceback.html#traceback.format_exception
Zobacz także: Wyodrębnij informacje śledzenia z obiektu wyjątku
Testowane w Pythonie 3.7.3.
źródło
Jeśli masz już obiekt Error i chcesz wydrukować całość, musisz wykonać to nieco niezręczne wywołanie:
Zgadza się,
print_exception
przyjmuje trzy argumenty pozycyjne: typ wyjątku, rzeczywisty obiekt wyjątku i własną wewnętrzną właściwość traceback wyjątku.W Pythonie 3.5 lub nowszym
type(err)
opcja jest opcjonalna ... ale jest to argument pozycyjny, więc nadal musisz jawnie przekazać None w jej miejsce.Nie mam pojęcia, dlaczego to wszystko nie jest po prostu
traceback.print_exception(err)
. Dlaczego miałbyś kiedykolwiek chcieć wydrukować błąd wraz ze śledzeniem innym niż ten, który należy do tego błędu, jest poza mną.źródło