Ausnahme mit Traceback in Python protokollieren

Lesezeit: 9 Minuten

Benutzer-Avatar
ZEITX

Wie kann ich meine Python-Fehler protokollieren?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?

Benutzer-Avatar
nosklo

Verwenden logging.exception aus dem except: Handler/Block, um die aktuelle Ausnahme zusammen mit den Ablaufverfolgungsinformationen zu protokollieren, denen eine Nachricht vorangestellt ist.

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)

logging.debug('This message should go to the log file')

try:
    run_my_stuff()
except:
    logging.exception('Got exception on main handler')
    raise

Wenn Sie sich jetzt die Protokolldatei ansehen, /tmp/logging_example.out:

DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
  File "/tmp/teste.py", line 9, in <module>
    run_my_stuff()
NameError: name 'run_my_stuff' is not defined

  • Ich habe den Django-Code dafür durchgesehen, und ich nehme an, die Antwort ist nein, aber gibt es eine Möglichkeit, die Rückverfolgung auf eine bestimmte Menge von Zeichen oder Tiefe zu beschränken? Das Problem ist, dass es bei großen Rückverfolgungen ziemlich lange dauert.

    – Eduard Lukas

    21. Mai 2014 um 13:16 Uhr


  • Beachten Sie, dass wenn Sie einen Logger mit definieren logger = logging.getLogger('yourlogger')Du musst schreiben logger.exception('...')damit das funktioniert…

    – 576i

    5. Februar 2017 um 12:43 Uhr

  • Können wir dies so ändern, dass die Nachricht mit Protokollebene INFO gedruckt wird?

    – NM

    31. Oktober 2017 um 9:38 Uhr

  • Beachten Sie, dass für bestimmte externe Apps, z. B. Azure Insight, der Trackback nicht in den Protokollen gespeichert wird. Es ist dann notwendig, sie explizit an die Nachrichtenzeichenfolge zu übergeben, wie unten gezeigt.

    – Edgar H

    16. Juli 2020 um 7:55 Uhr

  • Ich hatte immer gedacht logging.exception(...) (oder logger.exception(...) wenn so konfiguriert) würde es tun. Aber ich bin ein bisschen verwirrt mit dieser Methode geworden exception im Kontext eines Ausnahme-Hook-Handlers: Es scheint den Traceback in diesem Kontext nicht auszudrucken. Beginnen Sie zu vermuten, dass Ihr Anrufcode in einem sein muss except blockieren, damit dies funktioniert.

    – Mike Nagetier

    10. April 2021 um 17:49 Uhr

Benutzer-Avatar
Flycee

Verwenden exc_info Optionen können besser sein, bleibt Warnung oder Fehlertitel:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)

  • Ich kann mich nie erinnern, was die exc_info= kwarg heißt; Danke!

    – Berto

    31. Oktober 2017 um 15:00 Uhr

  • Dies ist identisch mit der Protokollierung.Ausnahme, mit der Ausnahme, dass der Typ zweimal redundant protokolliert wird. Verwenden Sie einfach “logging.exception”, es sei denn, Sie möchten eine andere Ebene als “Error”.

    – Wyrmholz

    17. November 2017 um 16:11 Uhr

  • @Wyrmwood, es ist nicht identisch, da Sie eine Nachricht übermitteln müssen logging.exception

    – Peter Holz

    21. April 2018 um 7:30 Uhr

  • Verwenden logging.errorkann man unangenehme Nachrichten wie vermeiden '' oder ' ' zu logging.exception.

    – Nuklear03020704

    16. April 2021 um 2:34 Uhr

Benutzer-Avatar
Brad Barrows

Mein Job hat mich kürzlich damit beauftragt, alle Tracebacks/Ausnahmen aus unserer Anwendung zu protokollieren. Ich habe zahlreiche Techniken ausprobiert, die andere online gepostet hatten, wie die obige, aber mich für einen anderen Ansatz entschieden. Überschreiben traceback.print_exception.

Ich habe ein Schreiben auf http://www.bbarrows.com/ Das wäre viel einfacher zu lesen, aber ich werde es auch hier einfügen.

Als ich damit beauftragt wurde, alle Ausnahmen zu protokollieren, auf die unsere Software in freier Wildbahn stoßen könnte, habe ich eine Reihe verschiedener Techniken ausprobiert, um unsere Python-Ausnahmerückverfolgungen zu protokollieren. Zuerst dachte ich, dass der Ausnahmehaken des Python-Systems, sys.excepthook, der perfekte Ort wäre, um den Protokollierungscode einzufügen. Ich habe etwas Ähnliches versucht:

