Hoe de volledige uitzonderingspraceBack te vangen en af ​​te drukken zonder het programma te stoppen / verlaten?

Ik wil uitzonderingen vangen en loggen zonder te verlaten, b.v.,

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

Ik wil de exact dezelfde uitvoer afdrukken die wordt afgedrukt wanneer de uitzondering wordt verhoogd zonder het PROBEREN..Eet onderscheppen van de uitzondering, en ik doe niet WILD het om mijn programma te verlaten. Hoe doe ik dit?


1, Autoriteit 100%

Een ander antwoord heeft al op gewezen op de traceback module.

Merk op dat met print_exc, in sommige hoekcases, u zult niet verkrijgen wat u zou verwachten. In Python 2.x:

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

… wordt de traceerback van de laatste weergegeven:

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

Als u echt toegang hebt tot het origineel Traceback Eén oplossing is om de uitzondering-info te cachen zoals geretourneerd van exc_infoin een lokale variabele en display het met 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

Produceren:

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

Er zijn hier echter weinig valkuilen:

  • Uit het document van sys_info:

    Het toewijzen van de traceback-retourwaarde aan een lokale variabele in een functie die een uitzondering verwerkt, zal een circulaire verwijzingveroorzaken. Dit voorkomt dat alles waarnaar wordt verwezen door een lokale variabele in dezelfde functie of door de traceback, wordt verzameld. […] Als je de traceback toch nodig hebt, zorg er dan voor dat je deze na gebruik verwijdert(best gedaan met een try …final statement)

  • maar, uit hetzelfde document:

    Vanaf Python 2.2 worden dergelijke cycli automatisch teruggewonnenwanneer afvalinzameling is ingeschakeld en ze onbereikbaar worden, maar het blijft efficiënter om het maken van cycli te vermijden.


Aan de andere kant, door u toegang te geven tot de traceback geassocieerd meteen uitzondering, levert Python 3 een minder verrassend resultaat op:

import traceback
try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass
    traceback.print_tb(err.__traceback__)

… wordt weergegeven:

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

Antwoord 2, autoriteit 94%

traceback.format_exc()of sys.exc_info()zal meer informatie opleveren als u dat wilt.

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

Antwoord 3, autoriteit 40%

Als u aan het debuggen bent en alleen de huidige stacktracering wilt zien, kunt u eenvoudig bellen:

traceback.print_stack()

Het is niet nodig om handmatig een uitzondering op te heffen om deze opnieuw op te vangen.


Antwoord 4, autoriteit 20%

Hoe de volledige traceback afdrukken zonder het programma te stoppen?

Als je je programma niet wilt stoppen bij een fout, moet je die fout afhandelen met een try/behalve:

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

Om de volledige traceback te extraheren, gebruiken we de module tracebackuit de standaardbibliotheek:

import traceback

En om een behoorlijk gecompliceerde stacktrace te maken om aan te tonen dat we de volledige stacktrace krijgen:

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

Afdrukken

Gebruik de traceback.print_excmethode om de volledige traceback te afdrukken:

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

Welke afdrukken:

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!

Beter dan printen, loggen:

Een best practice is echter om een logger voor uw module in te stellen. Het kent de naam van de module en kan niveaus wijzigen (onder andere attributen, zoals handlers)

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

In dat geval wilt u in plaats daarvan de functie logger.exception:

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

Welke logboeken:

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!

Of misschien wil je gewoon de string, in dat geval wil je in plaats daarvan de functie traceback.format_exc:

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

Welke logboeken:

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!

Conclusie

En voor alle drie de opties zien we dat we dezelfde uitvoer krijgen als wanneer we een fout hebben:

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

Welke te gebruiken

Prestatieproblemen zijn hier niet belangrijk, aangezien IO meestal domineert. Ik heb liever, omdat het precies doet wat wordt gevraagd op een voorwaarts compatibele manier:

logger.exception(error)

Logniveaus en outputs kunnen worden aangepast, waardoor het gemakkelijk uit te schakelen is zonder de code aan te raken. En meestal is doen wat direct nodig is de meest efficiënte manier om het te doen.


Antwoord 5, autoriteit 3%

Als aanvulling op Aaron Hall’s antwoord, als u aan het loggen bent, maar geen gebruik wilt maken van logging.exception()(aangezien het op het ERROR-niveau logt), kunt u een lager niveau gebruiken en exc_info=Truedoorgeven. bijv.

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

Antwoord 6, autoriteit 2%

