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

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

14 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!)).

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.

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

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

30voto

Mark McDonald Punkte 7208

Zusätzlich zu Aaron Hall's Antwort wenn Sie protokollieren, aber nicht die logging.exception() (da es auf der Ebene ERROR protokolliert), können Sie eine niedrigere Ebene verwenden und exc_info=True . z.B..

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)

0 Stimmen

Dies ist auch gut, wenn es um einen erkannten Fehler bei der Protokollierung geht... d.h. wenn Sie aus irgendeinem Grund versäumt haben, ein aktuelles Logger-Objekt zu erstellen.

28voto

bgdnlp Punkte 863

Ich sehe das in keiner der anderen Antworten erwähnt. Wenn Sie aus irgendeinem Grund ein Exception-Objekt weitergeben...

In Python 3.5+ können Sie einen Trace von einem Exception-Objekt erhalten, indem Sie traceback.TracebackException.from_exception() . Zum Beispiel:

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()))

Der obige Code führt jedoch zu:

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')

Dies sind nur zwei Ebenen des Stapels, im Gegensatz zu dem, was auf dem Bildschirm ausgegeben worden wäre, wenn die Ausnahme in stack_lvl_2() und nicht abgefangen werden (entfernen Sie das Kommentarzeichen # raise Zeile).

So wie ich es verstehe, liegt das daran, dass eine Ausnahme nur den aktuellen Stand des Stacks aufzeichnet, wenn sie ausgelöst wird, stack_lvl_3() in diesem Fall. Bei der Weitergabe durch den Stapel werden weitere Ebenen zu seinem __traceback__ . Aber wir haben es abgefangen in stack_lvl_2() Das heißt, es konnten nur die Stufen 3 und 2 aufgezeichnet werden. Um die vollständige Aufzeichnung auf stdout zu erhalten, müssten wir sie auf der höchsten (niedrigsten?) Stufe abfangen:

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()))

Daraus ergibt sich:

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')

Beachten Sie, dass der Stapeldruck anders ist, die erste und die letzte Zeile fehlen. Da es sich um eine diferente format() .

Das Abfangen der Ausnahme so weit wie möglich von dem Punkt entfernt, an dem sie ausgelöst wurde, sorgt für einfacheren Code und liefert gleichzeitig mehr Informationen.

0 Stimmen

Das ist viel besser als die vorherige(n) Methode(n), aber immer noch lächerlich umständlich, nur um einen Stacktrace auszugeben. Java braucht weniger Code F

24voto

soumya-kole Punkte 671

In Python 3 (funktioniert in 3.9) können wir eine Funktion definieren, die wir überall dort verwenden können, wo wir die Details ausgeben wollen.

import traceback

def get_traceback(e):
    lines = traceback.format_exception(type(e), e, e.__traceback__)
    return ''.join(lines)

try:
    1/0
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

try:
    spam(1,2)
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

Die Ausgabe würde wie folgt aussehen:

bash-3.2$ python3 /Users/soumyabratakole/PycharmProjects/pythonProject/main.py
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 26, in <module>
    1/0
ZeroDivisionError: division by zero

------End--------
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 33, in <module>
    spam(1,2)
NameError: name 'spam' is not defined

------End--------

15voto

nupanick Punkte 541

Wenn Sie bereits ein Error-Objekt haben und das Ganze ausdrucken wollen, müssen Sie diesen etwas umständlichen Aufruf machen:

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

Das ist richtig, print_exception nimmt drei positionale Argumente: Der Typ der Ausnahme, das eigentliche Ausnahmeobjekt und die interne Traceback-Eigenschaft der Ausnahme.

In Python 3.5 oder höher kann die type(err) ist optional... aber es ist ein Positionsargument, so dass Sie immer noch explizit None an seiner Stelle übergeben müssen.

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

Ich habe keine Ahnung, warum das alles nicht einfach traceback.print_exception(err) . Warum man einen Fehler zusammen mit einem anderen Traceback als dem, der zu diesem Fehler gehört, ausgeben möchte, ist mir schleierhaft.

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