1466 Stimmen

Wie kann man den vollständigen Traceback der Ausnahme abfangen und ausgeben, ohne das Programm anzuhalten/zu beenden?

Ich möchte Ausnahmen abfangen und protokollieren, ohne zu beenden, z. B.,

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

Ich möchte genau die gleiche Ausgabe drucken, die gedruckt wird, wenn die Ausnahme ausgelöst wird, ohne dass die try/except die Ausnahme abfängt, und ich tue no Ich möchte, dass es mein Programm beendet.

17 Stimmen

Das ist zwar keine vollständige Antwort, aber vielleicht möchte jemand wissen, dass man viele Informationen erhalten kann, wenn man in err.__traceback__ (zumindest in Python 3.x)

35 Stimmen

Es wurde 825.000 Mal aufgerufen, während man versuchte, herauszufinden, wie man seine Stacktraces ausdrucken kann. Das ist ein weiteres Zen von Python.

13 Stimmen

Anscheinend bin ich der Einzige auf der Welt, der den Stapel ausdrucken möchte. wenn kein Fehler vorliegt (= nur um zu sehen, wie ich genau zu dieser Zeile gekommen bin (es ist nicht mein Code, und er ist so hässlich, dass ich nicht weiß, wie er hierher gekommen ist!)).

1315voto

volting Punkte 14083

traceback.format_exc() o sys.exc_info() liefert mehr Informationen, wenn Sie das wollen.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

23 Stimmen

