Logging HOWTO

Autor:

Vinay Sajip <vinay_sajip at red-dove dot com>

Diese Seite enthält Tutorial-Informationen. Links zu Referenzinformationen und einem Protokollierungs-Kochbuch finden Sie unter Weitere Ressourcen.

Einsteiger-Tutorial zur Protokollierung

Protokollierung ist eine Methode zur Verfolgung von Ereignissen, die während der Ausführung von Software auftreten. Der Entwickler der Software fügt Protokollierungsaufrufe in seinen Code ein, um anzuzeigen, dass bestimmte Ereignisse aufgetreten sind. Ein Ereignis wird durch eine beschreibende Meldung dargestellt, die optional variable Daten enthalten kann (d. h. Daten, die sich potenziell bei jedem Auftreten des Ereignisses unterscheiden). Ereignisse haben auch eine Wichtigkeit, die der Entwickler dem Ereignis zuordnet; die Wichtigkeit kann auch als Stufe oder Schweregrad bezeichnet werden.

Wann sollte Protokollierung verwendet werden

Sie können auf Protokollierungsfunktionalität zugreifen, indem Sie einen Logger über logger = getLogger(__name__) erstellen und dann die Methoden debug(), info(), warning(), error() und critical() des Loggers aufrufen. Um zu entscheiden, wann Protokollierung verwendet werden soll und welche Logger-Methoden Sie wann verwenden sollten, siehe die folgende Tabelle. Sie gibt für jede einer Reihe gängiger Aufgaben das beste Werkzeug für diese Aufgabe an.

Aufgabe, die Sie ausführen möchten

Das beste Werkzeug für die Aufgabe

Konsolenausgabe für normale Verwendung eines Kommandozeilenskripts oder -programms anzeigen

print()

Ereignisse melden, die während des normalen Betriebs eines Programms auftreten (z. B. zur Statusüberwachung oder Fehleruntersuchung)

Die Methode info() eines Loggers (oder die Methode debug() für sehr detaillierte Ausgaben zu Diagnosezwecken)

Eine Warnung bezüglich eines bestimmten Laufzeitereignisses ausgeben

warnings.warn() im Bibliotheks-Code, wenn das Problem vermeidbar ist und die Client-Anwendung geändert werden sollte, um die Warnung zu beseitigen

Die Methode warning() eines Loggers, wenn die Client-Anwendung nichts an der Situation ändern kann, das Ereignis aber dennoch bemerkt werden sollte

Einen Fehler bezüglich eines bestimmten Laufzeitereignisses melden

Eine Ausnahme auslösen

Unterdrückung eines Fehlers melden, ohne eine Ausnahme auszulösen (z. B. Fehlerbehandlung in einem langlebigen Serverprozess)

Die Methode error(), exception() oder critical() eines Loggers, je nach speziellem Fehler und Anwendungsdomäne

Die Logger-Methoden sind nach der Stufe oder dem Schweregrad der Ereignisse benannt, die sie zur Verfolgung verwenden. Die Standardstufen und ihre Anwendbarkeit werden nachstehend beschrieben (in aufsteigender Reihenfolge des Schweregrads)

Stufe

Wann sie verwendet wird

DEBUG

Detaillierte Informationen, die in der Regel nur bei der Diagnose von Problemen von Interesse sind.

INFO

Bestätigung, dass die Dinge wie erwartet funktionieren.

WARNING

Ein Hinweis darauf, dass etwas Unerwartetes passiert ist, oder ein Hinweis auf ein Problem in naher Zukunft (z. B. „Festplattenspeicher gering“). Die Software funktioniert weiterhin wie erwartet.

ERROR

Aufgrund eines schwerwiegenderen Problems konnte die Software eine Funktion nicht ausführen.

CRITICAL

Ein schwerwiegender Fehler, der darauf hinweist, dass das Programm selbst möglicherweise nicht weiterlaufen kann.

Die Standardstufe ist WARNING, was bedeutet, dass nur Ereignisse dieser Schwere und höher verfolgt werden, es sei denn, das Protokollierungspaket ist anders konfiguriert.

Verfolgte Ereignisse können unterschiedlich behandelt werden. Die einfachste Methode zur Behandlung von verfolgten Ereignissen ist deren Ausgabe auf der Konsole. Eine weitere übliche Methode ist das Schreiben in eine Disk-Datei.

Ein einfaches Beispiel

Ein sehr einfaches Beispiel ist

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

Wenn Sie diese Zeilen in ein Skript eingeben und es ausführen, sehen Sie

WARNING:root:Watch out!

auf der Konsole ausgegeben. Die Meldung INFO erscheint nicht, da die Standardstufe WARNING ist. Die ausgegebene Meldung enthält die Angabe der Stufe und die Beschreibung des Ereignisses, die im Protokollierungsaufruf angegeben wurde, d. h. „Watch out!“. Die tatsächliche Ausgabe kann recht flexibel formatiert werden, wenn Sie dies benötigen; Formatierungsoptionen werden ebenfalls später erläutert.

Beachten Sie, dass wir in diesem Beispiel Funktionen direkt auf dem Modul logging verwenden, z. B. logging.debug, anstatt einen Logger zu erstellen und Funktionen auf diesem aufzurufen. Diese Funktionen arbeiten mit dem Stamm-Logger, können aber nützlich sein, da sie basicConfig() für Sie aufrufen, wenn es noch nicht aufgerufen wurde, wie in diesem Beispiel. In größeren Programmen möchten Sie die Protokollierungskonfiguration jedoch normalerweise explizit steuern – daher und aus anderen Gründen ist es besser, Logger zu erstellen und deren Methoden aufzurufen.

Protokollierung in eine Datei

Eine sehr häufige Situation ist die Aufzeichnung von Protokollierungsereignissen in einer Datei, also schauen wir uns das als Nächstes an. Stellen Sie sicher, dass Sie das Folgende in einer neu gestarteten Python-Umgebung ausprobieren und nicht einfach von der oben beschriebenen Sitzung fortfahren

import logging
logger = logging.getLogger(__name__)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logger.debug('This message should go to the log file')
logger.info('So should this')
logger.warning('And this, too')
logger.error('And non-ASCII stuff, too, like Øresund and Malmö')

