Jak złapać i wydrukować pełny wyjątek traceback bez zatrzymywania / opuszczania programu?

Chcę wyłapywać i zapisywać wyjątki bez wychodzenia, np.

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    # I want to print the entire traceback here,
    # not just the exception name and details

Chcę wydrukować dokładnie to samo wyjście, które zostanie wydrukowane, gdy wyjątek zostanie podniesiony bez próby..poza przechwyceniem wyjątku, a ja nie chcę, żeby on opuścił mój program. Jak to zrobić?

Author: Rob Bednark, 2010-09-13

16 answers

Inna odpowiedź wskazała już moduł traceback.

Zauważ, że z print_exc, w niektórych narożnych przypadkach, nie uzyskasz tego, czego byś się spodziewał. W Pythonie 2.x:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

...wyświetla ślad ostatniego wyjątku :

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

Jeśli naprawdę potrzebujesz dostępu do oryginalnego traceback jednym z rozwiązań jest buforowanie Exception infos jako zwrócone z exc_info w lokalnym zmienną i wyświetla ją za pomocą print_exception:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

Produkcja:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

Kilka pułapek z tym jednak:

  • Z doc sys_info:

    Przypisanie zwracanej wartości traceback do zmiennej lokalnej w funkcji obsługującej wyjątek spowoduje okrężne odniesienie . Spowoduje to, że wszelkie odniesienia do zmiennej lokalnej w tej samej funkcji lub przez traceback nie zostaną usunięte. [...] jeśli potrzebujesz traceback, upewnij się, że usuniesz go po użyciu (najlepiej spróbować ... wreszcie oświadczenie)

  • Ale z tego samego doc:

    Począwszy od Pythona 2.2, takie cykle są automatycznie odzyskiwane, gdy funkcja garbage collection jest włączona i stają się nieosiągalne, ale unikanie tworzenia cykli pozostaje bardziej efektywne.


Z drugiej strony, pozwalając na dostęp do traceback związany z wyjątkiem, Python 3 daje mniej zaskakujący wynik:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

... wyświetli:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
 676
Author: Sylvain Leroux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-22 14:43:00

traceback.format_exc() lub sys.exc_info() dostarczy więcej informacji, jeśli tego chcesz.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])
 766
Author: volting,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-30 13:39:48

Jeśli debugujesz i chcesz zobaczyć bieżący ślad stosu, możesz po prostu wywołać:

traceback.print_stack()

Nie ma potrzeby ręcznie podnosić wyjątku, aby go złapać ponownie.

 292
Author: dimo414,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 06:38:36

Jak wydrukować pełny traceback bez zatrzymywania programu?

Jeśli nie chcesz zatrzymać programu na błąd, musisz obsłużyć ten błąd za pomocą try/except:
try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

Aby wyodrębnić pełny traceback, użyjemy modułu traceback z biblioteki standardowej:

import traceback
I stworzyć przyzwoicie skomplikowaną stacktrace, aby zademonstrować, że otrzymujemy pełną stacktrace:]}
def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Druk

Do Drukuj pełny trackback, użyj metody traceback.print_exc:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

Który drukuje:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Lepsze niż drukowanie, logowanie:

Jednak najlepszą praktyką jest skonfigurowanie rejestratora dla Twojego modułu. Będzie znał nazwę modułu i będzie mógł zmieniać poziomy (między innymi atrybuty, takie jak programy obsługi)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

W takim przypadku będziesz chciał zamiast tego logger.exception funkcji:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Który loguje:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

A może po prostu chcesz ciąg znaków, w takim przypadku będziesz chciał traceback.format_exc funkcji zamiast:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

Który loguje:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Podsumowanie

I dla wszystkich trzech opcji widzimy, że otrzymujemy to samo wyjście, co gdy mamy błąd:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Którego użyć

Problemy z wydajnością nie są tutaj ważne, ponieważ IO Zwykle dominuje. W przeciwieństwie do poprzednich wersji, nie jest to możliwe.]}

logger.exception(error)

Poziomy logowania i wyjścia można regulować, dzięki czemu można je łatwo wyłączyć bez dotykania kodu. I zwykle robi to, co jest bezpośrednio potrzebne, to najbardziej efektywny sposób, aby to zrobić.

 130
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-10 14:29:08

Po pierwsze, nie używaj prints do logowania, istnieje astable, sprawdzony i dobrze przemyślany moduł stdlib do tego celu: logging. Zdecydowanie powinieneś użyć go zamiast tego.

