Python-Kurs / Tracebacks lesen und mit Debugger sowie logging Fehler finden

Lesen Sie bei einem Fehler zuerst die letzte Ausnahmezeile und die zugehörigen Stellen Ihres eigenen Codes im Traceback, danach reproduzieren Sie dieselbe Eingabe. Ein Debugger zeigt den Zustand während der Ausführung; logging hält ausgewählte Ereignisse mit festgelegter Wichtigkeit fest.

Einen Fehler eingrenzen und den Zustand davor untersuchen

Beginnen Sie unten im Traceback mit Ausnahmetyp und Meldung. Verfolgen Sie dann die Aufrufkette zu den eigenen Dateien und Zeilennummern. Eine Bibliothekszeile ist nicht automatisch die Ursache: Oft hat die aufrufende Anwendung einen unpassenden Wert oder eine falsche Konfiguration übergeben. Halten Sie Eingabe, Startbefehl und erwartetes Ergebnis fest und verkleinern Sie den Fall, ohne den Fehler zu entfernen.

Für die hier gezeigte Berechnung verwenden Sie eine Liste von Zahlen. Ein Haltepunkt vor der Negativprüfung kann die tatsächlich übergebenen Werte sichtbar machen. In VS Code öffnen Sie die Datei, wählen den Projektinterpreter und setzen am Rand neben der betreffenden Zeile einen Haltepunkt. Starten Sie die Debugansicht mit einer Python-Dateikonfiguration und prüfen Sie Variablen und Aufrufstapel. Einzelschritte zeigen, welche Anweisung als Nächstes ausgeführt wird. Die offizielle Anleitung zum Python-Debugger in VS Code beschreibt die Python-Debugger-Erweiterung und die entsprechenden Funktionen.

Ein Haltepunkt stoppt vor der Ausführung der betreffenden Zeile. Ein noch nicht berechnetes Ergebnis muss dort daher nicht bereits in den lokalen Variablen stehen. Beobachten Sie die Eingabe vor der fehlerhaften Operation und vergleichen Sie sie mit dem erwarteten Vertrag, statt lediglich nach dem Fehler zusätzliche Ausgaben anzuhängen.

Eine Berechnung mit getrennten Ausgabewegen ausführen

Speichern Sie den vollständigen Code als logging_demo.py in UTF-8 im Übungsordner. Führen Sie python logging_demo.py mit dem Projektinterpreter aus. Außerhalb einer aktivierten Umgebung kann unter macOS oder Ubuntu python3 der passende Befehlsname sein. Zusatzpakete und externe Dateien sind nicht nötig.

import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s")
logger = logging.getLogger(__name__)

def calculate(amounts):
    logger.debug("count=%s", len(amounts))
    if any(x < 0 for x in amounts):
        raise ValueError("negative amount")
    return sum(amounts)

try:
    total = calculate([3500, 8000, 2500])
except ValueError:
    logger.exception("calculation failed")
    raise SystemExit(1)
else:
    logger.info("total=%s", total)
print("finished")

Die Standardausgabe lautet:

finished

Zusätzlich wird auf dem standardmäßig für diese Konfiguration verwendeten Fehlerausgabekanal INFO:total=14000 protokolliert. Das Wort „Fehlerausgabe“ bezeichnet den Kanal, nicht die Wichtigkeit jedes dort ausgegebenen Ereignisses: Die INFO-Meldung beschreibt hier einen erfolgreichen Lauf. Ein Terminal kann beide Kanäle gemeinsam anzeigen; bei getrennter Erfassung sehen Sie die eine Zeile unter stdout und die andere unter stderr.

Die Anwendung konfiguriert das Logging vor der Berechnung. getLogger(__name__) liefert einen nach dem Modul benannten Logger. Der DEBUG-Aufruf beschreibt die Eingabeanzahl, die Negativprüfung meldet einen unzulässigen Wert, und der erfolgreiche Zweig protokolliert die Summe. Die Logging-Einführung erläutert Konfiguration und Ereignisse.