Geändert in Version 3.9: Das Argument encoding wurde hinzugefügt. In früheren Python-Versionen oder wenn es nicht angegeben wird, ist die verwendete Kodierung der Standardwert, der von open() verwendet wird. Obwohl im obigen Beispiel nicht gezeigt, kann jetzt auch ein errors-Argument übergeben werden, das bestimmt, wie Kodierungsfehler behandelt werden. Verfügbare Werte und den Standard finden Sie in der Dokumentation für open().

Und wenn wir nun die Datei öffnen und uns ansehen, was wir haben, sollten wir die Protokollmeldungen finden

DEBUG:__main__:This message should go to the log file
INFO:__main__:So should this
WARNING:__main__:And this, too
ERROR:__main__:And non-ASCII stuff, too, like Øresund and Malmö

Dieses Beispiel zeigt auch, wie Sie die Protokollierungsstufe festlegen können, die als Schwellenwert für die Verfolgung dient. In diesem Fall wurden alle Meldungen gedruckt, da wir den Schwellenwert auf DEBUG gesetzt haben.

Wenn Sie die Protokollierungsstufe über eine Kommandozeilenoption wie

--log=INFO

und Sie den Wert des Parameters für --log in einer Variablen loglevel haben, können Sie

getattr(logging, loglevel.upper())

verwenden, um den Wert zu erhalten, den Sie über das Argument level an basicConfig() übergeben. Möglicherweise möchten Sie Benutzereingaben auf Fehler überprüfen, vielleicht wie im folgenden Beispiel

# assuming loglevel is bound to the string value obtained from the
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)

Der Aufruf von basicConfig() sollte vor allen Aufrufen der Methoden eines Loggers wie debug(), info() usw. erfolgen. Andernfalls wird das Protokollierungsereignis möglicherweise nicht wie gewünscht behandelt.

Wenn Sie das obige Skript mehrmals ausführen, werden die Meldungen aufeinanderfolgender Ausführungen an die Datei example.log angehängt. Wenn jede Ausführung frisch beginnen soll, ohne sich an Meldungen früherer Ausführungen zu erinnern, können Sie das Argument filemode angeben, indem Sie den Aufruf im obigen Beispiel ändern zu

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

Die Ausgabe ist dieselbe wie zuvor, aber an die Protokolldatei wird nicht mehr angehängt, sodass die Meldungen früherer Ausführungen verloren gehen.

Protokollierung variabler Daten

Um variable Daten zu protokollieren, verwenden Sie eine Formatzeichenfolge für die Ereignisbeschreibungsnachricht und hängen Sie die variablen Daten als Argumente an. Zum Beispiel

import logging
logging.warning('%s before you %s', 'Look', 'leap!')

wird anzeigen

WARNING:root:Look before you leap!

Wie Sie sehen können, verwendet die Zusammenführung variabler Daten in der Ereignisbeschreibungsnachricht die alte %-Formatierung. Dies dient der Abwärtskompatibilität: Das Protokollierungspaket existiert vor neueren Formatierungsoptionen wie str.format() und string.Template. Diese neueren Formatierungsoptionen werden unterstützt, aber deren Untersuchung liegt außerhalb des Rahmens dieses Tutorials: weitere Informationen finden Sie unter Verwendung bestimmter Formatierungsstile in Ihrer Anwendung.

Ändern des Formats angezeigter Meldungen

Um das für die Anzeige von Meldungen verwendete Format zu ändern, müssen Sie das gewünschte Format angeben

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')

was drucken würde

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too

Beachten Sie, dass das „root“, das in früheren Beispielen erschien, verschwunden ist. Eine vollständige Liste der Dinge, die in Formatzeichenfolgen erscheinen können, finden Sie in der Dokumentation für LogRecord-Attribute, aber für eine einfache Verwendung benötigen Sie nur den levelname (Schweregrad), die message (Ereignisbeschreibung, einschließlich variabler Daten) und vielleicht die Anzeige, wann das Ereignis aufgetreten ist. Dies wird im nächsten Abschnitt beschrieben.

Anzeige von Datum/Zeit in Meldungen

Um das Datum und die Uhrzeit eines Ereignisses anzuzeigen, würden Sie „%(asctime)s“ in Ihre Formatzeichenfolge einfügen

import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')

was etwa so ausgeben sollte

2010-12-12 11:41:42,612 is when this event was logged.

Das Standardformat für die Datum/Zeit-Anzeige (wie oben gezeigt) ist ähnlich wie ISO8601 oder RFC 3339. Wenn Sie mehr Kontrolle über die Formatierung des Datums/der Uhrzeit benötigen, übergeben Sie ein datefmt-Argument an basicConfig, wie in diesem Beispiel

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

was ungefähr so ausgeben würde

12/12/2010 11:46:36 AM is when this event was logged.

Das Format des datefmt-Arguments ist dasselbe wie das, das von time.strftime() unterstützt wird.

Nächste Schritte

Damit ist das grundlegende Tutorial abgeschlossen. Es sollte ausreichen, um Ihnen den Einstieg in die Protokollierung zu ermöglichen. Das Protokollierungspaket bietet noch viel mehr, aber um das Beste daraus zu machen, müssen Sie etwas mehr Zeit in das Lesen der folgenden Abschnitte investieren. Wenn Sie bereit dafür sind, holen Sie sich Ihr Lieblingsgetränk und machen Sie weiter.

Wenn Ihre Protokollierungsanforderungen einfach sind, verwenden Sie die obigen Beispiele, um die Protokollierung in Ihre eigenen Skripte zu integrieren. Wenn Sie auf Probleme stoßen oder etwas nicht verstehen, stellen Sie bitte eine Frage im Hilfe-Bereich des Python-Diskussionsforums und Sie sollten bald Hilfe erhalten.

Immer noch hier? Sie können die nächsten Abschnitte weiterlesen, die ein etwas fortgeschritteneres/detaillierteres Tutorial als das obige Grundtutorial bieten. Danach können Sie sich das Logging Cookbook ansehen.

Fortgeschrittenes Tutorial zur Protokollierung

Die Protokollierungsbibliothek verfolgt einen modularen Ansatz und bietet mehrere Komponentenkategorien: Logger, Handler, Filter und Formatter.

  • Logger stellen die Schnittstelle bereit, die der Anwendungscode direkt verwendet.

  • Handler senden die Protokollsätze (die von Loggern erstellt wurden) an das entsprechende Ziel.

  • Filter bieten eine feinere Granularität zur Bestimmung, welche Protokollsätze ausgegeben werden sollen.

  • Formatter legen das Layout von Protokollsätzen in der endgültigen Ausgabe fest.

