Logging in Python

Im letzten Artikel zum Thema Debugging hatte ich breakpoint()vorgestellt, eine Funktion, die den Python-Debugger pdb startet. Des Weiteren gibt es das Modul logging, das es ermöglicht, bestimmte Ereignisse zur Laufzeit des Programms zu verfolgen. Um dies zu demonstrieren, greife ich auf das Beispiel aus dem Blogbeitrag “Python-Debugging mit breakpoint()” zurück:

number = input("Bitte geben Sie eine Zahl ein: ")

numbers = [2, 15, 33, 6, 27, 43, -11]
result = []

for num in numbers:
    if num < 10:
        result.append(num * int(number))

print(result)

Der Benutzer kann eine Zahl eingeben, die mit jeder Zahl aus der Liste numbers, die kleiner als 10 ist, multipliziert wird. Die Ergebnisse werden zur Liste result hinzugefügt.

Um nachzuverfolgen, was im Einzelnen passiert, kommt nun logging zum Einsatz. Vor den obigen Code sind dazu folgende Zeilen voranzustellen:

import logging

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

Zunächst wird das Level festgelegt. Hier ist es DEBUG, eines von fünf zur Verfügung stehenden Standard-Leveln:

  • DEBUG
  • INFO
  • WARNING
  • ERROR
  • CRITICAL

Standardmäßig verhält es sich so, dass nur die Meldungen der Level WARNING, ERROR und CRITICAL ausgegeben werden. Aus diesem Grund wurde im Code durch die Zeile

logging.basicConfig(level=logging.DEBUG)

das Level auf DEBUG gesetzt.

Mit der Funktion logging.getLogger() wird ein Objekt der Logger-Klasse erzeugt. Dies würde es ermöglichen, einen an die eigenen Bedürfnisse angepassten Logger zu verwenden.

Zunächst sehen wir uns aber unseren DEBUG-Logger in Aktion an. Zum obigen Code wird nun die Zeile

logger.debug(result)

hinzugefügt. Dies eröffnet die Möglichkeit nachzuverfolgen, ob bzw. welche Werte zur Liste result hinzugefügt werden. Der komplette Code sieht jetzt folgendermaßen aus:

import logging

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

number = input("Bitte geben Sie eine Zahl ein: ")

numbers = [2, 15, 33, 6, 27, 43, -11]
result = []

for num in numbers:
    if num < 10:
        result.append(num * int(number))
        logger.debug(result)

print(result)

Als Ergebnis erscheint in der Konsole:

Bitte geben Sie eine Zahl ein: 2
DEBUG:root:[4]
DEBUG:root:[4, 12]
DEBUG:root:[4, 12, -22]
[4, 12, -22]

Anhand dieser Ausgabe ist zu erkennen, dass das DEBUG-Level und der Standard-Logger root verwendet werden.

Möchte man nicht den Standard-root-Logger verwenden, dann muss man an die Methode getlogger() als Argument einen Name übergeben. Durch die Zeile

logger = logging.getLogger('mein_logger')

würde die Ausgabe dann wie folgt aussehen:

Bitte geben Sie eine Zahl ein: 2
DEBUG:mein_logger:[4]
DEBUG:mein_logger:[4, 12]
DEBUG:mein_logger:[4, 12, -22]
[4, 12, -22]

So ein selbst erstellter Logger würde weitere Konfigurationsmöglichkeiten eröffnen, was aber nicht Gegenstand dieses Blogbeitrags ist.

Stattdessen werfen wir noch einen Blick auf basicConfig(), eine Methode, die es erlaubt, den root-Logger anzupassen. So ist es möglich, die Debugging-Ausgaben in eine Datei schreiben zu lassen:

logging.basicConfig(filename='logfile.txt', level=logging.DEBUG)

In der Shell könnte man nun mit

tail -f logfile.txt

die Debugging-Meldungen verfolgen. Auf diese Weise sind diese Meldungen von anderen Meldungen getrennt, was zu einer besseren Übersicht führt.

Das soll es an dieser Stelle zum Thema Logging gewesen sein. Weitere Informationen und Beispiele findet Ihr im Logging-HOWTO.