it-swarm.com.de

Wie protokolliere ich einen Python -Fehler mit Debug-Informationen?

Ich drucke Python Ausnahmemeldungen in eine Protokolldatei mit logging.error:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

Ist es möglich, detailliertere Informationen über die Ausnahme und den Code zu drucken, der sie generiert hat, als nur die Ausnahmezeichenfolge? Dinge wie Zeilennummern oder Stapelspuren wären toll.

393

logger.exception gibt neben der Fehlermeldung einen Stack-Trace aus.

Zum Beispiel:

_import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")
_

Ausgabe:

_ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
_

@ Paulo Check "Beachten Sie, dass Sie in Python 3 die _logging.exception_ -Methode direkt im except -Teil aufrufen müssen. Wenn Sie diese Methode aufrufen An einem beliebigen Ort kann es zu einer bizarren Ausnahme kommen.

622
SiggyF

Eine nette Sache an logging.exception, die SiggyFs Antwort nicht anzeigt, ist, dass Sie eine beliebige Nachricht übergeben können, und bei der Protokollierung wird immer noch der vollständige Traceback mit allen Ausnahmedetails angezeigt:

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

Mit dem Standardprotokollierungsverhalten (in neueren Versionen), bei dem nur Fehler in sys.stderr gedruckt werden, sieht das folgendermaßen aus:

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
171
ncoghlan

Die Verwendung von exc_info-Optionen ist möglicherweise besser, damit Sie die Fehlerstufe auswählen können (wenn Sie exception verwenden, wird immer error angezeigt):

try:
    # do something here
except Exception as e:
    logging.fatal(e, exc_info=True)  # log exception info at FATAL log level
109
flycee

Zitiert

Was ist, wenn Ihre Anwendung auf andere Weise protokolliert - ohne das Modul logging?

Jetzt kann traceback hier verwendet werden.

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Verwenden Sie es in Python 2 :

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
    
  • Verwenden Sie es in Python 3 :

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)
    
31
zangw

Wenn Sie einfache Protokolle verwenden, sollten alle Ihre Protokolldatensätze dieser Regel entsprechen: one record = one line. Nach dieser Regel können Sie grep und andere Tools zum Verarbeiten Ihrer Protokolldateien verwenden.

Traceback-Informationen sind jedoch mehrzeilig. Meine Antwort ist also eine erweiterte Version der von zangw oben in diesem Thread vorgeschlagenen Lösung. Das Problem ist, dass Traceback-Zeilen möglicherweise \n enthalten, sodass wir zusätzliche Arbeit leisten müssen, um diese Zeilenenden zu beseitigen:

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

Danach (wenn Sie Ihre Protokolle analysieren) können Sie die erforderlichen Traceback-Zeilen aus Ihrer Protokolldatei kopieren/einfügen und dies tun:

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

Profitieren!

12
doomatel

Diese Antwort baut auf den oben genannten hervorragenden Antworten auf.

In den meisten Anwendungen rufen Sie logging.exception (e) nicht direkt auf. Höchstwahrscheinlich haben Sie einen benutzerdefinierten Logger für Ihre Anwendung oder Ihr Modul wie folgt definiert:

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

Verwenden Sie in diesem Fall einfach den Logger, um die Ausnahme (e) wie folgt aufzurufen:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)
9
Will

Ein bisschen Dekorateur-Behandlung (sehr locker inspiriert von der Vielleicht-Monade und dem Heben). Sie können sicher Anmerkungen vom Typ Python 3.6 entfernen und einen älteren Formatierungsstil für Nachrichten verwenden.

fehlbar.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

Demo:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

Sie können diese Lösung auch so ändern, dass sie etwas aussagekräftigeres als None aus dem Teil except zurückgibt (oder die Lösung generisch machen, indem Sie diesen Rückgabewert in den Argumenten von fallible angeben).

4
Eli Korvigo