import traceback
import StringIO
import logging
import os, sys

def my_excepthook(excType, excValue, traceback, logger=logger):
    logger.error("Logging an uncaught exception",
                 exc_info=(excType, excValue, traceback))

sys.excepthook = my_excepthook  

Dies funktionierte für den Hauptthread, aber ich stellte bald fest, dass my sys.excepthook in keinem neuen Thread vorhanden war, den mein Prozess gestartet hatte. Dies ist ein großes Problem, da fast alles in Threads in diesem Projekt passiert.

Nachdem ich viel Dokumentation gegoogelt und gelesen hatte, waren die hilfreichsten Informationen, die ich fand, aus dem Python Issue Tracker.

Der erste Beitrag im Thread zeigt ein funktionierendes Beispiel für die sys.excepthook NICHT über Threads hinweg bestehen bleiben (wie unten gezeigt). Anscheinend ist dies ein erwartetes Verhalten.

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0

threading.Thread(target=foo).start()

Die Nachrichten in diesem Python-Problem-Thread führen wirklich zu 2 vorgeschlagenen Hacks. Entweder Unterklasse Thread und packen Sie die run-Methode in unseren eigenen try except block, um Exceptions oder Monkey Patch abzufangen und zu protokollieren threading.Thread.run in Ihrem eigenen Versuch auszuführen, außer zu blockieren und die Ausnahmen zu protokollieren.

Die erste Methode der Unterklassenbildung Thread scheint mir in Ihrem Code weniger elegant zu sein, da Sie Ihre benutzerdefinierten importieren und verwenden müssten Thread Klasse ÜBERALL, wo Sie einen Protokollierungsthread haben wollten. Dies war ein Ärgernis, da ich unsere gesamte Codebasis durchsuchen und alle normalen ersetzen musste Threads mit diesem Brauch Thread. Es war jedoch klar, worum es ging Thread funktionierte und für jemanden einfacher zu diagnostizieren und zu debuggen wäre, wenn etwas mit dem benutzerdefinierten Protokollierungscode schief gelaufen wäre. Ein benutzerdefinierter Protokollierungsthread könnte folgendermaßen aussehen:

class TracebackLoggingThread(threading.Thread):
    def run(self):
        try:
            super(TracebackLoggingThread, self).run()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception, e:
            logger = logging.getLogger('')
            logger.exception("Logging an uncaught exception")

Die zweite Methode des Monkey Patching threading.Thread.run ist schön, weil ich es einfach mal direkt hinterher laufen lassen könnte __main__ und instrumentiere meinen Logging-Code in allen Ausnahmen. Das Debuggen von Monkey-Patches kann jedoch lästig sein, da es die erwartete Funktionalität von etwas ändert. Der vorgeschlagene Patch vom Python Issue Tracker war:

def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html

