Jak wydrukować pełny traceback bez zatrzymywania programu?

Piszę program, który parsuje 10 stron internetowych, lokalizuje pliki danych, zapisuje pliki, a następnie przetwarza je, aby dane mogły być łatwo używane w bibliotece NumPy. Istnieje mnóstwo błędów, które ten plik napotka poprzez złe linki, źle uformowany XML, brakujące wpisy i inne rzeczy, które jeszcze nie skategoryzowałem. Początkowo zrobiłem ten program do obsługi błędów takich jak:

try:
    do_stuff()
except:
    pass

Ale teraz chcę rejestrować błędy:

try:
    do_stuff()
except Exception, err:
    print Exception, err

Uwaga To jest drukowanie do pliku dziennika na później recenzja. To zwykle drukuje bardzo bezużyteczne dane. Chcę wydrukować dokładnie te same linie wydrukowane, gdy błąd uruchomi się bez próby-z wyjątkiem przechwycenia wyjątku,ale nie chcę, aby zatrzymał mój program, ponieważ jest zagnieżdżony w serii pętli for, które chciałbym zakończyć.

8 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!")
 342
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()[0])
 534
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
2016-06-14 21:03:52

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.

 165
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

To print the full traceback, 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, w takim przypadku, będziesz chcemy zamiast tego funkcji traceback.format_exc:

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!
 59
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
2016-03-23 01:50:19

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 .

 6
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.

 5
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

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:
    logger.info('General exception noted.', exc_info=True)
 4
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
2018-04-24 03:19:02

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.

 2
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