Die Protokollereignisinformationen werden zwischen Loggern, Handlern, Filtern und Formatter in einer LogRecord-Instanz übergeben.

Die Protokollierung erfolgt durch Aufrufen von Methoden auf Instanzen der Klasse Logger (im Folgenden Logger genannt). Jede Instanz hat einen Namen und sie sind konzeptionell in einer Namensraumhierarchie angeordnet, wobei Punkte als Trennzeichen verwendet werden. Beispielsweise ist ein Logger namens ‚scan‘ der Elternteil der Logger ‚scan.text‘, ‚scan.html‘ und ‚scan.pdf‘. Logger-Namen können alles sein, was Sie möchten, und geben den Bereich einer Anwendung an, in dem eine protokollierte Meldung ihren Ursprung hat.

Eine gute Konvention bei der Benennung von Loggern ist die Verwendung eines Modul-weiten Loggers in jedem Modul, das Protokollierung verwendet, wie folgt benannt

logger = logging.getLogger(__name__)

Das bedeutet, dass die Logger-Namen die Paket-/Modulhierarchie verfolgen und es intuitiv offensichtlich ist, wo Ereignisse nur anhand des Logger-Namens protokolliert werden.

Die Wurzel der Logger-Hierarchie wird als Stamm-Logger bezeichnet. Dies ist der Logger, der von den Funktionen debug(), info(), warning(), error() und critical() verwendet wird, die einfach die gleichnamige Methode des Stamm-Loggers aufrufen. Die Funktionen und Methoden haben die gleichen Signaturen. Der Name des Stamm-Loggers wird in der Protokollausgabe als ‚root‘ angezeigt.

Es ist natürlich möglich, Meldungen an verschiedene Ziele zu protokollieren. Die Unterstützung für das Schreiben von Protokollmeldungen in Dateien, HTTP-GET/POST-Orte, E-Mails über SMTP, generische Sockets, Warteschlangen oder betriebssystemspezifische Protokollierungsmechanismen wie Syslog oder das Windows NT-Ereignisprotokoll ist im Paket enthalten. Ziele werden von Handler-Klassen bedient. Sie können Ihre eigene Protokollzielklasse erstellen, wenn Sie spezielle Anforderungen haben, die von keiner der integrierten Handler-Klassen erfüllt werden.

Standardmäßig ist kein Ziel für Protokollmeldungen festgelegt. Sie können ein Ziel (wie Konsole oder Datei) festlegen, indem Sie basicConfig() wie in den Tutorial-Beispielen verwenden. Wenn Sie die Funktionen debug(), info(), warning(), error() und critical() aufrufen, prüfen diese, ob kein Ziel gesetzt ist; und wenn eines nicht gesetzt ist, setzen sie ein Ziel der Konsole (sys.stderr) und ein Standardformat für die angezeigte Meldung, bevor sie an den Stamm-Logger delegieren, um die eigentliche Meldungsausgabe durchzuführen.

Das von basicConfig() gesetzte Standardformat für Meldungen ist

severity:logger name:message

Sie können dies ändern, indem Sie eine Formatzeichenfolge mit dem Schlüsselwortargument format an basicConfig() übergeben. Alle Optionen zur Erstellung einer Formatzeichenfolge finden Sie unter Formatter-Objekte.

Protokollierungsablauf

Der Ablauf der Protokollereignisinformationen in Loggern und Handlern ist im folgenden Diagramm dargestellt.

Logger flow Create LogRecord Logging call in user code, e.g. logger.info(...) Stop Does a filter attached to logger reject the record? Pass record to handlers of current logger Is propagate true for current logger? Is there a parent logger? Set current logger to parent At least one handler in hierarchy? Use lastResort handler Handler enabled for level of record? Does a filter attached to handler reject the record? Stop Emit (includes formatting) Handler flow Logger enabled for level of call? No Yes Yes No No Yes Yes No No Yes No Yes No Yes Record passed to handler

Logger

Logger-Objekte haben eine dreifache Aufgabe. Erstens stellen sie mehrere Methoden der Anwendung bereit, damit Anwendungen Meldungen zur Laufzeit protokollieren können. Zweitens bestimmen Logger-Objekte, welche Protokollmeldungen basierend auf dem Schweregrad (die standardmäßige Filterfunktion) oder Filterobjekten bearbeitet werden sollen. Drittens leiten Logger-Objekte relevante Protokollmeldungen an alle interessierten Protokoll-Handler weiter.

Die am häufigsten verwendeten Methoden für Logger-Objekte fallen in zwei Kategorien: Konfiguration und Meldungsversand.

Dies sind die gängigsten Konfigurationsmethoden

  • Logger.setLevel() gibt die Protokollmeldung mit der niedrigsten Schwere an, die ein Logger verarbeitet. Debug ist die niedrigste integrierte Schweregradstufe und Critical die höchste integrierte Schweregradstufe. Wenn beispielsweise die Schweregradstufe INFO ist, verarbeitet der Logger nur INFO-, WARNING-, ERROR- und CRITICAL-Meldungen und ignoriert DEBUG-Meldungen.

  • Logger.addHandler() und Logger.removeHandler() fügen Handler-Objekte zum Logger-Objekt hinzu und entfernen sie. Handler werden in Handler detaillierter behandelt.

  • Logger.addFilter() und Logger.removeFilter() fügen Filter-Objekte zum Logger-Objekt hinzu und entfernen sie. Filter werden in Filter-Objekte detaillierter behandelt.

Sie müssen diese Methoden nicht immer für jeden von Ihnen erstellten Logger aufrufen. Siehe die letzten beiden Absätze dieses Abschnitts.

