it-swarm.com.de

Ausnahme mit Traceback protokollieren

Wie kann ich meine Python-Fehler protokollieren?

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

Verwenden Sie logging.exception from mit einem except:-Handler, um die aktuelle Ausnahme mit einer Nachricht zu protokollieren.

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

Sehen Sie sich nun die Protokolldatei an, /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
152
nosklo

Verwenden Sie die exc_info-Optionen möglicherweise besser, bleibt der Warn- oder Fehlertitel: 

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

Mein Job hat mich kürzlich dazu gebracht, alle Tracebacks/Ausnahmen aus unserer Anwendung zu protokollieren. Ich habe zahlreiche Techniken ausprobiert, die andere online veröffentlicht hatten, z. B. die oben genannte, entschied mich jedoch für einen anderen Ansatz. Überschreiben von traceback.print_exception

Ich habe ein Schreiben an http://www.bbarrows.com/ Das wäre viel einfacher zu lesen, aber ich füge es hier ein.

Bei der Protokollierung aller Ausnahmen, auf die unsere Software in der Wildnis stoßen könnte, habe ich verschiedene Techniken zum Protokollieren der Python-Ausnahme-Tracebacks ausprobiert. Zuerst dachte ich, dass der Python-System-Ausnahmehaken sys.excepthook der perfekte Ort ist, um den Protokollierungscode einzufügen. Ich habe etwas Ähnliches ausprobiert:

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 Haupt-Thread, aber ich stellte bald fest, dass mein sys.excepthook nicht in allen neuen Threads existierte, die mein Prozess startete. Dies ist ein riesiges Problem, da in diesem Projekt meistens alles in Threads geschieht.

Nachdem ich gegoogelt und eine Menge Dokumentation gelesen hatte, war die hilfreichste Information, die ich gefunden hatte, der Python Issue Tracker.

Der erste Beitrag im Thread zeigt ein Arbeitsbeispiel, in dem sys.excepthook NICHT über Threads bestehen bleibt (siehe unten). Anscheinend handelt es sich hierbei um 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-Issue-Thread führen tatsächlich zu zwei vorgeschlagenen Hacks. Entweder die Unterklasse Thread und die Ausführungsmethode in unserem eigenen try außer Block einfassen, um Ausnahmen abzufangen und zu protokollieren, oder den Affen-Patch threading.Thread.run in Ihrem eigenen try außer dem Blockieren und Protokollieren der Ausnahmen ausführen.

Die erste Methode der Unterklasse Thread scheint mir in Ihrem Code weniger elegant zu sein, da Sie Ihre benutzerdefinierte Thread-Klasse ÜBERALL importieren und verwenden müssten, obwohl Sie einen Logingthread haben wollten. Dies wurde am Ende zu einem Ärger, weil ich unsere gesamte Codebasis durchsuchen und alle normalen Threads durch diese benutzerdefinierte Thread ersetzen musste. Es war jedoch klar, was diese Thread tat, und es wäre für jemanden einfacher zu diagnostizieren und zu debuggen, wenn mit dem benutzerdefinierten Protokollierungscode etwas schief ging. Ein kundenspezifischer Protokollierungs-Thread 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 Affenpatching threading.Thread.run ist Nice, weil ich es gleich nach __main__ ausführen und meinen Protokollierungscode in allen Ausnahmen instrumentieren könnte. Das Patching von Affen kann beim Debuggen ärgerlich sein, da es die erwartete Funktionalität von etwas ändert. Der empfohlene Patch aus dem 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 mit dem Testen meiner Ausnahmelogging begann, wurde mir klar, dass ich alles falsch gemacht hatte.

Zum Testen hatte ich eine

raise Exception("Test")

irgendwo in meinem Code. Das Wrapping einer Methode, die diese Methode aufrief, war jedoch ein try, mit Ausnahme des Blocks, der das Traceback ausgab und die Ausnahme schluckte. Das war sehr frustrierend, weil ich sah, dass der Traceback in STDOUT gedruckt wurde, aber nicht protokolliert wurde. Ich beschloss dann, dass eine viel einfachere Methode zum Protokollieren der Tracebacks darin besteht, die Methode zu kopieren, die der gesamte Python-Code zum Drucken der Tracebacks selbst verwendet (traceback.print_exception .). Ich hatte etwas Ähnliches wie das Folgende:

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 das Traceback in einen String-Puffer und protokolliert es mit dem Protokollierungsfehler. Ich habe einen benutzerdefinierten Protokollierungs-Handler eingerichtet, der den "customLogger" -Logger eingerichtet hat, der die ERROR-Protokolle aufnimmt und zur Analyse nach Hause schickt.

48
Brad Barrows

Sie können alle nicht erfassten Ausnahmen im Hauptthread protokollieren, indem Sie sys.excepthook einen Handler zuweisen, möglicherweise mithilfe des Parameters exc_info der Python-Protokollierungsfunktionen :

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, die mit threading.Thread erstellt wurden, notsys.excepthook auslösen, wenn eine nicht erfasste Ausnahme in ihnen auftritt, wie in Ausgabe 1230540 in Pythons Issue Tracker beschrieben. Einige Hacks wurden vorgeschlagen, um diese Einschränkung zu umgehen, beispielsweise das Affen-Patching Thread.__init__, um self.run mit einer alternativen run-Methode zu überschreiben, die das Original in einen try-Block einschließt und sys.excepthook aus dem except-Block aufruft. Alternativ können Sie den Einstiegspunkt für jeden Ihrer Threads einfach selbst in try/except umschließen.

6
Mark Amery

Nicht erfasste Ausnahmemeldungen gehen an STDERR. Anstatt Ihre Protokollierung in Python selbst zu implementieren, können Sie STDERR mit einer beliebigen Shell, die Sie zum Ausführen Ihres Python-Skripts verwenden, an eine Datei senden. In einem Bash-Skript können Sie dies mit der Umleitung von Ausgaben tun, wie im BASH-Handbuch beschrieben .

Beispiele

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

./test.py 2>> mylog.log

Datei mit verschachtelten STDOUT- und STDERR-Ausgaben überschreiben:

./test.py &> mylog.log
3
panchicore

vielleicht nicht so stylisch, aber einfacher:

#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
0
Hugo Walter

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: 

0
Martin Thoma