(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init

Erst als ich anfing, meine Ausnahmeprotokollierung zu testen, wurde mir klar, dass ich alles falsch gemacht hatte.

Zum Testen hatte ich einen platziert

raise Exception("Test")

irgendwo in meinem Code. Das Umschließen einer Methode, die diese Methode aufrief, war jedoch ein Try-Except-Block, der das Traceback ausgab und die Ausnahme verschluckte. Das war sehr frustrierend, weil ich gesehen habe, dass der Traceback auf STDOUT gedruckt, aber nicht protokolliert wurde. Ich entschied dann, dass eine viel einfachere Methode zum Protokollieren der Tracebacks darin bestand, einfach die Methode zu patchen, die der gesamte Python-Code verwendet, um die Tracebacks selbst zu drucken, traceback.print_exception. Ich endete mit etwas Ähnlichem wie dem Folgenden:

def add_custom_print_exception():
    old_print_exception = traceback.print_exception
    def custom_print_exception(etype, value, tb, limit=None, file=None):
        tb_output = StringIO.StringIO()
        traceback.print_tb(tb, limit, tb_output)
        logger = logging.getLogger('customLogger')
        logger.error(tb_output.getvalue())
        tb_output.close()
        old_print_exception(etype, value, tb, limit=None, file=None)
    traceback.print_exception = custom_print_exception

Dieser Code schreibt den Traceback in einen String-Puffer und protokolliert ihn zum Protokollieren von ERROR. Ich habe einen benutzerdefinierten Protokollierungs-Handler eingerichtet, der den Logger „customLogger“ eingerichtet hat, der die Protokolle der ERROR-Ebene nimmt und sie zur Analyse nach Hause sendet.

  • Ein durchaus interessanter Ansatz. Eine Frage – add_custom_print_exception scheint sich nicht auf der Seite zu befinden, auf die Sie verlinkt haben, und stattdessen gibt es dort einen ganz anderen endgültigen Code. Welches ist Ihrer Meinung nach besser / endgültiger und warum? Vielen Dank!

    – fantastisch

    31. Juli 2014 um 0:53 Uhr

  • Danke, tolle Antwort!

    – 101

    12. Januar 2016 um 3:45 Uhr

  • Es gibt einen Ausschneide- und Einfügefehler. Beim delegierten Aufruf von old_print_exception sollten limit und file übergeben werden limit und file, nicht None — old_print_exception(etype, value, tb, limit, file)

    – Marwin

    27. Mai 2016 um 15:15 Uhr


  • Für Ihren letzten Codeblock können Sie einfach aufrufen, anstatt ein StringIO zu initialisieren und die Ausnahme davon auszugeben logger.error(traceback.format_tb()) (oder format_exc(), wenn Sie auch die Ausnahmeinformationen wünschen).

    – James

    6. Oktober 2018 um 21:01 Uhr


  • Ihr geteilter Link funktioniert jetzt nicht. Ich bin auch nicht in der Lage, das Traceback mit der obigen Methode zu protokollieren. Ich rief raise Exception im Code und vor dem habe ich die von Ihnen definierte Methode aufgerufen. Nichts passiert. @Brad und @Christian

    – Vaibhav Grover

    19. April 2021 um 6:07 Uhr


Sie können alle nicht abgefangenen Ausnahmen im Hauptthread protokollieren, indem Sie einen Handler zuweisen sys.excepthookvielleicht mit der exc_info Parameter der Logging-Funktionen von Python:

import sys
import logging

logging.basicConfig(filename="/tmp/foobar.log")

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        "Uncaught exception",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

raise Exception('Boom')

Wenn Ihr Programm jedoch Threads verwendet, beachten Sie, dass Threads erstellt mit threading.Thread Wille nicht Abzug sys.excepthook wenn eine nicht abgefangene Ausnahme in ihnen auftritt, wie in angegeben Ausgabe 1230540 im Issue-Tracker von Python. Dort wurden einige Hacks vorgeschlagen, um diese Einschränkung zu umgehen, z. B. Monkey-Patching Thread.__init__ zu überschreiben self.run mit Alternative run Methode, die das Original in a umschließt try blockieren und anrufen sys.excepthook aus dem Inneren except Block. Alternativ könnten Sie den Einstiegspunkt für jeden Ihrer Threads einfach manuell einschließen try/except dich selbst.

Was ich gesucht habe:

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

Sehen:

Sie können die Rückverfolgung mit einem Logger auf jeder Ebene (DEBUG, INFO, …) abrufen. Beachten Sie, dass mit logging.exceptionder Pegel ist FEHLER.

# test_app.py
import sys
import logging

logging.basicConfig(level="DEBUG")

def do_something():
    raise ValueError(":(")

try:
    do_something()
except Exception:
    logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
  File "test_app.py", line 10, in <module>
    do_something()
  File "test_app.py", line 7, in do_something
    raise ValueError(":(")
ValueError: :(

BEARBEITEN:

Dies funktioniert auch (mit Python 3.6)

logging.debug("Something went wrong", exc_info=True)

Benutzer-Avatar
Markus Amery

Nicht erfasste Ausnahmemeldungen werden an STDERR gesendet. Anstatt also Ihre Protokollierung in Python selbst zu implementieren, könnten Sie STDERR an eine Datei senden, indem Sie die Shell verwenden, die Sie zum Ausführen Ihres Python-Skripts verwenden. In einem Bash-Skript können Sie dies mit der Ausgabeumleitung tun, als im BASH-Leitfaden beschrieben.

Beispiele

Fehler an Datei anhängen, andere Ausgabe an das Terminal:

./test.py 2>> mylog.log

Datei mit verschachtelter STDOUT- und STDERR-Ausgabe überschreiben:

./test.py &> mylog.log

1034650cookie-checkAusnahme mit Traceback in Python protokollieren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy