logging.config — Logging-Konfiguration¶
Quellcode: Lib/logging/config.py
Dieser Abschnitt beschreibt die API zur Konfiguration des Logging-Moduls.
Konfigurationsfunktionen¶
Die folgenden Funktionen konfigurieren das Logging-Modul. Sie befinden sich im Modul logging.config. Ihre Verwendung ist optional – Sie können das Logging-Modul entweder mit diesen Funktionen konfigurieren oder durch Aufrufe der Haupt-API (definiert in logging selbst) und durch Definition von Handlern, die entweder in logging oder logging.handlers deklariert sind.
- logging.config.dictConfig(config)¶
Übernimmt die Logging-Konfiguration aus einem Wörterbuch. Der Inhalt dieses Wörterbuchs ist im Abschnitt Konfigurations-Wörterbuch-Schema unten beschrieben.
Wenn während der Konfiguration ein Fehler auftritt, löst diese Funktion einen
ValueError,TypeError,AttributeErroroderImportErrormit einer entsprechend beschreibenden Meldung aus. Die folgende Liste (möglicherweise unvollständig) enthält Bedingungen, die einen Fehler auslösen werden:Ein
level, das keine Zeichenkette ist oder eine Zeichenkette ist, die keinem tatsächlichen Logging-Level entspricht.Ein
propagate-Wert, der keine boolesche Variable ist.Eine ID, die kein entsprechendes Ziel hat.
Eine nicht existierende Handler-ID, die während eines inkrementellen Aufrufs gefunden wurde.
Ein ungültiger Logger-Name.
Unfähigkeit, auf ein internes oder externes Objekt aufzulösen.
Die Analyse wird von der Klasse
DictConfiguratordurchgeführt, deren Konstruktor das für die Konfiguration verwendete Wörterbuch übergeben bekommt und die eine Methodeconfigure()besitzt. Das Modullogging.confighat ein aufrufbaren AttributdictConfigClass, das anfänglich aufDictConfiguratorgesetzt ist. Sie können den Wert vondictConfigClassdurch eine geeignete eigene Implementierung ersetzen.dictConfig()ruftdictConfigClassauf und übergibt das angegebene Wörterbuch. Anschließend ruft es die Methodeconfigure()auf dem zurückgegebenen Objekt auf, um die Konfiguration zu aktivieren.def dictConfig(config): dictConfigClass(config).configure()
Zum Beispiel könnte eine Unterklasse von
DictConfiguratorin ihrer eigenen__init__()-MethodeDictConfigurator.__init__()aufrufen und dann benutzerdefinierte Präfixe einrichten, die im nachfolgendenconfigure()-Aufruf verwendet werden können.dictConfigClasswird an diese neue Unterklasse gebunden, und dann kanndictConfig()genauso aufgerufen werden wie im Standardzustand ohne Anpassungen.Hinzugefügt in Version 3.2.
- logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)¶
Liest die Logging-Konfiguration aus einer Datei im
configparser-Format. Das Format der Datei sollte wie in Format der Konfigurationsdatei beschrieben sein. Diese Funktion kann mehrmals aus einer Anwendung aufgerufen werden, was es einem Endbenutzer ermöglicht, aus verschiedenen vorgefertigten Konfigurationen auszuwählen (wenn der Entwickler einen Mechanismus zur Präsentation der Auswahlmöglichkeiten und zum Laden der ausgewählten Konfiguration bereitstellt).Sie löst
FileNotFoundErroraus, wenn die Datei nicht existiert, undRuntimeError, wenn die Datei ungültig oder leer ist.- Parameter:
fname – Ein Dateiname, ein dateiähnliches Objekt oder eine Instanz, die von
RawConfigParserabgeleitet ist. Wenn eine vonRawConfigParserabgeleitete Instanz übergeben wird, wird diese unverändert verwendet. Andernfalls wird einConfigParserinstanziiert und die Konfiguration von diesem aus dem infnameübergebenen Objekt gelesen. Wenn dieses eine Methodereadline()hat, wird angenommen, dass es sich um ein dateiähnliches Objekt handelt und es wird überread_file()gelesen; andernfalls wird angenommen, dass es sich um einen Dateinamen handelt und es wird anread()übergeben.defaults – Standardwerte, die an den
ConfigParserübergeben werden, können in diesem Argument angegeben werden.disable_existing_loggers – Wenn auf
Falsegesetzt, bleiben Logger, die beim Aufruf dieser Funktion vorhanden sind, aktiviert. Der Standardwert istTrue, da dies das alte Verhalten auf abwärtskompatible Weise ermöglicht. Dieses Verhalten besteht darin, alle vorhandenen Nicht-Root-Logger zu deaktivieren, es sei denn, sie oder ihre Vorläufer werden explizit in der Logging-Konfiguration genannt.encoding – Die beim Öffnen der Datei verwendete Kodierung, wenn fname ein Dateiname ist.
Geändert in Version 3.4: Eine Instanz einer Unterklasse von
RawConfigParserwird jetzt als Wert fürfnameakzeptiert. Dies erleichtertdie Verwendung einer Konfigurationsdatei, in der die Logging-Konfiguration nur ein Teil der Gesamtkonfiguration der Anwendung ist.
die Verwendung einer Konfiguration, die aus einer Datei gelesen und dann von der verwendenden Anwendung modifiziert wurde (z. B. basierend auf Kommandozeilenparametern oder anderen Aspekten der Laufzeitumgebung), bevor sie an
fileConfigübergeben wird.
Geändert in Version 3.10: Der Parameter encoding wurde hinzugefügt.
Geändert in Version 3.12: Es wird eine Ausnahme ausgelöst, wenn die angegebene Datei nicht existiert oder ungültig oder leer ist.
- logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)¶
Startet einen Socket-Server am angegebenen Port und lauscht auf neue Konfigurationen. Wenn kein Port angegeben ist, wird der Modulstandardwert
DEFAULT_LOGGING_CONFIG_PORTverwendet. Logging-Konfigurationen werden als Datei gesendet, die für die Verarbeitung durchdictConfig()oderfileConfig()geeignet ist. Gibt eineThread-Instanz zurück, auf der Siestart()aufrufen können, um den Server zu starten, und die Siejoin()können, wenn es angebracht ist. Um den Server zu stoppen, rufen SiestopListening()auf.Das Argument
verify, falls angegeben, sollte ein aufrufbares Objekt sein, das prüft, ob empfangene Bytes über den Socket gültig sind und verarbeitet werden sollten. Dies könnte durch Verschlüsseln und/oder Signieren dessen, was über den Socket gesendet wird, geschehen, so dass dasverify-aufrufbare Objekt eine Signaturprüfung und/oder Entschlüsselung durchführen kann. Dasverify-aufrufbare Objekt wird mit einem einzigen Argument aufgerufen – den über den Socket empfangenen Bytes – und sollte die zu verarbeitenden Bytes zurückgeben oderNone, um anzuzeigen, dass die Bytes verworfen werden sollten. Die zurückgegebenen Bytes könnten dieselben sein wie die übergebenen Bytes (z. B. wenn nur eine Verifizierung durchgeführt wird) oder sie könnten völlig unterschiedlich sein (vielleicht wenn eine Entschlüsselung durchgeführt wurde).Um eine Konfiguration an den Socket zu senden, lesen Sie die Konfigurationsdatei und senden Sie sie als Byte-Sequenz an den Socket, der eine vier Byte lange Längenzeichenkette vorangestellt ist, die binär mit
struct.pack('>L', n)gepackt ist.Hinweis
Da Teile der Konfiguration über
eval()ausgewertet werden, kann die Verwendung dieser Funktion zu Sicherheitsrisiken für ihre Benutzer führen. Während die Funktion nur an einen Socket auflocalhostbindet und somit keine Verbindungen von entfernten Rechnern akzeptiert, gibt es Szenarien, in denen unvertrauenswürdiger Code unter dem Konto des Prozesses ausgeführt werden könnte, derlisten()aufruft. Insbesondere wenn der Prozess, derlisten()aufruft, auf einem Mehrbenutzersystem läuft, auf dem sich Benutzer nicht vertrauen können, dann kann ein bösartiger Benutzer veranlassen, im Wesentlichen beliebigen Code im Prozess eines Opfers auszuführen, indem er sich einfach mit demlisten()-Socket des Opfers verbindet und eine Konfiguration sendet, die beliebigen Code ausführt, den der Angreifer in dem Prozess des Opfers ausgeführt haben möchte. Dies ist besonders einfach zu tun, wenn der Standardport verwendet wird, aber auch bei Verwendung eines anderen Ports nicht schwierig. Um das Risiko zu vermeiden, dass dies geschieht, verwenden Sie das Argumentverifyfürlisten(), um zu verhindern, dass nicht erkannte Konfigurationen angewendet werden.Geändert in Version 3.4: Das Argument
verifywurde hinzugefügt.Hinweis
Wenn Sie Konfigurationen an den Listener senden möchten, die keine vorhandenen Logger deaktivieren, müssen Sie ein JSON-Format für die Konfiguration verwenden, das
dictConfig()für die Konfiguration verwendet. Diese Methode ermöglicht es Ihnen,disable_existing_loggersalsFalsein der von Ihnen gesendeten Konfiguration anzugeben.
Sicherheitsüberlegungen¶
Die Funktionalität zur Logging-Konfiguration versucht, Komfort zu bieten, und ein Teil davon geschieht durch die Möglichkeit, Text aus Konfigurationsdateien in Python-Objekte zu konvertieren, die in der Logging-Konfiguration verwendet werden – beispielsweise, wie in Benutzerdefinierte Objekte beschrieben. Diese gleichen Mechanismen (Importieren von aufrufbaren Objekten aus benutzerdefinierten Modulen und deren Aufruf mit Parametern aus der Konfiguration) könnten jedoch verwendet werden, um beliebigen Code aufzurufen, und aus diesem Grund sollten Sie Konfigurationsdateien aus nicht vertrauenswürdigen Quellen mit *äußerster Vorsicht* behandeln und sich davon überzeugen, dass nichts Schlimmes passieren kann, wenn Sie sie laden, bevor Sie sie tatsächlich laden.
Konfigurations-Wörterbuch-Schema¶
Die Beschreibung einer Logging-Konfiguration erfordert die Auflistung der verschiedenen zu erstellenden Objekte und der Verbindungen zwischen ihnen; Sie können beispielsweise einen Handler namens 'console' erstellen und dann sagen, dass der Logger namens 'startup' seine Nachrichten an den 'console'-Handler senden soll. Diese Objekte sind nicht auf die vom Modul logging bereitgestellten beschränkt, da Sie möglicherweise Ihre eigene Formatter- oder Handlerklasse schreiben. Die Parameter für diese Klassen müssen möglicherweise auch externe Objekte wie sys.stderr enthalten. Die Syntax zur Beschreibung dieser Objekte und Verbindungen ist in Objektverbindungen unten definiert.
Details zum Wörterbuch-Schema¶
Das an dictConfig() übergebene Wörterbuch muss die folgenden Schlüssel enthalten
version – auf einen ganzzahligen Wert zu setzen, der die Schemaversion darstellt. Der einzige derzeit gültige Wert ist 1, aber die Existenz dieses Schlüssels ermöglicht die Weiterentwicklung des Schemas unter Beibehaltung der Abwärtskompatibilität.
Alle anderen Schlüssel sind optional, werden aber, wenn vorhanden, wie unten beschrieben interpretiert. In allen Fällen, in denen unten von einem 'konfigurierenden Wörterbuch' die Rede ist, wird dieses auf den speziellen Schlüssel '()' geprüft, um festzustellen, ob eine benutzerdefinierte Instanziierung erforderlich ist. Wenn ja, wird der in Benutzerdefinierte Objekte unten beschriebene Mechanismus verwendet, um eine Instanz zu erstellen; andernfalls wird der Kontext verwendet, um zu bestimmen, was instanziiert werden soll.
formatters – Der entsprechende Wert ist ein Wörterbuch, in dem jeder Schlüssel eine Formatter-ID und jeder Wert ein Wörterbuch ist, das beschreibt, wie die entsprechende
Formatter-Instanz konfiguriert werden soll.Das konfigurierende Wörterbuch wird auf die folgenden optionalen Schlüssel durchsucht, die den Argumenten entsprechen, die zum Erstellen eines
Formatter-Objekts übergeben werdenformatdatefmtstylevalidate(seit Version >=3.8)defaults(seit Version >=3.12)
Ein optionaler Schlüssel
classgibt den Namen der Formatter-Klasse an (als dot-separierte Modul- und Klassenname). Die Instanziierungsargumente sind wie fürFormatter, daher ist dieser Schlüssel am nützlichsten für die Instanziierung einer angepassten Unterklasse vonFormatter. Zum Beispiel könnte die alternative Klasse Ausnahmetracebacks in einem erweiterten oder komprimierten Format darstellen. Wenn Ihr Formatter unterschiedliche oder zusätzliche Konfigurationsschlüssel benötigt, sollten Sie Benutzerdefinierte Objekte verwenden.filters – Der entsprechende Wert ist ein Wörterbuch, in dem jeder Schlüssel eine Filter-ID und jeder Wert ein Wörterbuch ist, das beschreibt, wie die entsprechende Filter-Instanz konfiguriert werden soll.
Das konfigurierende Wörterbuch wird auf den Schlüssel
name(Standard ist die leere Zeichenkette) durchsucht und dieser wird zur Erstellung einerlogging.Filter-Instanz verwendet.handlers – Der entsprechende Wert ist ein Wörterbuch, in dem jeder Schlüssel eine Handler-ID und jeder Wert ein Wörterbuch ist, das beschreibt, wie die entsprechende Handler-Instanz konfiguriert werden soll.
Das konfigurierende Wörterbuch wird auf die folgenden Schlüssel durchsucht
class(obligatorisch). Dies ist der vollqualifizierte Name der Handler-Klasse.level(optional). Das Level des Handlers.formatter(optional). Die ID des Formatters für diesen Handler.filters(optional). Eine Liste von IDs der Filter für diesen Handler.Geändert in Version 3.11:
filterskann zusätzlich zu IDs auch Filterinstanzen aufnehmen.
Alle *anderen* Schlüssel werden als Schlüsselwortargumente an den Konstruktor des Handlers übergeben. Zum Beispiel, gegeben den Ausschnitt
handlers: console: class : logging.StreamHandler formatter: brief level : INFO filters: [allow_foo] stream : ext://sys.stdout file: class : logging.handlers.RotatingFileHandler formatter: precise filename: logconfig.log maxBytes: 1024 backupCount: 3
wird der Handler mit der ID
consolealslogging.StreamHandlerinstanziiert, wobeisys.stdoutals zugrundeliegender Stream verwendet wird. Der Handler mit der IDfilewird alslogging.handlers.RotatingFileHandlermit den Schlüsselwortargumentenfilename='logconfig.log', maxBytes=1024, backupCount=3instanziiert.loggers – Der entsprechende Wert ist ein Wörterbuch, in dem jeder Schlüssel ein Logger-Name und jeder Wert ein Wörterbuch ist, das beschreibt, wie die entsprechende Logger-Instanz konfiguriert werden soll.
Das konfigurierende Wörterbuch wird auf die folgenden Schlüssel durchsucht
level(optional). Das Level des Loggers.propagate(optional). Die Propagationseinstellung des Loggers.filters(optional). Eine Liste von IDs der Filter für diesen Logger.Geändert in Version 3.11:
filterskann zusätzlich zu IDs auch Filterinstanzen aufnehmen.handlers(optional). Eine Liste von IDs der Handler für diesen Logger.
Die angegebenen Logger werden entsprechend dem Level, der Propagation, den Filtern und den Handlern konfiguriert.
root – Dies ist die Konfiguration für den Root-Logger. Die Verarbeitung der Konfiguration erfolgt wie bei jedem Logger, außer dass die Einstellung
propagatenicht anwendbar ist.incremental – ob die Konfiguration inkrementell zur bestehenden Konfiguration interpretiert werden soll. Dieser Wert hat standardmäßig
False, was bedeutet, dass die angegebene Konfiguration die bestehende Konfiguration mit denselben Semantik ersetzt, die von der bestehenden APIfileConfig()verwendet wird.Wenn der angegebene Wert
Trueist, wird die Konfiguration wie im Abschnitt Inkrementelle Konfiguration beschrieben verarbeitet.disable_existing_loggers – ob vorhandene Nicht-Root-Logger deaktiviert werden sollen. Diese Einstellung spiegelt den gleichnamigen Parameter in
fileConfig()wider. Wenn abwesend, hat dieser Parameter standardmäßigTrue. Dieser Wert wird ignoriert, wenn incrementalTrueist.
Inkrementelle Konfiguration¶
Es ist schwierig, vollständige Flexibilität für inkrementelle Konfigurationen zu bieten. Da Objekte wie Filter und Formatter beispielsweise anonym sind, ist es nach der Einrichtung einer Konfiguration nicht möglich, auf solche anonymen Objekte zu verweisen, wenn eine Konfiguration erweitert wird.
Darüber hinaus gibt es keinen zwingenden Grund, den Objektgraphen von Loggern, Handlern, Filtern und Formatter zur Laufzeit willkürlich zu ändern, sobald eine Konfiguration eingerichtet ist; die Ausführlichkeit von Loggern und Handlern kann einfach durch Setzen von Levels (und im Falle von Loggern, Propagation-Flags) gesteuert werden. Die willkürliche Änderung des Objektgraphen auf sichere Weise ist in einer Multithreading-Umgebung problematisch; zwar nicht unmöglich, aber die Vorteile rechtfertigen nicht die Komplexität, die dies der Implementierung hinzufügt.
Daher ignoriert das System beim Vorhandensein des Schlüssels incremental in einem Konfigurationswörterbuch und wenn dieser True ist, alle Einträge für formatters und filters und verarbeitet nur die level-Einstellungen in den handlers-Einträgen sowie die level- und propagate-Einstellungen in den loggers- und root-Einträgen.
Die Verwendung eines Wertes im Konfigurationswörterbuch ermöglicht das Senden von Konfigurationen über das Netzwerk als gepickelte Wörterbücher an einen Socket-Listener. Daher kann die Logging-Ausführlichkeit einer langlaufenden Anwendung im Laufe der Zeit geändert werden, ohne dass die Anwendung gestoppt und neu gestartet werden muss.
Objektverbindungen¶
Das Schema beschreibt eine Menge von Logging-Objekten – Logger, Handler, Formatter, Filter –, die in einem Objektgraphen miteinander verbunden sind. Daher muss das Schema Verbindungen zwischen den Objekten darstellen. Sagen wir zum Beispiel, dass ein bestimmter Logger nach der Konfiguration einen bestimmten Handler angehängt hat. Für die Zwecke dieser Diskussion können wir sagen, dass der Logger die Quelle und der Handler das Ziel einer Verbindung zwischen den beiden darstellt. In den konfigurierten Objekten wird dies durch den Logger repräsentiert, der eine Referenz auf den Handler hält. Im Konfigurationswörterbuch geschieht dies, indem jedem Zielobjekt eine ID gegeben wird, die es eindeutig identifiziert, und dann die ID in der Konfiguration des Quellobjekts verwendet wird, um anzuzeigen, dass eine Verbindung zwischen dem Quell- und dem Zielobjekt mit dieser ID besteht.
Betrachten Sie also beispielsweise den folgenden YAML-Ausschnitt
formatters:
brief:
# configuration for formatter with id 'brief' goes here
precise:
# configuration for formatter with id 'precise' goes here
handlers:
h1: #This is an id
# configuration of handler with id 'h1' goes here
formatter: brief
h2: #This is another id
# configuration of handler with id 'h2' goes here
formatter: precise
loggers:
foo.bar.baz:
# other configuration for logger 'foo.bar.baz'
handlers: [h1, h2]
(Hinweis: YAML wird hier verwendet, da es etwas lesbarer ist als die äquivalente Python-Quellform für das Wörterbuch.)
Die IDs für Logger sind die Logger-Namen, die programmatisch verwendet würden, um eine Referenz auf diese Logger zu erhalten, z. B. foo.bar.baz. Die IDs für Formatter und Filter können beliebige Zeichenkettenwerte sein (wie brief, precise oben) und sind transient, d. h. sie sind nur für die Verarbeitung des Konfigurationswörterbuchs aussagekräftig und werden verwendet, um Verbindungen zwischen Objekten zu bestimmen, und werden nirgends gespeichert, wenn der Konfigurationsaufruf abgeschlossen ist.
Der obige Ausschnitt gibt an, dass der Logger mit dem Namen foo.bar.baz zwei Handler angehängt haben soll, die durch die Handler-IDs h1 und h2 beschrieben werden. Der Formatter für h1 ist der, der durch die ID brief beschrieben wird, und der Formatter für h2 ist der, der durch die ID precise beschrieben wird.
Benutzerdefinierte Objekte¶
Das Schema unterstützt benutzerdefinierte Objekte für Handler, Filter und Formatter. (Logger müssen für verschiedene Instanzen keine unterschiedlichen Typen haben, daher gibt es in diesem Konfigurationsschema keine Unterstützung für benutzerdefinierte Logger-Klassen.)
Zu konfigurierende Objekte werden durch Dictionaries beschrieben, die ihre Konfiguration detailliert darstellen. An einigen Stellen kann das Logging-System aus dem Kontext ableiten, wie ein Objekt instanziiert werden soll, aber wenn ein benutzerdefiniertes Objekt instanziiert werden soll, weiß das System nicht, wie dies geschehen soll. Um eine vollständige Flexibilität bei der Instanziierung benutzerdefinierter Objekte zu gewährleisten, muss der Benutzer eine "Factory" bereitstellen – ein aufrufbare Objekt, das mit einem Konfigurationsdictionary aufgerufen wird und das instanziierte Objekt zurückgibt. Dies wird signalisiert, indem ein absoluter Importpfad zur Factory unter dem Sonder-Schlüssel '()' zur Verfügung gestellt wird. Hier ist ein konkretes Beispiel.
formatters:
brief:
format: '%(message)s'
default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
custom:
(): my.package.customFormatterFactory
bar: baz
spam: 99.9
answer: 42
Der obige YAML-Schnipsel definiert drei Formatierer. Der erste mit der ID brief ist eine Standardinstanz von logging.Formatter mit der angegebenen Formatzeichenkette. Der zweite, mit der ID default, hat ein längeres Format und definiert auch das Zeitformat explizit, und ergibt einen logging.Formatter, der mit diesen beiden Formatzeichenketten initialisiert wird. In Python-Quellform dargestellt, haben die Formatierer brief und default Konfigurations-Unterdictionaries.
{
'format' : '%(message)s'
}
und
{
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
'datefmt' : '%Y-%m-%d %H:%M:%S'
}
respektive, und da diese Dictionaries nicht den Sonder-Schlüssel '()' enthalten, wird die Instanziierung aus dem Kontext abgeleitet: Als Ergebnis werden Standardinstanzen von logging.Formatter erstellt. Das Konfigurations-Unterdictionary für den dritten Formatierer mit der ID custom ist.
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42
}
und dies enthält den Sonder-Schlüssel '()', was bedeutet, dass eine benutzerdefinierte Instanziierung gewünscht ist. In diesem Fall wird die angegebene Factory-Callable verwendet. Wenn es sich um ein tatsächliches aufrufbares Objekt handelt, wird es direkt verwendet – andernfalls, wenn Sie eine Zeichenkette (wie im Beispiel) angeben, wird das tatsächliche aufrufbare Objekt anhand normaler Importmechanismen gefunden. Das aufrufbare Objekt wird mit den **verbleibenden** Elementen im Konfigurations-Unterdictionary als Schlüsselwortargumente aufgerufen. Im obigen Beispiel wird angenommen, dass der Formatierer mit der ID custom durch den Aufruf zurückgegeben wird.
my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)
Warnung
Die Werte für Schlüssel wie bar, spam und answer im obigen Beispiel sollten keine Konfigurationsdictionaries oder Referenzen wie cfg://foo oder ext://bar sein, da sie nicht von der Konfigurationsmaschine verarbeitet, sondern unverändert an das aufrufbare Objekt übergeben werden.
Der Schlüssel '()' wurde als Sonder-Schlüssel verwendet, da er kein gültiger Schlüsselwortparametername ist und somit nicht mit den Namen der im Aufruf verwendeten Schlüsselwortargumente kollidiert. Die '()' dient auch als Eselsbrücke dafür, dass der entsprechende Wert ein aufrufbares Objekt ist.
Geändert in Version 3.11: Das Mitglied filters von handlers und loggers kann neben IDs auch Filterinstanzen akzeptieren.
Sie können auch einen Sonder-Schlüssel '.' angeben, dessen Wert eine Zuordnung von Attributnamen zu Werten ist. Wenn gefunden, werden die angegebenen Attribute auf dem benutzerdefinierten Objekt gesetzt, bevor es zurückgegeben wird. Somit wird mit der folgenden Konfiguration
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42,
'.' {
'foo': 'bar',
'baz': 'bozz'
}
}
das zurückgegebene Formatierungsobjekt das Attribut foo auf 'bar' und das Attribut baz auf 'bozz' gesetzt haben.
Warnung
Die Werte für Attribute wie foo und baz im obigen Beispiel sollten keine Konfigurationsdictionaries oder Referenzen wie cfg://foo oder ext://bar sein, da sie nicht von der Konfigurationsmaschine verarbeitet, sondern unverändert als Attributwerte gesetzt werden.
Reihenfolge der Handler-Konfiguration¶
Handler werden in alphabetischer Reihenfolge ihrer Schlüssel konfiguriert, und ein konfigurierter Handler ersetzt das Konfigurationsdictionary in (einer Arbeitskopie des) handlers-Dictionary im Schema. Wenn Sie ein Konstrukt wie cfg://handlers.foo verwenden, verweist handlers['foo'] zunächst auf das Konfigurationsdictionary für den Handler namens foo und später (sobald dieser Handler konfiguriert wurde) auf die instanziierte Handler-Instanz. Somit kann cfg://handlers.foo entweder auf ein Dictionary oder eine Handler-Instanz aufgelöst werden. Im Allgemeinen ist es ratsam, Handler so zu benennen, dass abhängige Handler **nach** den Handlern konfiguriert werden, von denen sie abhängen; das erlaubt, dass etwas wie cfg://handlers.foo bei der Konfiguration eines Handlers verwendet wird, der vom Handler foo abhängt. Wenn der abhängige Handler bar genannt worden wäre, hätte dies zu Problemen geführt, da die Konfiguration von bar vor der von foo versucht worden wäre und foo noch nicht konfiguriert worden wäre. Wenn der abhängige Handler jedoch foobar genannt worden wäre, wäre er nach foo konfiguriert worden, mit dem Ergebnis, dass cfg://handlers.foo auf den konfigurierten Handler foo und nicht auf sein Konfigurationsdictionary aufgelöst würde.
Zugriff auf externe Objekte¶
Es gibt Zeiten, in denen eine Konfiguration auf Objekte außerhalb der Konfiguration verweisen muss, zum Beispiel sys.stderr. Wenn das Konfigurationsdictionary mit Python-Code erstellt wird, ist dies unkompliziert, aber ein Problem entsteht, wenn die Konfiguration über eine Textdatei (z. B. JSON, YAML) bereitgestellt wird. In einer Textdatei gibt es keine Standardmethode, um sys.stderr von der wörtlichen Zeichenkette 'sys.stderr' zu unterscheiden. Um diese Unterscheidung zu erleichtern, sucht das Konfigurationssystem nach bestimmten Sonderpräfixen in Zeichenkettenwerten und behandelt sie speziell. Wenn beispielsweise die wörtliche Zeichenkette 'ext://sys.stderr' als Wert in der Konfiguration bereitgestellt wird, wird ext:// entfernt und der Rest des Werts anhand normaler Importmechanismen verarbeitet.
Die Behandlung solcher Präfixe erfolgt analog zur Protokollbehandlung: Es gibt einen generischen Mechanismus, der nach Präfixen sucht, die dem regulären Ausdruck ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ entsprechen, wobei, wenn der prefix erkannt wird, der suffix präfixabhängig verarbeitet wird und das Ergebnis der Verarbeitung den Zeichenkettenwert ersetzt. Wenn der Präfix nicht erkannt wird, bleibt der Zeichenkettenwert unverändert.
Zugriff auf interne Objekte¶
Neben externen Objekten gibt es manchmal auch die Notwendigkeit, auf Objekte in der Konfiguration zu verweisen. Dies geschieht implizit durch das Konfigurationssystem für Dinge, von denen es weiß. Zum Beispiel wird der Zeichenkettenwert 'DEBUG' für eine level in einem Logger oder Handler automatisch in den Wert logging.DEBUG konvertiert, und die Einträge handlers, filters und formatter nehmen eine Objekt-ID entgegen und lösen sie zum entsprechenden Zielobjekt auf.
Für benutzerdefinierte Objekte, die dem Modul logging nicht bekannt sind, ist jedoch ein generischerer Mechanismus erforderlich. Betrachten Sie beispielsweise logging.handlers.MemoryHandler, der ein target-Argument entgegennimmt, das ein anderer Handler ist, an den delegiert werden soll. Da das System diese Klasse bereits kennt, muss im Konfigurationsfall das gegebene target nur die Objekt-ID des relevanten Zielhandlers sein, und das System löst zum Handler aus der ID auf. Wenn jedoch ein Benutzer eine my.package.MyHandler definiert, die einen alternate Handler hat, wüsste das Konfigurationssystem nicht, dass sich alternate auf einen Handler bezieht. Um dem Rechnung zu tragen, ermöglicht ein generisches Auflösungssystem dem Benutzer die Angabe.
handlers:
file:
# configuration of file handler goes here
custom:
(): my.package.MyHandler
alternate: cfg://handlers.file
Die wörtliche Zeichenkette 'cfg://handlers.file' wird auf ähnliche Weise wie Zeichenketten mit dem Präfix ext:// aufgelöst, sucht jedoch in der Konfiguration selbst und nicht im Import-Namespace. Der Mechanismus ermöglicht den Zugriff über Punkt oder Index, ähnlich wie bei str.format. Somit würde im folgenden Schnipsel
handlers:
email:
class: logging.handlers.SMTPHandler
mailhost: localhost
fromaddr: my_app@domain.tld
toaddrs:
- support_team@domain.tld
- dev_team@domain.tld
subject: Houston, we have a problem.
in der Konfiguration, die Zeichenkette 'cfg://handlers' auf das Dictionary mit dem Schlüssel handlers aufgelöst werden, die Zeichenkette 'cfg://handlers.email' auf das Dictionary mit dem Schlüssel email im handlers-Dictionary und so weiter. Die Zeichenkette 'cfg://handlers.email.toaddrs[1]' würde zu 'dev_team@domain.tld' aufgelöst werden und die Zeichenkette 'cfg://handlers.email.toaddrs[0]' zum Wert 'support_team@domain.tld'. Der Wert subject könnte entweder über 'cfg://handlers.email.subject' oder äquivalent über 'cfg://handlers.email[subject]' zugegriffen werden. Die letztere Form muss nur verwendet werden, wenn der Schlüssel Leerzeichen oder nicht-alphanumerische Zeichen enthält. Bitte beachten Sie, dass die Zeichen [ und ] in den Schlüsseln nicht erlaubt sind. Wenn ein Indexwert nur dezimale Ziffern enthält, wird der Zugriff über den entsprechenden ganzzahligen Wert versucht, und bei einem Fehlschlag wird auf den Zeichenkettenwert zurückgegriffen.
Bei einer Zeichenkette cfg://handlers.myhandler.mykey.123 wird diese zu config_dict['handlers']['myhandler']['mykey']['123'] aufgelöst. Wenn die Zeichenkette als cfg://handlers.myhandler.mykey[123] angegeben wird, versucht das System, den Wert aus config_dict['handlers']['myhandler']['mykey'][123] abzurufen, und greift bei einem Fehlschlag auf config_dict['handlers']['myhandler']['mykey']['123'] zurück.
Importauflösung und benutzerdefinierte Importer¶
Die Importauflösung verwendet standardmäßig die eingebaute Funktion __import__() für ihre Importe. Möglicherweise möchten Sie diese durch Ihren eigenen Importmechanismus ersetzen: Wenn ja, können Sie das Attribut importer der DictConfigurator oder ihrer Oberklasse, der BaseConfigurator-Klasse, ersetzen. Sie müssen jedoch vorsichtig sein, aufgrund der Art und Weise, wie Funktionen über Deskriptoren aus Klassen aufgerufen werden. Wenn Sie ein Python-aufrufbares Objekt für Ihre Importe verwenden und es auf Klassenebene anstatt auf Instanzebene definieren möchten, müssen Sie es mit staticmethod() umschließen. Zum Beispiel.
from importlib import import_module
from logging.config import BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)
Sie müssen es nicht mit staticmethod() umschließen, wenn Sie das Import-Callable auf einer Konfigurator-*Instanz* festlegen.
Konfiguration von QueueHandler und QueueListener¶
Wenn Sie einen QueueHandler konfigurieren möchten, wobei zu beachten ist, dass dieser normalerweise in Verbindung mit einem QueueListener verwendet wird, können Sie beide zusammen konfigurieren. Nach der Konfiguration ist die QueueListener-Instanz als Attribut listener des erstellten Handlers verfügbar, und dieser ist wiederum über getHandlerByName() für Sie verfügbar, indem Sie den Namen übergeben, den Sie für den QueueHandler in Ihrer Konfiguration verwendet haben. Das Dictionary-Schema für die Konfiguration des Paares ist im folgenden Beispiel-YAML-Schnipsel dargestellt.
handlers:
qhand:
class: logging.handlers.QueueHandler
queue: my.module.queue_factory
listener: my.package.CustomListener
handlers:
- hand_name_1
- hand_name_2
...
Die Schlüssel queue und listener sind optional.
Wenn der Schlüssel queue vorhanden ist, kann der entsprechende Wert einer der folgenden sein:
Ein Objekt, das die öffentlichen APIs
Queue.put_nowaitundQueue.getimplementiert. Dies kann beispielsweise eine tatsächliche Instanz vonqueue.Queueoder eine Unterklasse davon sein, oder ein Proxy, der übermultiprocessing.managers.SyncManager.Queue()erhalten wurde.Dies ist natürlich nur möglich, wenn Sie das Konfigurationsdictionary im Code erstellen oder ändern.
Eine Zeichenkette, die sich zu einem aufrufbaren Objekt auflöst, das bei Aufruf ohne Argumente die zu verwendende Queue-Instanz zurückgibt. Dieses aufrufbare Objekt könnte eine Unterklasse von
queue.Queueoder eine Funktion sein, die eine geeignete Queue-Instanz zurückgibt, wie z. B.my.module.queue_factory().Ein Dictionary mit einem Schlüssel
'()', das auf die übliche Weise wie in Benutzerdefinierte Objekte besprochen konstruiert wird. Das Ergebnis dieser Konstruktion sollte eine Instanz vonqueue.Queuesein.
Wenn der Schlüssel queue fehlt, wird eine Standard-unbegrenzte Instanz von queue.Queue erstellt und verwendet.
Wenn der Schlüssel listener vorhanden ist, kann der entsprechende Wert einer der folgenden sein:
Eine Unterklasse von
logging.handlers.QueueListener. Dies ist natürlich nur möglich, wenn Sie das Konfigurationsdictionary im Code erstellen oder ändern.Eine Zeichenkette, die sich zu einer Klasse auflöst, die eine Unterklasse von
QueueListenerist, wie z. B.'my.package.CustomListener'.Ein Dictionary mit einem Schlüssel
'()', das auf die übliche Weise wie in Benutzerdefinierte Objekte besprochen konstruiert wird. Das Ergebnis dieser Konstruktion sollte ein aufrufbares Objekt mit der gleichen Signatur wie der Initialisierer vonQueueListenersein.
Wenn der Schlüssel listener fehlt, wird logging.handlers.QueueListener verwendet.
Die Werte unter dem Schlüssel handlers sind die Namen anderer Handler in der Konfiguration (nicht im obigen Schnipsel gezeigt), die an den Queue-Listener übergeben werden.
Alle benutzerdefinierten Queue-Handler- und Listener-Klassen müssen mit den gleichen Initialisierungssignaturen wie QueueHandler und QueueListener definiert werden.
Hinzugefügt in Version 3.12.
Konfigurationsdateiformat¶
Das von fileConfig() verstandene Konfigurationsdateiformat basiert auf den Funktionalitäten von configparser. Die Datei muss Abschnitte namens [loggers], [handlers] und [formatters] enthalten, die die Entitäten jedes Typs, die in der Datei definiert sind, namentlich identifizieren. Für jede solche Entität gibt es einen separaten Abschnitt, der angibt, wie diese Entität konfiguriert wird. Somit sind für einen Logger namens log01 im Abschnitt [loggers] die relevanten Konfigurationsdetails in einem Abschnitt [logger_log01] enthalten. Ebenso wird für einen Handler namens hand01 im Abschnitt [handlers] seine Konfiguration in einem Abschnitt namens [handler_hand01] gehalten, während ein Formatierer namens form01 im Abschnitt [formatters] seine Konfiguration in einem Abschnitt namens [formatter_form01] spezifiziert. Die Konfiguration des Root-Loggers muss in einem Abschnitt namens [logger_root] angegeben werden.
Hinweis
Die API fileConfig() ist älter als die API dictConfig() und bietet keine Funktionalität, um bestimmte Aspekte des Loggings abzudecken. Zum Beispiel können Sie keine Filter-Objekte konfigurieren, die für das Filtern von Nachrichten über einfache Ganzzahl-Level hinausgehen, mit fileConfig(). Wenn Sie Instanzen von Filter in Ihrer Logging-Konfiguration haben müssen, müssen Sie dictConfig() verwenden. Beachten Sie, dass zukünftige Erweiterungen der Konfigurationsfunktionalität zu dictConfig() hinzugefügt werden, daher ist es ratsam, auf diese neuere API umzusteigen, wenn es Ihnen passt.
Beispiele für diese Abschnitte in der Datei sind unten angegeben.
[loggers]
keys=root,log02,log03,log04,log05,log06,log07
[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09
Der Root-Logger muss ein Level und eine Liste von Handlern angeben. Ein Beispiel für einen Root-Logger-Abschnitt ist unten angegeben.
[logger_root]
level=NOTSET
handlers=hand01
Der Eintrag level kann einer von DEBUG, INFO, WARNING, ERROR, CRITICAL oder NOTSET sein. Nur für den Root-Logger bedeutet NOTSET, dass alle Nachrichten protokolliert werden. Die Level-Werte werden im Kontext des Namensraums des logging-Pakets ausgewertet.
Der Eintrag handlers ist eine durch Kommas getrennte Liste von Handler-Namen, die im Abschnitt [handlers] vorkommen müssen. Diese Namen müssen im Abschnitt [handlers] vorhanden sein und entsprechende Abschnitte in der Konfigurationsdatei haben.
Für Logger, die nicht der Root-Logger sind, sind einige zusätzliche Informationen erforderlich. Dies wird durch das folgende Beispiel veranschaulicht.
[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser
Die Einträge level und handlers werden wie für den Root-Logger interpretiert, mit der Ausnahme, dass, wenn das Level eines Nicht-Root-Loggers als NOTSET angegeben ist, das System Logger in der Hierarchie höher abfragt, um das effektive Level des Loggers zu bestimmen. Der Eintrag propagate wird auf 1 gesetzt, um anzuzeigen, dass Nachrichten zu Handlern höher in der Logger-Hierarchie von diesem Logger weitergeleitet werden müssen, oder auf 0, um anzuzeigen, dass Nachrichten **nicht** an Handler in der Hierarchie weitergeleitet werden. Der Eintrag qualname ist der hierarchische Kanalname des Loggers, d. h. der Name, den die Anwendung zum Abrufen des Loggers verwendet.
Abschnitte, die die Handler-Konfiguration spezifizieren, werden durch die folgenden veranschaulicht.
[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)
Der Eintrag class gibt die Handler-Klasse an (wie durch eval() im Namensraum des logging-Pakets bestimmt). Das level wird wie für Logger interpretiert, und NOTSET bedeutet "alles protokollieren".
Der Eintrag formatter gibt den Schlüsselnamen des Formatierers für diesen Handler an. Wenn leer, wird ein Standardformatierer (logging._defaultFormatter) verwendet. Wenn ein Name angegeben ist, muss er im Abschnitt [formatters] vorkommen und einen entsprechenden Abschnitt in der Konfigurationsdatei haben.
Der Eintrag args ist, wenn er im Kontext des Namensraums des logging-Pakets ausgewertet wird, die Liste der Argumente für den Konstruktor der Handler-Klasse. Beziehen Sie sich auf die Konstruktoren der entsprechenden Handler oder auf die folgenden Beispiele, um zu sehen, wie typische Einträge konstruiert werden. Wenn nicht angegeben, ist der Standardwert ().
Der optionale Eintrag kwargs ist, wenn er im Kontext des Namensraums des logging-Pakets ausgewertet wird, das Dictionary der Schlüsselwortargumente für den Konstruktor der Handler-Klasse. Wenn nicht angegeben, ist der Standardwert {}.
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')
[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')
[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
kwargs={'timeout': 10.0}
[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)
[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}
Abschnitte, die die Formatierer-Konfiguration spezifizieren, werden durch die folgenden typisiert.
[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s %(customfield)s
datefmt=
style=%
validate=True
defaults={'customfield': 'defaultvalue'}
class=logging.Formatter
Die Argumente für die Formatierer-Konfiguration sind dieselben wie die Schlüssel im Dictionary-Schema des Formatierer-Abschnitts.
Der Eintrag defaults ist, wenn er im Kontext des logging-Pakets ausgewertet wird, ein Dictionary mit Standardwerten für benutzerdefinierte Formatierungsfelder. Wenn nicht angegeben, ist der Standardwert None.
Hinweis
Aufgrund der Verwendung von eval(), wie oben beschrieben, gibt es potenzielle Sicherheitsrisiken, die sich aus der Verwendung von listen() zum Senden und Empfangen von Konfigurationen über Sockets ergeben. Die Risiken sind auf Fälle beschränkt, in denen mehrere Benutzer ohne gegenseitiges Vertrauen Code auf derselben Maschine ausführen; siehe die Dokumentation zu listen() für weitere Informationen.
Siehe auch
- Modul
logging API-Referenz für das Logging-Modul.
- Modul
logging.handlers Nützliche Handler, die im Logging-Modul enthalten sind.