Python logging: So gehts richtig! (mit Codebeispielen)

    Avatarbild von Patrick
    Patrick

    veröffentlicht am: 08.06.2020
    zuletzt aktualisiert am: 02.02.2023

    Python logging: So funktioniert der logger

    Viele Programmierer benutzen zur Fehlersuche die Funktion print. Oft werden die Funktionsaufrufe danach auskommentiert oder gelöscht. Stattdessen würde ich eher empfehlen, mit dem konfigurierbaren Python logging Modul zu arbeiten. Grund dafür ist, dass man dieses auch in produktiven Anwendungen beibehalten kann. Im Folgenden möchte ich dir zeigen, wie du das Modul logging zu diesem Zweck nutzen kannst.

    Grundlegende Nutzung und Konfiguration von dem Python logging Modul

    Um logging zu nutzen, kannst du das Modul einfach importieren. Dieses stellt mehrere Funktionen bereit, die du nutzen kannst, um Meldungen auszugeben. Diese unterscheiden sich nach ihrem Level. Standardmäßig gibt es die Level debug, info, warning, error und critical.

    import logging
    
    logging.debug('debug')
    logging.info('info')
    logging.warning('warning')
    logging.error('error')
    logging.critical('critical')
    WARNING:root:warning
    ERROR:root:error
    CRITICAL:root:critical

    Anhand des vorigen Beispiels siehst du, dass nur die Nachrichten von warning, error und critical ausgegeben wurden. Das liegt daran, dass der Level in logging standardmäßig so konfiguriert ist, dass nur diese ausgegeben werden. Mit der Funktion basicConfig kannst du diesen anders konfigurieren:

    logging.basicConfig(level=logging.DEBUG)
    DEBUG:root:debug
    INFO:root:info
    WARNING:root:warning
    ERROR:root:error
    CRITICAL:root:critical

    Zusätzlich kannst du an basicConfig noch den Parameter filename übergeben, welcher der Name der Datei ist, in der das Log gespeichert werden soll. Standardmäßig werden die Nachrichten sonst nur in der Textausgabe ausgegeben.

    logging.basicConfig(filename='test.log', level=logging.DEBUG)

    Wenn du dies nicht anders festlegst, wird die Logdatei bei jeder Ausführung des Programms erweitert. Dies wird durch den Dateimodus festgelegt, mit dem die Datei geöffnet wird. Diesen kannst du mit dem Parameter filemode beeinflussen. Der Wert w beispielsweise führt dazu, dass der vorige Inhalt überschrieben wird.

    logging.basicConfig(
        filename='test.log',
        filemode='w',
        level=logging.DEBUG
    )

    Wenn du mehr über filemode wissen willst, kannst du gerne unseren Artikel zu Python open lesen. Eine Übersicht über alle für filemode möglichen Werte findest du hier (https://docs.python.org/3/library/functions.html#open).

    Weiterhin besteht die Möglichkeit, einen String anzugeben, der beeinflusst, was im Log angezeigt wird. Dafür können wir den Parameter format verwenden.

    logging.basicConfig(
        filename='test.log',
        filemode='w',
        format='%(asctime)s %(levelname)s: %(message)s',
        level=logging.DEBUG
    )
    2020-06-06 11:10:17,596 INFO: Hello world!

    Weiterhin unterstützt basicConfig noch den Parameter datefmt. Mit diesem kannst du das Format in der Uhrzeit und Datum angezeigt werden festlegen.

    logging.basicConfig(
        filename='test.log',
        filemode='w',
        format='%(asctime)s %(levelname)s: %(message)s',
        datefmt='%d.%m.%y %H:%M:%S',
        level=logging.DEBUG
    )
    06.06.20 11:11:26 INFO: Hello world!

    Für eine Übersicht über alle Optionen für datefmt empfiehlt sich dieser Abschnitt der Dokumentation (https://docs.python.org/3/library/time.html#time.strftime).

    Python logging in Klassen

    Du kannst logging in Klassen genauso verwenden wie im vorigen Abschnitt. Im Folgenden ist eine kleine Testklasse abgebildet, die eine Zahl durch eine andere teilt.

    import logging
    logging.basicConfig(level=logging.DEBUG)
    
    class Test:
        def teilen(self, dividend, divisor):
            try:
                logging.info(f'teile {dividend} durch {divisor}')
                quotient = dividend / divisor
                logging.info(f'{dividend} / {divisor} = {quotient}')
                return quotient
            except Exception as e:
                logging.error(f'Fehler beim teilen ({dividend} / {divisor}):', exc_info=True)
    
    test = Test()
    e = test.teilen(1024, 128)
    e = test.teilen(1024, 0)
    INFO:root:teile 1024 durch 128
    INFO:root:1024 / 128 = 8.0
    INFO:root:teile 1024 durch 0
    ERROR:root:Fehler beim teilen (1024 / 0):
    Traceback (most recent call last):
      File ".\logging_python.py", line 35, in teilen
        quotient = dividend / divisor
    ZeroDivisionError: division by zero

    Der Parameter exc_info von error hat im vorigen Beispiel dafür gesorgt, dass die Fehlermeldung auch im Log auftaucht.

    Logger verwenden mit getLogger

    Mit der Funktion getLogger kannst du einen Logger aufrufen oder erstellen. Meist wird für die Erstellung eines neuen Loggers die Variable __name__ verwendet. Diese enthält den Namen des Moduls, in dem sie verwendet wird.

    logger = logging.getLogger(__name__)

    Auch mit einem logger kannst du den Level bestimmen. Dafür verfügt die Klasse über die Methode setLevel.

    logger.setLevel(logging.DEBUG)

    Wir können den Logger genauso verwenden, wie logging zuvor. So würde unsere Klasse aus Abschnitt 2 mit dem Logger so aussehen:

    class Test:
        def teilen(self, dividend, divisor):
            try:
                logger.info(f'teile {dividend} durch {divisor}')
                quotient = dividend / divisor
                logger.info(f'{dividend} / {divisor} = {quotient}')
                return quotient
            except Exception as e:
                logger.error(f'Fehler beim teilen ({dividend} / {divisor}):', exc_info=True)

    Außerdem kannst du noch verschiedene Handler festlegen. Beispielsweise gibt es den sogenannten FileHandler, mit dem du festlegen kannst, in welcher Datei das Log gespeichert werden soll und mit welchem Modus es verwendet werden soll. Diesen Handlern kannst du sogenannte Formatter zuweisen, um das Verhalten von basicConfigs format- und datefmt-Parametern zu simulieren. Der FileHandler muss dann mittels der Methode addHandler zum Logger hinzugefügt werden.

    formatter = logging.Formatter(
        '%(asctime)s %(levelname)s %(lineno)s: %(message)s',
        '%d.%m.%y %H:%M:%S'
    )
    datei_handler = logging.FileHandler('file_handler.log', 'w')
    datei_handler.setFormatter(formatter)
    
    logger.addHandler(datei_handler)
    INFO: teile 1024 durch 128
    INFO: 1024 / 128 = 8.0

    Natürlich kannst du deinem Logger mehrere Handler gleichzeitig zuweisen.

    Fazit

    Pythons logging Modul stellt einige praktische Funktionen und Klassen zur Verfügung, um uns zu ermöglichen, Logs zu erstellen. Dies macht uns die Fehlersuche wesentlich einfacher. Wir können verschiedene Handler erstellen, um festzulegen wo das Log gespeichert bzw angezeigt werden soll. Jeder dieser Handler kann seine eigene Formatierung haben und ein Logger kann mehrere Handler gleichzeitig verwenden.

    Wenn du noch Fragen, Anmerkungen, Lob oder Kritik hast, lass es mich bitte mit einem Kommentar wissen.

    😩 Gelangweilt von den Udemy & YouTube-Tutorials?!

    Lerne spielerisch Python und komme deiner gutbezahlten (und an der 🌴 liegenden) Traumkarriere einen Schritt weiter.

    TP Star TP Star TP Star TP Star TP Star

    "Für Leute die gerne Python oder Java lernen wollen ist Codegree klasse. Ist nicht wie bei anderen Konkurrenten auf Videokursen aufgebaut..."

    - Lennart Sparbier

    100% kostenlos registrieren · keine Kreditkarte notwendig

    👋 Wir warten bereits auf dich!

    Lerne das, was du wirklich brauchst.

    Im Gegensatz zu der Abendschule oder der alteingesessenen Uni lernst du bei codegree die Sprachen & Pakete, die wirklich im Jobmarkt gesucht werden.

    Logo von Python
    Logo von PyTorch
    Logo von Pandas
    Logo von Matplotlib
    Logo von Java
    Logo von NumPy
    Mehr erfahren

    100% kostenlos registrieren · keine Zahlungsdaten notwendig