Po drugie, nie daj się skusić na zrobienie bałaganu z niepowiązanymi narzędziami, gdy istnieje natywne i proste podejście. Tutaj jest:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

To jest to. Już po tobie.

Wyjaśnienie dla każdego, kto jest zainteresowany, jak rzeczy działają pod maską

Czym log.exception jest właściwie wykonanie to tylko wywołanie log.error (czyli log event with level ERROR) i następnie wydrukuj traceback.

Dlaczego jest lepiej?

Oto kilka rozważań:]}
  • jest po prostu prawo ;
  • to proste;
  • To proste.

Dlaczego nikt nie powinien używać traceback lub wywoływać loggera z exc_info=True lub brudzić sobie ręce sys.exc_info?

Cóż, tylko dlatego! Wszystkie one istnieją w różnych celach. Na przykład traceback.print_exc ' s wyjście jest nieco inne niż pakiety tworzone przez interpreter. Jeśli go użyjesz, pomylisz każdego, kto czyta Twoje dzienniki, będą uderzać o nie głowami.

Przekazywanie exc_info=True do logowania połączeń jest po prostu niewłaściwe. Ale , jest to przydatne przy wyłapywaniu możliwych do odzyskania błędów i chcesz je logować (używając np. poziomu INFO) również z traceback, ponieważ log.exception tworzy logi tylko jednego poziomu - ERROR.

I zdecydowanie powinieneś unikać zadzierasz z sys.exc_info tak bardzo, jak potrafisz. Po prostu nie jest to publiczny interfejs, tylko wewnętrzny - możesz go używać, jeśli na pewno wiesz, co robisz. Nie jest przeznaczony tylko do drukowania WYJĄTKÓW.

 23
Author: tosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-02 21:12:03

Oprócz odpowiedzi @Aaron Hall, jeśli logujesz się, ale nie chcesz używać logging.exception() (ponieważ loguje się na poziomie błędu), możesz użyć niższego poziomu i przekazać exc_info=True. np.

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)
 14
Author: Mark McDonald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-27 03:44:13

Nie widzę tego w żadnej innej odpowiedzi. Jeśli przekazujesz obiekt wyjątku z dowolnego powodu...

W Pythonie 3.5+ można uzyskać ślad z obiektu wyjątku za pomocą traceback.TracebackException.from_exception(). Na przykład:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

Jednak powyższy kod daje:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

To tylko dwa poziomy stosu, w przeciwieństwie do tego, co byłoby wydrukowane na ekranie, gdyby wyjątek został podniesiony w stack_lvl_2() i nie przechwycony (odkomentuj linię # raise).

Jak rozumiem, dzieje się tak dlatego, że wyjątek zapisuje tylko bieżący poziom stosu, gdy jest podniesiony, stack_lvl_3() w tym przypadku. W miarę przechodzenia przez stos, do jego __traceback__ dodawane są kolejne poziomy. Ale przechwyciliśmy go w stack_lvl_2(), co oznacza, że wszystko, co udało się nagrać, to poziom 3 i 2. Aby uzyskać pełny ślad jako wydrukowany na stdout musimy złapać go na najwyższym(NAJNIŻSZYM?) poziom:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

Które wyniki in:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

Zwróć uwagę, że druk stosu jest inny, brakuje pierwszej i ostatniej linii. Bo to jest inny format().

Przechwycenie wyjątku tak daleko od punktu, w którym został podniesiony, jak to możliwe, ułatwia kod, a jednocześnie daje więcej informacji.

 12
Author: bgdnlp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-01 11:24:52

Aby uzyskać precyzyjny ślad stosu, jako ciąg znaków, który zostałby podniesiony, gdyby nie było próby/except, po prostu umieść go w bloku except, który wychwytuje obrażający wyjątek.

desired_trace = traceback.format_exc(sys.exc_info())

Oto jak go użyć (zakładając, że flaky_func jest zdefiniowany i log wywoła Twój ulubiony system logowania):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

Dobrym pomysłem jest złapanie i ponowne podbicie KeyboardInterrupt s, aby nadal można było zabić program za pomocą Ctrl-C. Logowanie wykracza poza zakres pytanie, ale dobrym rozwiązaniem jest logowanie . Dokumentacja dla modułów sys i traceback .

 8
Author: Edward Newell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 18:35:03

Musisz umieścić try / except wewnątrz most innerloop gdzie może wystąpić błąd, tj.

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

... i tak dalej

Innymi słowy, będziesz musiał zawinąć instrukcje, które mogą się nie udać w try / except tak szczegółowe, jak to możliwe, w najbardziej wewnętrznej pętli, jak to możliwe.

 7
Author: Ivo van der Wijk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-13 17:10:16

Uwaga o ta ODPOWIEDŹ's komentarze: print(traceback.format_exc()) robi dla mnie lepszą robotę niż traceback.print_exc(). W tym drugim przypadku hello jest czasami dziwnie "mieszany" z tekstem traceback, na przykład jeśli oba chcą pisać na stdout lub stderr w tym samym czasie, generując dziwne wyniki (przynajmniej podczas budowania z edytora tekstu i przeglądania wyników w panelu "Build results").

Traceback (ostatnie wywołanie last):
Plik "C:\Users\User\Desktop\test.py", linia 7, w
hell do_stuff ()
Plik "C:\Users\User\Desktop\test.py", linia 4, w do_stuff
1/0
ZeroDivisionError: integer division or modulo by zero
o
[Zakończone w 0.1 s]

Więc używam:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
 7
Author: Basj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-06 17:18:38

traceback.format_exception

Jeśli masz tylko obiekt exception, możesz uzyskać ślad jako ciąg znaków z dowolnego punktu kodu w Pythonie 3 za pomocą:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

Pełny przykład:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

Wyjście:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

Dokumentacja: https://docs.python.org/3.7/library/traceback.html#traceback.format_exception

Zobacz także: Wyodrębnij informacje o śledzeniu z obiektu wyjątku

Testowany w Pythonie 3.7.3.

 6
Author: Ciro Santilli TRUMP BAN IS BAD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-17 09:19:06

Jeśli masz już obiekt błędu i chcesz wydrukować całość, musisz wykonać to nieco niezręczne wywołanie:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

Zgadza się, print_exception przyjmuje trzy argumenty pozycyjne: typ wyjątku, rzeczywisty obiekt wyjątku i jego wewnętrzna własność traceback.

W Pythonie 3.5 lub nowszym, {[3] } jest opcjonalne... ale jest to argument pozycyjny, więc nadal musisz wyraźnie przekazać żadnego w jego miejsce.

traceback.print_exception(None, err, err.__traceback__)

Nie mam pomysł, dlaczego to wszystko nie jest tylko traceback.print_exception(err). Dlaczego kiedykolwiek chcesz wydrukować błąd, wraz z innym śladem niż ten, który należy do tego błędu, jest poza mną.

 4
Author: nupanick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-26 02:43:57

Chcesz moduł traceback . Pozwoli Ci to drukować zrzuty stosów, tak jak to zwykle robi Python. W szczególności funkcja print_last wyświetli ostatni wyjątek i ślad stosu.

 3
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-13 17:25:22

To jest moje rozwiązanie, aby zapisać błąd w pliku dziennika, a także na konsoli:

import logging, sys
import traceback
logging.basicConfig(filename='error.log', level=logging.DEBUG)

def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    exc_info=(exc_type, exc_value, exc_traceback)
    logging.critical("\nDate:" + str(datetime.datetime.now()), exc_info=(exc_type, exc_value, exc_traceback))
    print("An error occured, check error.log to see the error details")
    traceback.print_exception(*exc_info)


sys.excepthook = handle_exception
 0
Author: Ahmad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-10 12:50:14

Rozwiązanie Python 3

Stacktrace_helper.py

from linecache import getline
import sys
import traceback


def get_stack_trace():
    exc_type, exc_value, exc_tb = sys.exc_info()
    trace = traceback.format_stack()
    trace = list(filter(lambda x: ("\\lib\\" not in x and "/lib/" not in x and "stacktrace_helper.py" not in x), trace))
    ex_type = exc_type.__name__
    ex_line = exc_tb.tb_lineno
    ex_file = exc_tb.tb_frame.f_code.co_filename
    ex_message = str(exc_value)
    line_code = ""
    try:
        line_code = getline(ex_file, ex_line).strip()
    except:
        pass

    trace.insert(
        0, f'File "{ex_file}", line {ex_line}, line_code: {line_code} , ex: {ex_type} {ex_message}',
    )
    return trace


def get_stack_trace_str(msg: str = ""):
    trace = list(get_stack_trace())
    trace_str = "\n".join(list(map(str, trace)))
    trace_str = msg + "\n" + trace_str
    return trace_str

 0
Author: Bar Nuri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-16 13:34:26

Możesz zrobić:

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    raise err
 0
Author: A.I.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-20 22:14:14