Ik zie dit in geen van de andere antwoorden vermeld. Als je om wat voor reden dan ook een Exception-object doorgeeft…

In Python 3.5+ kun je een tracering krijgen van een Exception-object met behulp van traceback.TracebackException.from_exception(). Bijvoorbeeld:

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

De bovenstaande code resulteert echter in:

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

Dit zijn slechts twee niveaus van de stapel, in tegenstelling tot wat er op het scherm zou zijn afgedrukt als de uitzondering was verhoogd in stack_lvl_2()en niet was onderschept (verwijder commentaar op de # raiseregel).

Zoals ik het begrijp, is dat omdat een uitzondering alleen het huidige niveau van de stapel registreert wanneer deze wordt verhoogd, in dit geval stack_lvl_3(). Terwijl het terug door de stapel gaat, worden er meer niveaus toegevoegd aan zijn __traceback__. Maar we hebben het onderschept in stack_lvl_2(), wat betekent dat het alleen niveau 3 en 2 kon opnemen. Om het volledige spoor te krijgen zoals afgedrukt op stdout, zouden we het op de hoogste (laagste? ) niveau:

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

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

Merk op dat de stapelafdruk anders is, de eerste en laatste lijnen ontbreken. Omdat het een verschillend format().

Intercepteer de uitzondering zo ver weg van het punt waar het zo mogelijk is aangevoerd zorgt voor eenvoudiger code, terwijl hij ook meer informatie geeft.


7, Autoriteit 2%

traceback.format_exception(exception_object)

Als u alleen het uitzonderingsobject hebt, kunt u de traceerback krijgen als een tekenreeks vanaf elk punt van de code in Python 3 met:

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

Volledig voorbeeld:

#!/usr/bin/env python3
import traceback
def f():
    g()
def g():
    raise Exception('asdf')
try:
    g()
except Exception as e:
    exc_obj = e
tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

Uitgang:

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

Documentatie: https://docs.python.org/3.9 /Library/traceBack.html#traceBack.Format_Exception

Zie ook: Traceback-informatie uit een uitzonderingsobject

Getest in Python 3.9


8

U moet het proberen / behalve in de meeste innersloop plaatsen waar de fout kan optreden, d.w.z.

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

… enzovoort

Met andere woorden, u moet instructies inpakken die mogelijk falen in het proberen / behalve zo specifiek mogelijk, in de meest innerlijke lus mogelijk.


9

een opmerking over Dit antwoord Opmerkingen: print(traceback.format_exc())een betere baan voor mij dan traceback.print_exc(). Met de laatste is de hellosoms vreemd “gemengd” met de Traceback-tekst, zoals als beide tegelijkertijd naar StDout of Stderr willen schrijven, waardoor rare output wordt geproduceerd (tenminste bij het bouwen van binnen Teksteditor en het bekijken van de uitvoer in het paneel “Build Resultaten”).

Traceback (meest recente oproep Laatste):
Bestand “C: \ gebruikers \ user \ Desktop \ test.py”, lijn 7, in
hel do_stuff ()
Bestand “C: \ gebruikers \ user \ desktop \ test.py”, lijn 4, in do_stuff
1/0
ZerodivisionError: integer divisie of modulo door nul
O
[Voltooid in 0.1S]

Dus ik gebruik:

import traceback, sys
def do_stuff():
    1/0
try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')

10

Als u al een foutobject hebt en u het hele ding wilt afdrukken, moet u deze enigszins ongemakkelijke oproep doen:

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

Dat klopt, print_exceptionneemt drie positionele argumenten: het type uitzondering, het eigenlijke uitzonderingsobject en de eigen interne traceback-eigenschap van het uitzondering.

In python 3.5 of hoger is het type(err)optioneel… maar het is een positioneel argument, dus je moet nog steeds expliciet Geen in zijn plaats doorgeven.

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

Ik heb geen idee waarom dit allemaal niet gewoon traceback.print_exception(err)is. Waarom je ooit een fout zou willen afdrukken, samen met een andere traceback dan degene die bij die fout hoort, is mij een raadsel.


Antwoord 11

Je wilt de traceback-module. Hiermee kunt u stapeldumps afdrukken zoals Python dat normaal doet. In het bijzonder zal de functie print_lastde laatste uitzondering en een stapel afdrukken traceren.


Antwoord 12

Dit is mijn oplossing om de fout in een logbestand en ook op de console te schrijven:

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

Antwoord 13

python 3-oplossing

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

Antwoord 14

Je zou kunnen doen:

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

Other episodes