Nachdem das Logger-Objekt konfiguriert ist, erstellen die folgenden Methoden Protokollmeldungen

  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error() und Logger.critical() erstellen Protokollsätze mit einer Meldung und einer Stufe, die den entsprechenden Methodennamen entspricht. Die Meldung ist eigentlich eine Formatzeichenfolge, die die Standard-Zeichenfolgensubstitutionssyntax von %s, %d, %f usw. enthalten kann. Der Rest ihrer Argumente ist eine Liste von Objekten, die den Substitutionsfeldern in der Meldung entsprechen. In Bezug auf **kwargs kümmert sich das Logging-Methode nur um ein Schlüsselwort namens exc_info und verwendet es, um zu bestimmen, ob Ausnahmeinformationen protokolliert werden sollen.

  • Logger.exception() erstellt eine Protokollmeldung ähnlich wie Logger.error(). Der Unterschied besteht darin, dass Logger.exception() zusätzlich einen Stack-Trace ausgibt. Rufen Sie diese Methode nur aus einem Ausnahmebehandler auf.

  • Logger.log() nimmt eine Protokollstufe als explizites Argument. Dies ist etwas umständlicher zum Protokollieren von Meldungen als die oben genannten Komfortmethoden für Protokollstufen, aber so protokolliert man auf benutzerdefinierten Protokollstufen.

getLogger() gibt eine Referenz auf eine Logger-Instanz mit dem angegebenen Namen zurück, falls vorhanden, oder root, falls nicht. Die Namen sind durch Punkte getrennte hierarchische Strukturen. Mehrere Aufrufe von getLogger() mit demselben Namen geben eine Referenz auf dasselbe Logger-Objekt zurück. Logger, die weiter unten in der hierarchischen Liste stehen, sind Kinder von Loggern höher in der Liste. Zum Beispiel sind für einen Logger mit dem Namen foo Logger mit den Namen foo.bar, foo.bar.baz und foo.bam alle Nachkommen von foo.

Logger haben ein Konzept des effektiven Niveaus. Wenn einem Logger kein Niveau explizit zugewiesen wird, wird stattdessen das Niveau seines übergeordneten Elements als sein effektives Niveau verwendet. Wenn das übergeordnete Element keine explizite Stufe eingestellt hat, wird dessen übergeordnetes Element untersucht und so weiter – alle Vorfahren werden durchsucht, bis eine explizit eingestellte Stufe gefunden wird. Der Stamm-Logger hat immer eine explizit eingestellte Stufe (standardmäßig WARNING). Bei der Entscheidung, ob ein Ereignis verarbeitet werden soll, wird das effektive Niveau des Loggers verwendet, um zu bestimmen, ob das Ereignis an die Handler des Loggers weitergeleitet wird.

Kind-Logger leiten Meldungen an die Handler weiter, die mit ihren übergeordneten Loggern verbunden sind. Aus diesem Grund ist es nicht notwendig, Handler für alle von einer Anwendung verwendeten Logger zu definieren und zu konfigurieren. Es reicht aus, Handler für einen Top-Level-Logger zu konfigurieren und bei Bedarf Kind-Logger zu erstellen. (Sie können die Weiterleitung jedoch deaktivieren, indem Sie das Attribut propagate eines Loggers auf False setzen.)

Handler

Handler-Objekte sind dafür verantwortlich, die geeigneten Protokollmeldungen (basierend auf dem Schweregrad der Protokollmeldungen) an das vom Handler angegebene Ziel zu senden. Logger-Objekte können null oder mehr Handler-Objekte über eine addHandler()-Methode zu sich selbst hinzufügen. Als Beispielszenario möchte eine Anwendung möglicherweise alle Protokollmeldungen in eine Protokolldatei senden, alle Protokollmeldungen ab der Stufe ERROR oder höher an stdout und alle Meldungen ab der Stufe CRITICAL an eine E-Mail-Adresse. Dieses Szenario erfordert drei einzelne Handler, wobei jeder Handler für das Senden von Meldungen eines bestimmten Schweregrads an einen bestimmten Speicherort zuständig ist.

Die Standardbibliothek enthält ziemlich viele Handler-Typen (siehe Nützliche Handler); die Tutorials verwenden hauptsächlich StreamHandler und FileHandler in ihren Beispielen.

Es gibt nur sehr wenige Methoden in einem Handler, mit denen sich Anwendungsentwickler befassen müssen. Die einzigen Handler-Methoden, die für Anwendungsentwickler relevant zu sein scheinen, die die integrierten Handler-Objekte verwenden (d. h. keine benutzerdefinierten Handler erstellen), sind die folgenden Konfigurationsmethoden

  • Die Methode setLevel() gibt, genau wie bei Logger-Objekten, die niedrigste Schweregradstufe an, die an das entsprechende Ziel gesendet wird. Warum gibt es zwei setLevel()-Methoden? Die im Logger eingestellte Stufe bestimmt, welche Schweregradstufen von Meldungen an seine Handler weitergegeben werden. Die in jedem Handler eingestellte Stufe bestimmt, welche Meldungen dieser Handler weiterleitet.

  • setFormatter() wählt ein Formatter-Objekt für diesen Handler aus.

  • addFilter() und removeFilter() konfigurieren bzw. dekonfigurieren Filter-Objekte auf Handlern.

Anwendungscode sollte keine Instanzen von Handler direkt instanziieren und verwenden. Stattdessen ist die Klasse Handler eine Basisklasse, die die Schnittstelle definiert, die alle Handler haben sollten, und einige Standardverhaltensweisen festlegt, die Kindklassen verwenden (oder überschreiben) können.

Formatter

Formatter-Objekte konfigurieren die endgültige Reihenfolge, Struktur und den Inhalt der Protokollmeldung. Im Gegensatz zur Basisklasse logging.Handler kann Anwendungscode Formatierklassen instanziieren, obwohl Sie die Formatierklasse wahrscheinlich unterordnen könnten, wenn Ihre Anwendung ein spezielles Verhalten benötigt. Der Konstruktor nimmt drei optionale Argumente entgegen – eine Meldungsformatzeichenfolge, eine Datumsformatzeichenfolge und ein Stil-Indikator.

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

Wenn keine Meldungsformatzeichenfolge vorhanden ist, wird standardmäßig die rohe Meldung verwendet. Wenn keine Datumsformatzeichenfolge vorhanden ist, ist das Standard-Datumsformat

%Y-%m-%d %H:%M:%S

mit den Millisekunden am Ende angehängt. Der style ist einer von '%', '{' oder '$'. Wenn keiner davon angegeben ist, wird '%' verwendet.