print(sys.exc_info()[0] druckt <class 'Exception'> .

16 Stimmen

Verwenden Sie nicht exc... der Traceback enthält alle Informationen stackoverflow.com/questions/4564559/

1 Stimmen

Es mag erwartet werden (oder auch nicht), aber dies erzeugt keinen vollständigen Traceback. Es ist durch die Punkte begrenzt, die die Ausnahme auslösen und sie behandeln (Python 3.8). Sie müssen nicht try / catch a den Traceback anzeigen .

851voto

Sylvain Leroux Punkte 47483

In einigen anderen Antworten wurde bereits auf die Traceback Modul.

Bitte beachten Sie, dass bei print_exc In einigen Eckfällen werden Sie nicht das erhalten, was Sie erwarten würden. In Python 2.x:

import traceback

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

    traceback.print_exc()

...zeigt den Traceback des Befehls zuletzt Ausnahme:

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

Wenn Sie wirklich auf das Original zugreifen müssen Traceback Eine Lösung ist die Zwischenspeicherung der Ausnahme-Infos wie zurückgegeben von exc_info in einer lokalen Variablen und zeigen Sie sie mit 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

Produzieren:

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

Allerdings gibt es dabei einige Fallstricke:

  • Aus dem Dokument von sys_info :

    Das Zuweisen des Traceback-Rückgabewerts an eine lokale Variable in einer Funktion, die eine Ausnahme behandelt, führt zu einer Zirkelbezug . Dadurch wird verhindert, dass alles, worauf eine lokale Variable in derselben Funktion oder der Traceback verweist, in den Garbage Collect gelangt. [...] Wenn Sie den Traceback benötigen, stellen Sie sicher, dass Sie ihn nach der Verwendung löschen (am besten mit einem Versuch ... endlich Aussage)

  • sondern aus demselben Dokument:

    Ab Python 2.2 werden solche Zyklen automatisch zurückgewonnen wenn die Müllabfuhr aktiviert ist und sie unerreichbar werden, aber es ist immer noch effizienter, die Erzeugung von Zyklen zu vermeiden.


Andererseits können Sie durch den Zugriff auf den Traceback verbunden mit eine Ausnahme, Python 3 liefert ein weniger überraschendes Ergebnis:

import traceback

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

    traceback.print_tb(err.__traceback__)

... wird angezeigt:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")

367voto

dimo414 Punkte 44554

Wenn Sie debuggen und nur den aktuellen Stack-Trace sehen wollen, können Sie einfach aufrufen:

traceback.print_stack()

Es ist nicht nötig, eine Ausnahme manuell auszulösen, nur um sie wieder abzufangen.

15 Stimmen

Das Traceback-Modul tut genau das - eine Ausnahme auslösen und abfangen.

6 Stimmen

Die Ausgabe erfolgt übrigens standardmäßig nach STDERR. Es erschien nicht in meinen Protokollen, weil es an eine andere Stelle umgeleitet wurde.

1 Stimmen

@pppery Ich kann es mit Python 3.8 nicht sehen. Und die Sache mit try y catch ist, dass es nicht den vollständigen Traceback anzeigt, nur von raise a except .

227voto

Wie kann man den vollständigen Traceback ausdrucken, ohne das Programm anzuhalten?

Wenn Sie Ihr Programm bei einem Fehler nicht anhalten wollen, müssen Sie diesen Fehler mit einem try/except behandeln:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

Um den vollständigen Traceback zu extrahieren, verwenden wir die traceback Modul aus der Standardbibliothek:

import traceback

Und um einen anständig komplizierten Stacktrace zu erstellen, um zu demonstrieren, dass wir den vollständigen Stacktrace erhalten:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Drucken

An drucken den vollständigen Traceback, verwenden Sie die traceback.print_exc Methode:

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

Welche Drucke:

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!

Besser als Drucken, Protokollieren:

Am besten ist es jedoch, einen Logger für Ihr Modul einzurichten. Er kennt den Namen des Moduls und ist in der Lage, die Ebenen zu ändern (neben anderen Attributen, wie z. B. Handler)

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

In diesem Fall müssen Sie die logger.exception stattdessen die Funktion:

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

Welche Protokolle:

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!

Oder vielleicht wollen Sie nur die Zeichenkette, in diesem Fall brauchen Sie die traceback.format_exc stattdessen die Funktion:

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

Welche Protokolle:

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!

Schlussfolgerung

Bei allen drei Optionen erhalten wir die gleiche Ausgabe wie bei einem Fehler:

>>> 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!

Was zu verwenden ist

Leistungsaspekte sind hier nicht wichtig, da IO normalerweise dominiert. Ich würde es bevorzugen, da es genau das tut, was in einer vorwärtskompatiblen Weise angefordert wird:

logger.exception(error)

Logging-Level und -Outputs können angepasst werden, so dass sie leicht ausgeschaltet werden können, ohne den Code zu berühren. Und in der Regel ist es am effizientesten, nur das zu tun, was direkt benötigt wird.

7 Stimmen

Wie oben gesagt und auch für mich, traceback.print_exc() gibt nur den letzten Aufruf zurück: Wie kann man mehrere Ebenen des Stapels (und möglicherweise alle Ebenen) zurückgeben?

0 Stimmen

@geekobi Ich bin mir nicht sicher, worauf Sie hinauswollen. Ich zeige, dass wir den Traceback bis zum Einstiegspunkt des Programms/Interpreters erhalten. Was ist Ihnen nicht klar?

2 Stimmen

Was @geekobi damit sagen will, ist, dass traceback.print_exc() bei einem Catch und Re-Raise nur den Re-Raise-Stack zurückgibt, nicht den ursprünglichen Stack.

51voto

tosh Punkte 1663

Erstens: Verwenden Sie nicht print s für die Protokollierung gibt es ein stabiles, bewährtes und gut durchdachtes stdlib Modul, um dies zu tun: logging . Sie sind definitiv sollte stattdessen verwenden.

Zweitens: Lassen Sie sich nicht dazu verleiten, eine Chaos mit unverbundenen Werkzeugen, wenn es einen nativen und einfachen Ansatz gibt. Hier ist er:

log = logging.getLogger(__name__)

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

Das war's. Sie sind jetzt fertig.

Erklärungen für alle, die sich dafür interessieren, wie die Dinge unter der Haube funktionieren

Was log.exception ist eigentlich nur ein Aufruf an log.error (d.h. Protokollereignis mit Stufe ERROR ) und print traceback then.

Warum ist es besser?

Nun, hier sind einige Überlegungen:

  • es ist einfach derecha ;
  • es ist ganz einfach;
  • es ist einfach.

Warum sollte niemand die traceback oder Anruflogger mit exc_info=True oder sich die Hände schmutzig machen mit sys.exc_info ?

Na ja, einfach so! Es gibt sie alle für unterschiedliche Zwecke. Zum Beispiel, traceback.print_exc unterscheidet sich ein wenig von den Rückverfolgungen, die der Interpreter selbst erzeugt. Wenn Sie es verwenden, werden Sie jeden verwirren, der Ihre Protokolle liest, er wird sich den Kopf daran stoßen.

Weitergabe exc_info=True Anrufe zu protokollieren, ist einfach unangemessen. Aber ist es nützlich, wenn Sie behebbare Fehler abfangen und diese protokollieren wollen (z.B. mit INFO Ebene) auch mit Tracebacks, denn log.exception erzeugt Protokolle von nur einer Ebene - ERROR .

Und Sie sollten es auf jeden Fall vermeiden, sich mit sys.exc_info so viel Sie können. Es handelt sich nicht um eine öffentliche Schnittstelle, sondern um eine interne - Sie kann Verwenden Sie es, wenn Sie genau wissen, was Sie tun. Es ist nicht nur für das Drucken von Ausnahmen gedacht.

17 Stimmen

Es funktioniert auch nicht so, wie es ist. Das ist noch nicht alles. Ich bin noch nicht fertig: Diese Antwort ist reine Zeitverschwendung.

0 Stimmen

Ich würde auch hinzufügen, dass Sie einfach tun können logging.exception() . Es ist nicht notwendig, eine Instanz des Protokolls zu erstellen, es sei denn, Sie haben besondere Anforderungen.

20 Stimmen

Ich finde diese Antwort irgendwie lächerlich. Sie ist voll von "tu dies/ tu das nicht, nur weil", ohne zu erklären, warum. Ihre Punkte in "Warum ist es besser?" sagen praktisch alle das Gleiche: "Weil es so ist." Das finde ich nicht hilfreich. Du hast wenigstens ein bisschen was erklärt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X