Wichtigkeit, Traceback und Prozessstatus unterscheiden

Stufe Typische Rolle in einer festgelegten Anwendungspolitik
DEBUG Detaillierte Diagnose, etwa die Eingabeanzahl
INFO Normaler Ablauf oder ein berechnetes Ergebnis
WARNING Beachtenswerter Zustand, ohne dass die gesamte Verarbeitung gescheitert sein muss
ERROR Eine Operation ist fehlgeschlagen

Bei level=logging.INFO wird die DEBUG-Meldung nicht ausgegeben. Nach einer Änderung zu logging.DEBUG erscheint zusätzlich DEBUG:count=3. Prüfen Sie dies jeweils in einem frischen Prozess: Ein bereits mit Handlern konfigurierter Prozess reagiert auf einen weiteren gewöhnlichen basicConfig-Aufruf nicht unbedingt wie ein frischer Skriptstart.

logger.exception protokolliert innerhalb des Handlers die Fehlermeldung zusammen mit der aktuell behandelten Ausnahme und ihrem Traceback. Logging allein legt keinen fehlgeschlagenen Prozessstatus fest. Deshalb folgt hier ausdrücklich raise SystemExit(1): Beim nicht behebbaren negativen Betrag endet der Lauf mit Status 1 und erreicht print("finished") nicht. Ein erfolgreicher normaler Lauf endet dagegen mit 0. Die Referenz zur Prozessbeendigung erklärt die Ausnahme SystemExit.

Prüfen Sie den Status unmittelbar nach dem Programm: in PowerShell mit $LASTEXITCODE, in einer macOS- oder Ubuntu-Shell mit echo $?. Andere danach gestartete Programme können den zuletzt beobachteten Status ersetzen. Die sichtbare Reihenfolge zusammengeführter stdout- und stderr-Ausgaben kann wegen Pufferung und Erfassungswerkzeug variieren; sie ist kein Ersatz für eine getrennte Prüfung beider Kanäle und des Status.

Gezielte Fehler auslösen und das Original wiederherstellen

  1. Starten Sie die ursprüngliche Liste. Erwarten Sie finished auf stdout, INFO:total=14000 im Log und Status 0.
  2. Ändern Sie nur 3500 in der Liste zu -1. Erwarten Sie die ERROR-Meldung calculation failed, einen Traceback mit ValueError: negative amount, keinen normalen Abschluss und Status 1.
  3. Stellen Sie die Liste wieder her und ändern Sie nur die Stufe von INFO zu DEBUG. Prüfen Sie die zusätzliche Anzahlmeldung 3 und weiterhin Summe 14000 sowie den normalen Abschluss.
  4. Stellen Sie Liste und Stufe vollständig wieder her. Wiederholen Sie den ursprünglichen Aufruf und prüfen Sie Ergebnis, beide Ausgabekanäle und Status erneut.

Die gezeigte Funktion setzt eine mehrfach nutzbare Sammlung von Zahlen voraus: Sie fragt ihre Länge ab, prüft Elemente mit any und summiert sie anschließend. Ein beliebiger einmal nutzbarer Iterator erfüllt diesen Vertrag nicht automatisch; ein Generator hat beispielsweise keine passende len-Operation. Ändern Sie bei einer solchen Erweiterung den Datenvertrag und die Umsetzung gemeinsam.

Schreiben Sie keine Passwörter, Authentifizierungsheader oder vollständigen personenbezogenen Datensätze in Diagnosemeldungen. Begrenzen Sie auch die Menge wiederholt protokollierter Daten. Wenn Logs in Dateien gespeichert werden, gehören Speicherort, Rotation, Aufbewahrung und Zugriffsrechte zur Planung. Nutzerhinweise und Entwicklerdiagnosen haben unterschiedliche Zwecke; eine nachvollziehbare Fehlermeldung und ein sichtbarer Fehlstatus bleiben wichtiger als ein möglichst umfangreiches Protokoll. Die Übung zur gezielten Ausnahmebehandlung erklärt die Abgrenzung zwischen Wiederherstellung und Weitergabe.