Wenn der style auf '%' gesetzt ist, verwendet die Nachrichtenformatzeichenfolge die zeichenfolgenbasierte Ersetzung %(<dictionary key>)s; die möglichen Schlüssel sind in LogRecord-Attribute dokumentiert. Wenn der Stil '{' ist, wird angenommen, dass die Nachrichtenformatzeichenfolge mit str.format() (mit Schlüsselwortargumenten) kompatibel ist, während wenn der Stil '$' ist, die Nachrichtenformatzeichenfolge mit dem übereinstimmen sollte, was von string.Template.substitute() erwartet wird.

Geändert in Version 3.2: Der Parameter style wurde hinzugefügt.

Die folgende Nachrichtenformatzeichenfolge protokolliert die Zeit in einem lesbaren Format, die Schwere der Nachricht und den Inhalt der Nachricht, in dieser Reihenfolge

'%(asctime)s - %(levelname)s - %(message)s'

Formatierer verwenden eine vom Benutzer konfigurierbare Funktion, um die Erstellungszeit eines Datensatzes in ein Tupel umzuwandeln. Standardmäßig wird time.localtime() verwendet; um dies für eine bestimmte Formatiererinstanz zu ändern, setzen Sie das Attribut converter der Instanz auf eine Funktion mit derselben Signatur wie time.localtime() oder time.gmtime(). Um dies für alle Formatierer zu ändern, z.B. wenn Sie möchten, dass alle Protokollierungszeiten in GMT angezeigt werden, setzen Sie das Attribut converter in der Klasse Formatter (auf time.gmtime für GMT-Anzeige).

Protokollierung konfigurieren

Programmierer können die Protokollierung auf drei Arten konfigurieren

  1. Explizites Erstellen von Loggern, Handlern und Formatierern mit Python-Code, der die oben genannten Konfigurationsmethoden aufruft.

  2. Erstellen einer Protokollkonfigurationsdatei und Lesen dieser mit der Funktion fileConfig().

  3. Erstellen eines Wörterbuchs mit Konfigurationsinformationen und Übergeben dieses an die Funktion dictConfig().

Für die Referenzdokumentation zu den beiden letzteren Optionen siehe Konfigurationsfunktionen. Das folgende Beispiel konfiguriert einen sehr einfachen Logger, einen Konsolenhandler und einen einfachen Formatierer mithilfe von Python-Code

import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Das Ausführen dieses Moduls von der Befehlszeile aus ergibt die folgende Ausgabe

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

Das folgende Python-Modul erstellt einen Logger, einen Handler und einen Formatierer, die fast identisch mit denen im obigen Beispiel sind, wobei der einzige Unterschied die Namen der Objekte sind

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Hier ist die Datei logging.conf

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

Die Ausgabe ist fast identisch mit der des beispielbasierten Nicht-Konfigurationsdateibeispiels

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

Sie können sehen, dass der Ansatz mit Konfigurationsdateien einige Vorteile gegenüber dem Ansatz mit Python-Code hat, hauptsächlich die Trennung von Konfiguration und Code und die Möglichkeit für Nicht-Programmierer, die Protokollierungseigenschaften einfach zu ändern.

Warnung

Die Funktion fileConfig() nimmt einen Standardparameter, disable_existing_loggers, der aus Gründen der Abwärtskompatibilität standardmäßig auf True gesetzt ist. Dies ist möglicherweise nicht das, was Sie möchten, da dadurch alle nicht-root-Logger, die vor dem Aufruf von fileConfig() existieren, deaktiviert werden, es sei denn, sie (oder ein Vorfahre) sind explizit in der Konfiguration aufgeführt. Bitte beachten Sie die Referenzdokumentation für weitere Informationen und geben Sie für diesen Parameter False an, wenn Sie dies wünschen.

Das an dictConfig() übergebene Wörterbuch kann ebenfalls einen booleschen Wert mit dem Schlüssel disable_existing_loggers angeben, der, wenn er nicht explizit im Wörterbuch angegeben ist, ebenfalls standardmäßig als True interpretiert wird. Dies führt zu dem oben beschriebenen Verhalten der Logger-Deaktivierung, was möglicherweise nicht erwünscht ist. In diesem Fall geben Sie den Schlüssel explizit mit dem Wert False an.

Beachten Sie, dass die in Konfigurationsdateien referenzierten Klassennamen entweder relativ zum Protokollierungsmodul sein müssen oder absolute Werte, die über normale Importmechanismen aufgelöst werden können. Somit könnten Sie entweder WatchedFileHandler (relativ zum Protokollierungsmodul) oder mypackage.mymodule.MyHandler (für eine Klasse, die im Paket mypackage und Modul mymodule definiert ist, wobei mypackage auf dem Python-Importpfad verfügbar ist) verwenden.

In Python 3.2 wurde eine neue Methode zur Konfiguration der Protokollierung eingeführt, die Wörterbücher zur Speicherung von Konfigurationsinformationen verwendet. Diese bietet eine Obermenge der Funktionalität des oben dargestellten Konfigurationsdateibasierten Ansatzes und ist die empfohlene Konfigurationsmethode für neue Anwendungen und Bereitstellungen. Da ein Python-Wörterbuch zur Speicherung von Konfigurationsinformationen verwendet wird und Sie dieses Wörterbuch auf verschiedene Arten füllen können, haben Sie mehr Optionen für die Konfiguration. Sie können beispielsweise eine Konfigurationsdatei im JSON-Format verwenden oder, wenn Sie Zugriff auf YAML-Verarbeitungsfunktionen haben, eine Datei im YAML-Format, um das Konfigurationswörterbuch zu füllen. Oder natürlich können Sie das Wörterbuch in Python-Code erstellen, es in gepickelter Form über einen Socket empfangen oder welchen Ansatz auch immer für Ihre Anwendung sinnvoll ist.

Hier ist ein Beispiel für dieselbe Konfiguration wie oben, im YAML-Format für den neuen wörterbuchbasierten Ansatz

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]

Weitere Informationen zur Protokollierung mit einem Wörterbuch finden Sie unter Konfigurationsfunktionen.

Was passiert, wenn keine Konfiguration bereitgestellt wird

Wenn keine Protokollierungskonfiguration bereitgestellt wird, kann es zu einer Situation kommen, in der ein Protokollierungsereignis ausgegeben werden muss, aber keine Handler gefunden werden können, um das Ereignis auszugeben.

