Python-Protokollierung: Ein ausführliches Tutorial

Blog

Python-Protokollierung: Ein ausführliches Tutorial

Python-Protokollierung: Ein ausführliches Tutorial - Das Python-Protokollierungsmodul wird mit der Standardbibliothek geliefert und bietet grundlegende Protokollierungsfunktionen. Bei korrekter Einrichtung kann eine Log-Meldung



Da Anwendungen komplexer werden, haben gut logs können sehr nützlich sein, nicht nur beim Debuggen, sondern auch, um Einblicke in Anwendungsprobleme/Leistung zu geben.






Die Python-Standardbibliothek wird mit einem Protokollierung Modul, das die meisten grundlegenden Protokollierungsfunktionen bereitstellt. Bei korrekter Einrichtung kann eine Protokollnachricht viele nützliche Informationen darüber liefern, wann und wo das Protokoll ausgelöst wird, sowie über den Protokollkontext, z. B. den laufenden Prozess/Thread.



Trotz der Vorteile wird das Logging-Modul oft übersehen, da es einige Zeit braucht, um es richtig einzurichten und obwohl es meiner Meinung nach vollständig ist, ist das offizielle Logging-Dokument unter https://docs.python.org/3/library/logging.html gibt nicht wirklich Best Practices für die Protokollierung oder hebt einige Überraschungen bei der Protokollierung hervor.



Dieses Python-Logging-Tutorial ist kein vollständiges Dokument über das Logging-Modul, sondern ein Leitfaden für die ersten Schritte, der einige Logging-Konzepte sowie einige Fallstricke vorstellt, auf die Sie achten sollten. Der Beitrag endet mit Best Practices und enthält einige Hinweise zu fortgeschritteneren Protokollierungsthemen.






So zeigen Sie private Tweets 2020 an

Bitte beachten Sie, dass alle Code-Schnipsel im Beitrag davon ausgehen, dass Sie das Logging-Modul bereits importiert haben:

import logging

Konzepte für die Python-Protokollierung

Dieser Abschnitt gibt einen Überblick über einige Konzepte, die häufig im Protokollierungsmodul anzutreffen sind.

Python-Protokollierungsebenen

Der Log-Level entspricht der Wichtigkeit, die einem Log beigemessen wird: Ein Fehler-Log sollte dann dringender sein als das Warn-Log, wohingegen ein Debug-Log nur beim Debuggen der Anwendung nützlich sein sollte.

Es gibt sechs Protokollebenen in Python; jeder Ebene ist eine ganze Zahl zugeordnet, die den Schweregrad des Protokolls angibt: NOTSET=0, DEBUG=10, INFO=20, WARN=30, ERROR=40 und CRITICAL=50.

Azure Key Vault lokale Entwicklung

Alle Level sind ziemlich einfach (DEBUG

Formatierung der Python-Protokollierung

Der Protokollformatierer reichert eine Protokollnachricht grundsätzlich an, indem er Kontextinformationen hinzufügt. Es kann nützlich sein zu wissen, wann das Protokoll gesendet wird, wohin (Python-Datei, Zeilennummer, Methode usw.) und zusätzlicher Kontext wie Thread und Prozess (kann beim Debuggen einer Multithread-Anwendung äußerst nützlich sein).

Wenn beispielsweise ein Log Hello World über einen Log-Formatierer gesendet wird:

'%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:%(lineno)d — %(message)s'

es wird werden

2018-02-07 19:47:41,864 - a.b.c - WARNING - :1 - hello world

was ist webroot passwort manager

Python-Logging-Handler

Der Log-Handler ist die Komponente, die effektiv ein Protokoll schreibt/anzeigt: Zeigen Sie es in der Konsole (über StreamHandler), in einer Datei (über FileHandler) an oder senden Sie Ihnen sogar eine E-Mail über SMTPHandler usw.

Jeder Protokollhandler hat 2 wichtige Felder:

  • Ein Formatierer, der einem Protokoll Kontextinformationen hinzufügt.
  • Eine Protokollebene, die Protokolle herausfiltert, deren Ebenen minderwertig sind. Ein Log-Handler mit dem INFO-Level wird also keine DEBUG-Logs verarbeiten.

Die Standardbibliothek bietet eine Handvoll Handler, die für allgemeine Anwendungsfälle ausreichen sollten: https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers . Die gängigsten sind StreamHandler und FileHandler:

console_handler = logging.StreamHandler() file_handler = logging.FileHandler('filename')

Python-Logger

Logger ist wahrscheinlich derjenige, der am häufigsten direkt im Code verwendet wird und auch der komplizierteste ist. Einen neuen Logger erhalten Sie wie folgt:

toto_logger = logging.getLogger('toto')

Ein Logger hat drei Hauptfelder:

  • Weitergeben: Entscheidet, ob ein Protokoll an das übergeordnete Element des Loggers weitergegeben werden soll. Standardmäßig ist sein Wert True.
  • A-Ebene: Wie die Log-Handler-Ebene wird die Logger-Ebene verwendet, um weniger wichtige Logs herauszufiltern. Im Gegensatz zum Log-Handler wird die Ebene jedoch nur beim untergeordneten Logger überprüft; Sobald das Protokoll an seine Eltern weitergegeben wurde, wird die Ebene nicht überprüft. Dies ist eher ein nicht intuitives Verhalten.
  • Handler: Die Liste der Handler, an die ein Protokoll gesendet wird, wenn es bei einem Logger ankommt. Dies ermöglicht eine flexible Protokollbehandlung – Sie können beispielsweise einen Dateiprotokoll-Handler haben, der alle DEBUG-Protokolle protokolliert, und einen E-Mail-Protokoll-Handler, der nur für CRITICAL-Protokolle verwendet wird. In dieser Hinsicht ähnelt die Logger-Handler-Beziehung einer Publisher-Consumer-Beziehung: Ein Protokoll wird an alle Handler gesendet, sobald es die Prüfung auf Logger-Ebene besteht.

Ein Logger ist einzigartig nach Namen, d.h. wenn ein Logger mit dem Namen toto erstellt wurde, werden die folgenden Aufrufe von |_+_| gibt das gleiche Objekt zurück:

Der Unreal-Engine-Entwicklerkurs - C++ lernen und Spiele erstellen
logging.getLogger('toto')

Wie Sie vielleicht schon vermutet haben, haben Logger eine Hierarchie. Ganz oben in der Hierarchie befindet sich der Root-Logger, auf den über die Datei logging.root zugegriffen werden kann. Dieser Logger wird aufgerufen, wenn Methoden wie |_+_| wird genutzt. Standardmäßig ist das Root-Log-Level WARN, daher wird jedes Log mit niedrigerem Level (zB über |_+_|) ignoriert. Eine weitere Besonderheit des Root-Loggers besteht darin, dass sein Standard-Handler erstellt wird, wenn zum ersten Mal ein Protokoll mit einem höheren Level als WARN protokolliert wird. Verwenden des Root-Loggers direkt oder indirekt über Methoden wie |_+_| wird generell nicht empfohlen.

Wenn ein neuer Logger erstellt wird, wird sein übergeordneter Logger standardmäßig auf den Root-Logger gesetzt:

wo kann man pols-münzen kaufen
assert id(logging.getLogger('toto')) == id(logging.getLogger('toto'))

Der Logger verwendet jedoch die Punktnotation, was bedeutet, dass ein Logger mit dem Namen a.b dem Logger a untergeordnet ist. Dies gilt jedoch nur, wenn der Logger a erstellt wurde, andernfalls ist ab parent immer noch die Wurzel.

logging.debug()

Wenn ein Logger entscheidet, ob ein Log gemäß der Level-Prüfung bestehen soll (z. B. wenn der Log-Level niedriger als der Logger-Level ist, wird das Log ignoriert), verwendet er seinen effektiven Level anstelle des tatsächlichen Levels. Der effektive Pegel ist der gleiche wie der Logger-Pegel, wenn der Pegel nicht NOTSET ist, d. h. alle Werte von DEBUG bis CRITICAL; Wenn der Logger-Level jedoch NOTSET ist, ist der effektive Level der erste Vorfahren-Level, der einen Nicht-NOTSET-Level hat.

Standardmäßig hat ein neuer Logger den NOTSET-Level, und da der Root-Logger einen WARN-Level hat, ist der effektive Logger-Level WARN. Selbst wenn an einen neuen Logger einige Handler angehängt sind, werden diese Handler nicht aufgerufen, es sei denn, die Protokollebene überschreitet WARN:

logging.info('info')

Standardmäßig wird der Logger-Level verwendet, um zu entscheiden, ob ein Log durchläuft: Wenn der Log-Level niedriger als der Logger-Level ist, wird das Log ignoriert.

Best Practices für die Python-Protokollierung

Das Logging-Modul ist zwar sehr praktisch, enthält aber einige Macken, die selbst den Besten stundenlange Kopfschmerzen bereiten können Python Entwickler. Hier sind meiner Meinung nach die Best Practices für die Verwendung dieses Moduls:

  • Konfigurieren Sie den Root-Logger, aber verwenden Sie ihn niemals in Ihrem Code – rufen Sie beispielsweise niemals eine Funktion wie |_+_| auf, die den Root-Logger tatsächlich im Hintergrund aufruft. Wenn Sie Fehlermeldungen von von Ihnen verwendeten Bibliotheken abfangen möchten, stellen Sie sicher, dass der Root-Logger beispielsweise so konfiguriert ist, dass er in eine Datei schreibt, um das Debuggen zu erleichtern. Standardmäßig gibt der Root-Logger nur an |_+_| aus, sodass das Protokoll leicht verloren gehen kann.
  • Um die Protokollierung zu verwenden, stellen Sie sicher, dass Sie mit |_+_| einen neuen Logger erstellen. Normalerweise verwende ich |_+_| als Loggername, aber es kann alles verwendet werden, solange es konsistent ist. Um weitere Handler hinzuzufügen, habe ich normalerweise eine Methode, die einen Logger zurückgibt (Sie finden das Wesentliche auf https://gist.github.com/nguyenkims/e92df0f8bd49973f0c94bddf36ed7fd0 ).
logging.debug()

Nachdem Sie einen neuen Logger erstellen und verwenden können:

lab = logging.getLogger('a.b') assert lab.parent == logging.root # lab's parent is indeed the root logger
  • Verwenden Sie RotatingFileHandler-Klassen wie den im Beispiel verwendeten TimedRotatingFileHandler anstelle von FileHandler, da er die Datei automatisch für Sie dreht, wenn die Datei eine Größenbeschränkung erreicht oder dies täglich tut.
  • Verwenden Sie Tools wie Sentry, Airbrake, Raygun usw., um Fehlerprotokolle automatisch für Sie abzufangen. Dies ist besonders im Kontext einer Web-App nützlich, wo das Protokoll sehr ausführlich sein kann und Fehlerprotokolle leicht verloren gehen können. Ein weiterer Vorteil der Verwendung dieser Tools besteht darin, dass Sie Details zu Variablenwerten im Fehler erhalten, damit Sie wissen, welche URL den Fehler auslöst, welcher Benutzer betroffen ist usw.

#Python