Das Ereignis wird über einen "Handler letzter Instanz" ausgegeben, der in lastResort gespeichert ist. Dieser interne Handler ist keinem Logger zugeordnet und verhält sich wie ein StreamHandler, der die Ereignisbeschreibungsnachricht an den aktuellen Wert von sys.stderr schreibt (und somit alle geltenden Umleitungen respektiert). Es erfolgt keine Formatierung der Nachricht – es wird nur die reine Ereignisbeschreibungsnachricht gedruckt. Die Stufe des Handlers ist auf WARNING eingestellt, sodass alle Ereignisse dieser und höherer Schweregrade ausgegeben werden.

Geändert in Version 3.2: Für Python-Versionen vor 3.2 gilt Folgendes:

  • Wenn raiseExceptions auf False gesetzt ist (Produktionsmodus), wird das Ereignis stillschweigend verworfen.

  • Wenn raiseExceptions auf True gesetzt ist (Entwicklungsmodus), wird einmalig eine Meldung "No handlers could be found for logger X.Y.Z" ausgegeben.

Um das Verhalten vor Version 3.2 zu erhalten, kann lastResort auf None gesetzt werden.

Protokollierung für eine Bibliothek konfigurieren

Wenn Sie eine Bibliothek entwickeln, die Protokollierung verwendet, sollten Sie sorgfältig dokumentieren, wie die Bibliothek Protokollierung verwendet – zum Beispiel die Namen der verwendeten Logger. Auch ihre Protokollierungskonfiguration muss bedacht werden. Wenn die verwendende Anwendung keine Protokollierung verwendet und der Bibliotheks-Code Protokollaufrufe tätigt, dann (wie im vorherigen Abschnitt beschrieben) werden Ereignisse der Schweregrad WARNING und höher auf sys.stderr ausgegeben. Dies gilt als bestes Standardverhalten.

Wenn aus irgendeinem Grund diese Meldungen nicht ausgegeben werden sollen, wenn keine Protokollierungskonfiguration vorhanden ist, können Sie einen "do-nothing"-Handler an den Top-Level-Logger Ihrer Bibliothek anhängen. Dies vermeidet die Ausgabe der Meldung, da für die Ereignisse der Bibliothek immer ein Handler gefunden wird: er erzeugt nur keine Ausgabe. Wenn der Bibliotheksbenutzer Protokollierung für die Anwendungskonfiguration konfiguriert, wird diese Konfiguration wahrscheinlich einige Handler hinzufügen, und wenn die Pegel entsprechend konfiguriert sind, werden die in der Bibliotheks-Code ausgeführten Protokollaufrufe normal an diese Handler gesendet.

Ein "do-nothing"-Handler ist im Protokollierungspaket enthalten: NullHandler (seit Python 3.1). Eine Instanz dieses Handlers könnte an den Top-Level-Logger des von der Bibliothek verwendeten Protokollierungs-Namensraums angehängt werden (wenn Sie verhindern möchten, dass die von Ihrer Bibliothek protokollierten Ereignisse in Abwesenheit einer Protokollierungskonfiguration an sys.stderr ausgegeben werden). Wenn die gesamte Protokollierung durch eine Bibliothek *foo* mit Loggern erfolgt, deren Namen "foo.x", "foo.x.y" usw. entsprechen, dann sollte der Code

import logging
logging.getLogger('foo').addHandler(logging.NullHandler())

den gewünschten Effekt erzielen. Wenn eine Organisation eine Reihe von Bibliotheken produziert, kann der angegebene Logger-Name "orgname.foo" anstelle von nur "foo" lauten.

Hinweis

Es wird dringend davon abgeraten, in Ihrer Bibliothek an den Stamm-Logger zu protokollieren. Verwenden Sie stattdessen einen Logger mit einem eindeutigen und leicht identifizierbaren Namen, wie z.B. __name__ für das Top-Level-Paket oder Modul Ihrer Bibliothek. Die Protokollierung an den Stamm-Logger macht es für den Anwendungsentwickler schwierig oder unmöglich, die Protokollierungsvorbereitung oder die Handler Ihrer Bibliothek nach Wunsch zu konfigurieren.

Hinweis

Es wird dringend davon abgeraten, an die Logger Ihrer Bibliothek andere Handler als NullHandler anzuhängen. Dies liegt daran, dass die Konfiguration von Handlern das Vorrecht des Anwendungsentwicklers ist, der Ihre Bibliothek verwendet. Der Anwendungsentwickler kennt seine Zielgruppe und weiß, welche Handler für seine Anwendung am besten geeignet sind: Wenn Sie "hinter den Kulissen" Handler hinzufügen, können Sie seine Fähigkeit, Unit-Tests durchzuführen und Protokolle zu liefern, die seinen Anforderungen entsprechen, beeinträchtigen.

Protokollierungsstufen

Die numerischen Werte der Protokollierungsstufen sind in der folgenden Tabelle aufgeführt. Diese sind hauptsächlich von Interesse, wenn Sie Ihre eigenen Stufen definieren möchten und ihnen bestimmte Werte im Verhältnis zu den vordefinierten Stufen zuweisen müssen. Wenn Sie eine Stufe mit demselben numerischen Wert definieren, überschreibt dies den vordefinierten Wert; der vordefinierte Name geht verloren.

Stufe

Numerischer Wert

CRITICAL

50

ERROR

40

WARNING

30

INFO

20

DEBUG

10

NICHT SET

0

Stufen können auch mit Loggern verknüpft werden, entweder vom Entwickler festgelegt oder durch Laden einer gespeicherten Protokollierungskonfiguration. Wenn eine Protokollierungsmethode für einen Logger aufgerufen wird, vergleicht der Logger seine eigene Stufe mit der Stufe, die dem Methodenaufruf zugeordnet ist. Wenn die Stufe des Loggers höher ist als die des Methodenaufrufs, wird keine Protokollnachricht generiert. Dies ist der grundlegende Mechanismus zur Steuerung der Ausführlichkeit der Protokollausgabe.

Protokollnachrichten werden als Instanzen der Klasse LogRecord kodiert. Wenn ein Logger entscheidet, ein Ereignis tatsächlich zu protokollieren, wird eine Instanz von LogRecord aus der Protokollnachricht erstellt.

Protokollnachrichten werden durch die Verwendung von *Handlern* einem Dispatch-Mechanismus unterworfen, die Instanzen von Unterklassen der Klasse Handler sind. Handler sind dafür verantwortlich, sicherzustellen, dass eine protokollierte Nachricht (in Form eines LogRecord) an einem bestimmten Ort (oder an einer Menge von Orten) landet, der für die Zielgruppe dieser Nachricht nützlich ist (wie Endbenutzer, Support-Mitarbeiter, Systemadministratoren, Entwickler). Handlern werden LogRecord-Instanzen übergeben, die für bestimmte Ziele bestimmt sind. Jeder Logger kann null, einen oder mehrere Handler haben (über die Methode addHandler() von Logger). Zusätzlich zu allen direkt einem Logger zugeordneten Handlern werden *alle Handlern, die allen Vorfahren des Loggers zugeordnet sind*, aufgerufen, um die Nachricht zu verteilen (es sei denn, das Flag *propagate* eines Loggers ist auf einen falschen Wert gesetzt, woraufhin die Weitergabe an Vorfahren-Handler stoppt).

Ähnlich wie bei Loggern können Handlern Stufen zugeordnet werden. Die Stufe eines Handlers fungiert auf die gleiche Weise als Filter wie die Stufe eines Loggers. Wenn ein Handler ein Ereignis tatsächlich weiterleitet, wird die Methode emit() verwendet, um die Nachricht an ihr Ziel zu senden. Die meisten vom Benutzer definierten Unterklassen von Handler müssen diese emit() überschreiben.

Benutzerdefinierte Stufen

Das Definieren eigener Stufen ist möglich, sollte aber nicht notwendig sein, da die vorhandenen Stufen auf Basis praktischer Erfahrung gewählt wurden. Wenn Sie jedoch davon überzeugt sind, dass Sie benutzerdefinierte Stufen benötigen, sollten Sie dabei mit großer Sorgfalt vorgehen, und es ist möglicherweise *eine sehr schlechte Idee, benutzerdefinierte Stufen zu definieren, wenn Sie eine Bibliothek entwickeln*. Denn wenn mehrere Bibliotheksautoren ihre eigenen benutzerdefinierten Stufen definieren, besteht die Möglichkeit, dass die Protokollausgabe solcher mehreren gemeinsam verwendeten Bibliotheken für den entwickelnden Benutzer schwer zu kontrollieren und/oder zu interpretieren ist, da ein bestimmter numerischer Wert für verschiedene Bibliotheken unterschiedliche Bedeutungen haben könnte.

Nützliche Handler

Zusätzlich zur Basisklasse Handler werden viele nützliche Unterklassen bereitgestellt

  1. StreamHandler Instanzen senden Nachrichten an Streams (dateiahnliche Objekte).

  2. FileHandler Instanzen senden Nachrichten an Dateidateien.

  3. BaseRotatingHandler ist die Basisklasse für Handler, die Protokolldateien an einem bestimmten Punkt rotieren. Sie ist nicht dazu bestimmt, direkt instanziiert zu werden. Verwenden Sie stattdessen RotatingFileHandler oder TimedRotatingFileHandler.

  4. RotatingFileHandler Instanzen senden Nachrichten an Dateidateien, mit Unterstützung für maximale Dateigrößen und Rotation von Protokolldateien.

  5. TimedRotatingFileHandler Instanzen senden Nachrichten an Dateidateien und rotieren die Protokolldatei in bestimmten Zeitintervallen.

  6. SocketHandler Instanzen senden Nachrichten an TCP/IP-Sockets. Seit 3.4 werden auch Unix-Domain-Sockets unterstützt.

  7. DatagramHandler Instanzen senden Nachrichten an UDP-Sockets. Seit 3.4 werden auch Unix-Domain-Sockets unterstützt.

  8. SMTPHandler Instanzen senden Nachrichten an eine bestimmte E-Mail-Adresse.

  9. SysLogHandler Instanzen senden Nachrichten an einen Unix-Syslog-Daemon, möglicherweise auf einem entfernten Computer.

  10. NTEventLogHandler Instanzen senden Nachrichten an ein Windows NT/2000/XP-Ereignisprotokoll.

  11. MemoryHandler Instanzen senden Nachrichten an einen Puffer im Speicher, der geleert wird, sobald bestimmte Kriterien erfüllt sind.

  12. HTTPHandler Instanzen senden Nachrichten an einen HTTP-Server unter Verwendung von GET oder POST Semantik.

  13. WatchedFileHandler Instanzen beobachten die Datei, in die sie protokollieren. Wenn sich die Datei ändert, wird sie geschlossen und mit dem Dateinamen neu geöffnet. Dieser Handler ist nur auf Unix-ähnlichen Systemen nützlich; Windows unterstützt den zugrunde liegenden Mechanismus nicht.

  14. QueueHandler Instanzen senden Nachrichten an eine Warteschlange, wie sie in den Modulen queue oder multiprocessing implementiert sind.

  15. NullHandler Instanzen tun nichts mit Fehlermeldungen. Sie werden von Bibliotheksentwicklern verwendet, die Protokollierung verwenden möchten, aber die Meldung "No handlers could be found for logger XXX" vermeiden möchten, die angezeigt werden kann, wenn der Benutzer der Bibliothek die Protokollierung nicht konfiguriert hat. Siehe Protokollierung für eine Bibliothek konfigurieren für weitere Informationen.

Hinzugefügt in Version 3.1: Die Klasse NullHandler.

Hinzugefügt in Version 3.2: Die Klasse QueueHandler.

Die Klassen NullHandler, StreamHandler und FileHandler sind im Kern-Protokollierungsmodul definiert. Die anderen Handler sind in einem Untermodul, logging.handlers, definiert. (Es gibt auch ein weiteres Untermodul, logging.config, für Konfigurationsfunktionalität.)

Protokollierte Nachrichten werden zur Präsentation über Instanzen der Klasse Formatter formatiert. Sie werden mit einer für die Verwendung mit dem %-Operator geeigneten Formatzeichenfolge und einem Wörterbuch initialisiert.

Für die Formatierung mehrerer Nachrichten in einem Stapel können Instanzen von BufferingFormatter verwendet werden. Zusätzlich zur Formatzeichenfolge (die auf jede Nachricht im Stapel angewendet wird) gibt es Platz für Kopf- und Fußzeilen-Formatzeichenfolgen.

Wenn die Filterung basierend auf der Logger-Stufe und/oder Handler-Stufe nicht ausreicht, können Instanzen von Filter sowohl zu Logger- als auch zu Handler-Instanzen hinzugefügt werden (über ihre Methode addFilter()). Bevor entschieden wird, eine Nachricht weiter zu verarbeiten, konsultieren sowohl Logger als auch Handler alle ihre Filter um Erlaubnis. Wenn ein Filter einen falschen Wert zurückgibt, wird die Nachricht nicht weiterverarbeitet.

Die grundlegende Filter-Funktionalität ermöglicht die Filterung nach einem bestimmten Logger-Namen. Wenn diese Funktion verwendet wird, werden Nachrichten, die an den benannten Logger und seine Kinder gesendet werden, durch den Filter gelassen und alle anderen verworfen.

Beim Protokollieren ausgelöste Ausnahmen

Das Protokollierungspaket ist so konzipiert, dass Ausnahmen, die während der Protokollierung in der Produktion auftreten, unterdrückt werden. Dies geschieht, damit Fehler, die bei der Behandlung von Protokollierungsereignissen auftreten – wie z. B. fehlerhafte Protokollkonfiguration, Netzwerk- oder ähnliche Fehler – die Anwendung, die die Protokollierung verwendet, nicht vorzeitig beenden.

SystemExit und KeyboardInterrupt Ausnahmen werden niemals unterdrückt. Andere Ausnahmen, die während der Methode emit() einer Unterklasse von Handler auftreten, werden an ihre Methode handleError() übergeben.

Die Standardimplementierung von handleError() in Handler prüft, ob eine Modulvariable, raiseExceptions, gesetzt ist. Wenn sie gesetzt ist, wird ein Traceback auf sys.stderr gedruckt. Wenn sie nicht gesetzt ist, wird die Ausnahme unterdrückt.

Hinweis

Der Standardwert von raiseExceptions ist True. Dies liegt daran, dass Sie während der Entwicklung in der Regel über alle aufgetretenen Ausnahmen benachrichtigt werden möchten. Es wird empfohlen, raiseExceptions für den Produktionseinsatz auf False zu setzen.

Verwendung beliebiger Objekte als Nachrichten

In den vorherigen Abschnitten und Beispielen wurde davon ausgegangen, dass die beim Protokollieren des Ereignisses übergebene Nachricht eine Zeichenfolge ist. Dies ist jedoch nicht die einzige Möglichkeit. Sie können ein beliebiges Objekt als Nachricht übergeben, und seine __str__()-Methode wird aufgerufen, wenn das Protokollierungssystem es in eine Zeichenfolgenrepräsentation konvertieren muss. Tatsächlich können Sie, wenn Sie möchten, die Berechnung einer Zeichenfolgenrepräsentation ganz vermeiden – zum Beispiel emittiert der SocketHandler ein Ereignis, indem er es pickelt und über das Netzwerk sendet.

Optimierung

Die Formatierung von Nachrichtenargumenten wird aufgeschoben, bis sie nicht mehr vermieden werden kann. Die Berechnung der an die Protokollierungsmethode übergebenen Argumente kann jedoch ebenfalls teuer sein, und Sie möchten dies vermeiden, wenn der Logger Ihr Ereignis einfach verwirft. Um zu entscheiden, was zu tun ist, können Sie die Methode isEnabledFor() aufrufen, die ein Stufenargument entgegennimmt und true zurückgibt, wenn das Ereignis vom Logger für diese Stufe des Aufrufs erstellt würde. Sie können Code wie diesen schreiben

if logger.isEnabledFor(logging.DEBUG):
    logger.debug('Message with %s, %s', expensive_func1(),
                                        expensive_func2())

so dass, wenn die Schwelle des Loggers über DEBUG eingestellt ist, die Aufrufe von expensive_func1 und expensive_func2 nie erfolgen.

Hinweis

In einigen Fällen kann isEnabledFor() selbst teurer sein, als Sie möchten (z. B. für tief verschachtelte Logger, bei denen eine explizite Stufe nur hoch in der Logger-Hierarchie festgelegt ist). In solchen Fällen (oder wenn Sie das Aufrufen einer Methode in engen Schleifen vermeiden möchten) können Sie das Ergebnis eines Aufrufs von isEnabledFor() in einer lokalen oder Instanzvariable speichern und diese anstelle des wiederholten Aufrufs der Methode verwenden. Ein solcher gespeicherter Wert müsste nur dann neu berechnet werden, wenn sich die Protokollierungskonfiguration während der Laufzeit der Anwendung dynamisch ändert (was nicht allzu häufig vorkommt).

Es gibt weitere Optimierungen, die für spezifische Anwendungen vorgenommen werden können, die eine genauere Kontrolle darüber benötigen, welche Protokollierungsinformationen gesammelt werden. Hier ist eine Liste von Dingen, die Sie tun können, um eine Verarbeitung zu vermeiden, die Sie nicht benötigen

Was Sie nicht sammeln möchten

Wie Sie die Sammlung vermeiden

Informationen darüber, wo Aufrufe getätigt wurden.

Set logging._srcfile auf None. Dies vermeidet den Aufruf von sys._getframe(), was dazu beitragen kann, Ihren Code in Umgebungen wie PyPy zu beschleunigen (die Code, der sys._getframe() verwendet, nicht beschleunigen kann).

Informationen zu Threads.

Setzen Sie logging.logThreads auf False.

Aktuelle Prozess-ID (os.getpid())

Setzen Sie logging.logProcesses auf False.

Aktueller Prozessname bei Verwendung von multiprocessing zur Verwaltung mehrerer Prozesse.

Setzen Sie logging.logMultiprocessing auf False.

Aktueller asyncio.Task-Name bei Verwendung von asyncio.

Setzen Sie logging.logAsyncioTasks auf False.

Beachten Sie auch, dass das Kern-Logging-Modul nur die grundlegenden Handler enthält. Wenn Sie logging.handlers und logging.config nicht importieren, verbrauchen sie keinen Speicher.

Weitere Ressourcen

Siehe auch

Modul logging

API-Referenz für das Logging-Modul.

Modul logging.config

Konfigurations-API für das Logging-Modul.

Modul logging.handlers

Nützliche Handler, die im Logging-Modul enthalten sind.

Ein Logging-Kochbuch