Ich bin sicher, dass viele Leute dies getan haben, indem sie einen Loggernamen angegeben und eine Loggerinstanz verwendet haben (wie getLogger (__ name __)
in Python). Ich denke, es gibt viele Leute, die nur an diesen Loggernamen und den zu protokollierenden Namen denken.
Also habe ich versucht zu lernen, wie man mit Easy Python eine gemeinsame Protokollierungsbibliothek erstellt.
Hier ist eine kurze Zusammenfassung Ihrer Bedenken bei der Protokollierung. Beachten Sie, dass diese nicht notwendig und ausreichend sind und dass dieser Artikel nicht für alle eine Lösung bietet.
――Wie verwenden Sie den Namen, den Sie bei der Verwendung von Logger festgelegt haben?
Dieser Artikel ist für Python und Flask gedacht, mit denen ich häufig Validierungscode implementiere. Auf der anderen Seite sind die oben genannten Bedenken für Sprachen und Betriebssysteme (die häufig wie Linux verwendet werden) gleich. Mit anderen Worten, das Wissen darüber, wie sich die diesmal ausgewählte Sprache und das ausgewählte Framework nähern / worauf sich der Programmierer bei der Verwendung dieser konzentrieren sollte, kann auch bei der Verwendung anderer Sprachen und Frameworks verwendet werden. Ich denke es könnte sein.
Die Einführung ist länger geworden.
Um die Protokollierungsbibliothek zu verstehen, müssen wir zunächst die Logger-Instanz kennen.
Wenn Sie Protokolle mit dem Protokollierungspaket schreiben, bitten wir die Logger-Instanz, den Job auszuführen. Logger-Instanzen sind die ** einzige Instanz **, die für bestimmte Protokollierungsaktivitäten verantwortlich ist. Eine gute Möglichkeit für Programmierer, eine Logger-Instanz in ihrem Code zu verwenden, besteht darin, eine an das Protokollierungsmodul angehängte Protokollierungsfunktion aufzurufen (z. B. "logging.warning ()") oder eine Logger-Instanz mit "getLogger (name = None)" abzurufen. Ist diese Protokollierungsmethode aufzurufen. (Es ist keine neue Instanz aus der Logger-Klasse!)
Wie Sie beim Lesen des Protokollierungsmoduls sehen können, ruft jede Protokollierungsfunktion im Protokollierungsmodul selbst die [Root-Logger-Instanzmethode] auf. Ich bin](https://github.com/python/cpython/blob/3.8/Lib/logging/init.py#L2047). Der Root-Logger wird beim Laden des Logging-Moduls generiert und im Bereich des Moduls gehalten.
Wie man es benutzt ist so.
use_root_logger.py
import logging
# https://docs.python.org/ja/3/library/logging.html
# https://docs.python.org/ja/3/howto/logging.html
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything
Als nächstes über die Logger-Instanz. Es gibt nur eine Logger-Instanz im selben Prozess, die von getLogger (name = None) abgerufen werden kann. Mit anderen Worten, es ist Singleton. Jede Logger-Instanz wird von "logging.Manager" verwaltet, und "logging.Manager" selbst wird [instanziiert](https: /), sodass sie beim Laden des Protokollierungsmoduls zu einem Klassenfeld der Klasse "logging.Logger" wird. /github.com/python/cpython/blob/3.8/Lib/logging/init.py#L1890).
logging.Manager
sucht nach einer vorhandenen Logger-Instanz mit dem Argumentnamen als Schlüssel und gibt diese Instanz zurück, falls vorhanden.
Sie müssen nicht lange nachdenken, und so verwenden Sie es.
use_get_logger.py
import logging
# https://docs.python.org/ja/3/library/logging.html
# https://docs.python.org/ja/3/howto/logging.html
logger = logging.getLogger('simple_example')
#Unterlassung
logger.warning('warn message')
Das Wort Root Logger kam heraus. In vielen Protokollierungsbibliotheken ist Logger ein Singleton und hat eine Baumstruktur. (Zumindest weiß ich, dass Python-Protokollierung, java java.util.logging, org.apache.logging.log4j. C # NLog war sicherlich das gleiche.)
Der Logger im Protokollierungsmodul hat eine Baumstruktur mit dem Root-Logger als Apex. Es ist möglicherweise leichter vorstellbar, dass es sich um eine Verzeichnisstruktur für Personen im Internetbereich und Windows-Infrastrukturingenieure wie mich handelt.
Es mag viele Zwecke für dieses Design geben, aber ich denke, der größte Vorteil ist, dass die Namespaces getrennt werden können und die einzige Ressource klar dargestellt werden kann. Da logging.Manager
Logger-Instanzen anhand von Zeichenfolgen identifiziert und verwaltet, ist es einfach, die Eindeutigkeit der Ressource namens Logger-Instanz beizubehalten, da die Methode zum Erstellen einer Baumstruktur wie FQDN tot ist.
Angenommen, Sie haben einen Support-Service für Reisende in Shimoro Onsen eingerichtet. Das Reservierungssystem hat die Subsystem-ID "Buch", das Reservierungs-Web hat "Web", die API hat "API", OSS wird als Entwicklungsframework verwendet und die Protokolle werden getrennt, da sie von verschiedenen Teams betrieben werden. Wenn dies der Fall ist, ist es zweckmäßig, die Logger-Instanz durch diese Einheit zu teilen. So was.
gero_onsen.py
web_book_logger = logging.getLogger('gero.book.web')
... #Legen Sie einen Handler fest, der in die Datenbank eingefügt werden soll, die auf dem Verwaltungsbenutzerbildschirm angezeigt wird
api_book_logger = logging.getLogger('gero.book.api')
... #Stellen Sie die Handler so ein, dass sie zu CloudWatch-Protokollen fliegen
from flask import Flask
app = Flask('gero')
app.logger.addHandler(...) #Richten Sie einen Handler ein, um das Infrastruktur-Team über Slack zu benachrichtigen
Da die Loggernamen durch Verbinden mit "." Klassifiziert und identifiziert werden können, kann das Risiko eines versehentlichen Tragens des Namens und infolgedessen eines Überschreibens des Logger-Betriebs verringert werden.
Es gibt noch eine weitere Funktion, die für Entwickler praktisch ist. Das heißt, der im oberen Logger festgelegte Inhalt wird vom untergeordneten Element geerbt.
Aufbau | Von oben übernommen * |
---|---|
Protokollstufe der Logger-Instanz | ✔ |
Handler | ✔ |
Wenn Sie im vorherigen Beispiel die Protokollstufe von "gero. ** book **" ändern, ändern sich auch die Protokollstufen von "gero. ** book.web **" und "gero. ** book.api **". , Gero.book ". Sie können vom Debug-Status in den Nicht-Deubg-Status wechseln, indem Sie nur die obere Logger-Instanz wechseln, ohne die Einstellungen aller Logger-Instanzen zu ändern. Das ist nützlich.
Lass es uns bewegen.
logger_tree_exam_child1.py
import logging
import sys
def init_logger():
# INIT
global gero_book_logger
global gero_question_logger
global gero_book_web_logger
# gero.book
gero_book_logger = logging.getLogger('gero.book')
gero_book_handler = logging.StreamHandler()
gero_book_formatter = logging.Formatter(fmt='%(asctime)-15s [%(name)s] %(message)s')
gero_book_handler.setFormatter(gero_book_formatter)
gero_book_logger.addHandler(gero_book_handler)
# gero.question
gero_question_logger = logging.getLogger('gero_question')
gero_question_handler = logging.StreamHandler()
gero_question_formatter = logging.Formatter(fmt='%(asctime)-15s [%(name)s] %(message)s')
gero_question_handler.setFormatter(gero_question_formatter)
gero_question_logger.addHandler(gero_question_handler)
# gero.book.web (gero.Machen Sie ein Kind aus Buch)
gero_book_web_logger = logging.getLogger('gero.book.web')
# handler,Formatierer nicht eingestellt
init_logger()
# PRINT
print('test 1-1: "gero.book.Gibt es ein Weblog?"', file=sys.stderr)
# SET LOG LEVEL (gero.book.Nicht im Web einstellen)
gero_book_logger.setLevel(logging.DEBUG)
gero_question_logger.setLevel(logging.INFO)
# gero.book
gero_book_logger.debug('debug now')
gero_book_logger.info('info now')
gero_book_logger.warning('warning now')
gero_book_logger.error('error now')
gero_book_logger.critical('critical now')
# gero.question
gero_question_logger.debug('debug now')
gero_question_logger.info('info now')
gero_question_logger.warning('warning now')
gero_question_logger.error('error now')
gero_question_logger.critical('critical now')
# gero.book.web
gero_book_web_logger.debug('debug now')
gero_book_web_logger.info('info now')
gero_book_web_logger.warning('warning now')
gero_book_web_logger.error('error now')
gero_book_web_logger.critical('critical now')
print('test 1-2: "gero.Wenn Sie die Fehlerstufe des Buchloggers ändern, wird gero verwendet.book.Wird sich das Niveau des Webs ändern?"', file=sys.stderr)
gero_book_logger.setLevel(logging.ERROR)
# gero.book.web
gero_book_web_logger.debug('debug now')
gero_book_web_logger.info('info now')
gero_book_web_logger.warning('warning now')
gero_book_web_logger.error('error now')
gero_book_web_logger.critical('critical now')
Das Ausführungsergebnis ist wie folgt.
test 1-1: "gero.book.Gibt es ein Weblog?"
2020-09-05 13:51:02,874 [gero.book] debug now
2020-09-05 13:51:02,875 [gero.book] info now
2020-09-05 13:51:02,875 [gero.book] warning now
2020-09-05 13:51:02,875 [gero.book] error now
2020-09-05 13:51:02,875 [gero.book] critical now
2020-09-05 13:51:02,875 [gero_question] info now
2020-09-05 13:51:02,875 [gero_question] warning now
2020-09-05 13:51:02,875 [gero_question] error now
2020-09-05 13:51:02,875 [gero_question] critical now
2020-09-05 13:51:02,875 [gero.book.web] debug now
2020-09-05 13:51:02,875 [gero.book.web] info now
2020-09-05 13:51:02,875 [gero.book.web] warning now
2020-09-05 13:51:02,875 [gero.book.web] error now
2020-09-05 13:51:02,876 [gero.book.web] critical now
test 1-2: "gero.Wenn Sie die Fehlerstufe des Buchloggers ändern, wird gero verwendet.book.Wird sich das Niveau des Webs ändern?"
2020-09-05 13:51:02,876 [gero.book.web] error now
2020-09-05 13:51:02,876 [gero.book.web] critical now
Process finished with exit code 0
Durch Ändern der Protokollstufe der übergeordneten Logger-Instanz können Sie feststellen, dass sich die Protokollausgabeebene des untergeordneten Protokolls ändert. Was ist also, wenn Sie Ihre eigene Protokollebene für die untergeordnete Logger-Instanz festlegen oder wenn Sie einen eigenen Handler für die untergeordnete Loggger-Instanz festlegen und die Protokollstufe für die übergeordnete Logger-Instanz ändern?
logger_tree_exam_child1.py
import logging
import sys
"""Wenn Sie Ihre eigene Protokollstufe für untergeordnete Logger-Instanzen festlegen
"""
def init_logger():
# INIT
global gero_book_logger
global gero_book_web_logger
# gero.book
gero_book_logger = logging.getLogger('gero.book')
gero_book_handler = logging.StreamHandler()
gero_book_formatter = logging.Formatter(fmt='%(asctime)-15s [%(name)s] %(message)s')
gero_book_handler.setFormatter(gero_book_formatter)
gero_book_logger.addHandler(gero_book_handler)
# gero.book.web (gero.Machen Sie ein Kind aus Buch)
gero_book_web_logger = logging.getLogger('gero.book.web')
# handler,Formatierer nicht eingestellt
init_logger()
# PRINT
print('test 2-1: "gero.book.Nachdem Sie die Protokollstufe für das Web separat eingestellt haben, klicken Sie auf gero.Ändern Sie die Protokollstufe des Buches"', file=sys.stderr)
# SET LOG LEVEL
gero_book_logger.setLevel(logging.DEBUG)
gero_book_web_logger.setLevel(logging.DEBUG)
print('Vorher ändern', file=sys.stderr)
# gero.book
gero_book_logger.debug('Sollte herauskommen')
gero_book_logger.info('Sollte herauskommen')
gero_book_logger.warning('Sollte herauskommen')
gero_book_logger.error('Sollte herauskommen')
gero_book_logger.critical('Sollte herauskommen')
# gero.book.web
gero_book_web_logger.debug('Sollte herauskommen')
gero_book_web_logger.info('Sollte herauskommen')
gero_book_web_logger.warning('Sollte herauskommen')
gero_book_web_logger.error('Sollte herauskommen')
gero_book_web_logger.critical('Sollte herauskommen')
print('Nach der veränderung', file=sys.stderr)
gero_book_logger.setLevel(logging.WARNING)
# gero.book
gero_book_logger.debug('Erscheint nicht')
gero_book_logger.info('Erscheint nicht')
gero_book_logger.warning('Sollte herauskommen')
gero_book_logger.error('Sollte herauskommen')
gero_book_logger.critical('Sollte herauskommen')
# gero.book.web
gero_book_web_logger.debug('Wirst du rauskommen? Erscheint nicht?')
gero_book_web_logger.info('Wirst du rauskommen? Erscheint nicht?')
gero_book_web_logger.warning('Sollte herauskommen')
gero_book_web_logger.error('Sollte herauskommen')
gero_book_web_logger.critical('Sollte herauskommen')
Das Ausführungsergebnis ist wie folgt. Es scheint, dass die für die untergeordnete Instanz "gero.book.web" festgelegte Protokollstufe funktioniert.
test 2-1: "gero.book.Nachdem Sie die Protokollstufe für das Web separat eingestellt haben, klicken Sie auf gero.Ändern Sie die Protokollstufe des Buches"
Vorher ändern
2020-09-06 03:11:27,524 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Sollte herauskommen
Nach der veränderung
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book]Sollte herauskommen
2020-09-06 03:11:27,525 [gero.book.web]Wirst du rauskommen? Erscheint nicht?
2020-09-06 03:11:27,526 [gero.book.web]Wirst du rauskommen? Erscheint nicht?
2020-09-06 03:11:27,526 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,526 [gero.book.web]Sollte herauskommen
2020-09-06 03:11:27,526 [gero.book.web]Sollte herauskommen
Process finished with exit code 0
Fügen wir nun einen Handler zu "gero.book.web" hinzu. Dieses Mal legen wir die Protokollebene für "gero.book.web" nicht fest.
logger_tree_exam_child3.py
import logging
import sys
"""Wenn Sie Ihren eigenen Handler für eine untergeordnete Loggger-Instanz festlegen
"""
def init_logger():
# INIT
global gero_book_logger
global gero_book_web_logger
# gero.book
gero_book_logger = logging.getLogger('gero.book')
gero_book_handler = logging.StreamHandler()
gero_book_formatter = logging.Formatter(fmt='%(asctime)-15s [%(name)s] %(message)s')
gero_book_handler.setFormatter(gero_book_formatter)
gero_book_logger.addHandler(gero_book_handler)
# gero.book.web (gero.Machen Sie ein Kind aus Buch)
gero_book_web_logger = logging.getLogger('gero.book.web')
init_logger()
# PRINT
print('test 3-1: "gero.book.Nachdem Sie die Protokollstufe für das Web separat eingestellt haben, klicken Sie auf gero.Ändern Sie die Protokollstufe des Buches"', file=sys.stderr)
# SET LOG LEVEL
gero_book_logger.setLevel(logging.DEBUG)
print('Vorher ändern', file=sys.stderr)
# gero.book
gero_book_logger.debug('Sollte herauskommen')
gero_book_logger.info('Sollte herauskommen')
gero_book_logger.warning('Sollte herauskommen')
gero_book_logger.error('Sollte herauskommen')
gero_book_logger.critical('Sollte herauskommen')
# gero.book.web
gero_book_web_logger.debug('Sollte herauskommen')
gero_book_web_logger.info('Sollte herauskommen')
gero_book_web_logger.warning('Sollte herauskommen')
gero_book_web_logger.error('Sollte herauskommen')
gero_book_web_logger.critical('Sollte herauskommen')
print('Nach der veränderung', file=sys.stderr)
print('- gero.book.Handler zur Webseite hinzugefügt', file=sys.stderr)
gero_book_web_handler = logging.StreamHandler()
gero_book_web_formatter = logging.Formatter(fmt='%(asctime)-15s [%(name)s] ### this is web ### %(message)s')
gero_book_web_handler.setFormatter(gero_book_web_formatter)
gero_book_web_logger.addHandler(gero_book_web_handler)
print('- gero.Ändern Sie die Buchprotokollstufe in WARNUNG', file=sys.stderr)
gero_book_logger.setLevel(logging.WARNING)
# gero.book
gero_book_logger.debug('Erscheint nicht')
gero_book_logger.info('Erscheint nicht')
gero_book_logger.warning('Sollte herauskommen')
gero_book_logger.error('Sollte herauskommen')
gero_book_logger.critical('Sollte herauskommen')
# gero.book.web
gero_book_web_logger.debug('Wirst du rauskommen? Erscheint nicht?')
gero_book_web_logger.info('Wirst du rauskommen? Erscheint nicht?')
gero_book_web_logger.warning('Sollte herauskommen')
gero_book_web_logger.error('Sollte herauskommen')
gero_book_web_logger.critical('Sollte herauskommen')
print(gero_book_web_logger.handlers)
Das Ausführungsergebnis ist wie folgt. Es gibt zwei Protokolle für "gero.book.web". Da der hinzugefügte Handler wirklich "hinzugefügt" wird, besteht das Verhalten darin, dass sowohl der Handler des übergeordneten Loggers als auch der ursprüngliche Handler ausgegeben werden. Außerdem wurde das Protokoll des ursprünglichen Handlers gemäß der übergeordneten LogLevel verschoben.
test 3-1: "gero.book.Nachdem Sie die Protokollstufe für das Web separat eingestellt haben, klicken Sie auf gero.Ändern Sie die Protokollstufe des Buches"
Vorher ändern
2020-09-06 03:21:11,709 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,709 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book.web]Sollte herauskommen
Nach der veränderung
- gero.book.Handler zur Webseite hinzugefügt
- gero.Ändern Sie die Buchprotokollstufe in WARNUNG
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,710 [gero.book]Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web] ### this is web ###Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web] ### this is web ###Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web]Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web] ### this is web ###Sollte herauskommen
2020-09-06 03:21:11,711 [gero.book.web]Sollte herauskommen
[<StreamHandler <stderr> (NOTSET)>]
Process finished with exit code 0
Im obigen Beispielcode habe ich am Ende einen kleinen "print (gero_book_web_logger.handlers)" geschrieben. Das Ergebnis dieser Ausführung ist "[<StreamHandler
logging/__init__.py
#Abkürzung
def callHandlers(self, record):
#Abkürzung
c = self
found = 0
while c:
for hdlr in c.handlers:
found = found + 1
if record.levelno >= hdlr.level:
hdlr.handle(record)
if not c.propagate:
c = None #break out
else:
c = c.parent
#Abkürzung
Der von getLogger (name = None) angegebene Name ist also nicht nur ein Name, sondern wie der absolute Pfad von Logger und hierarchisch. Darüber hinaus ist ersichtlich, dass die auf der höheren Ebene vorgenommenen Einstellungen vom Kind geerbt werden und dass die vom Kind vorgenommenen Einstellungen Vorrang haben.
Aufgrund dieser Aktionen kann ich mir vorstellen, dass die Verwendung einfacher ist, wenn Sie sie wie folgt verwenden.
DEBUG
.logging.get_logger (" path.to.logger ")
festgelegte Instanz wird abgerufen.In diesem Artikel wird zunächst erwähnt, wie die Protokollierungsfunktion (dh der Root-Logger) des Protokollierungsmoduls, das auch in der offiziellen Python-Dokumentation erwähnt wird, zur Organisation der Idee verwendet wird. Ich persönlich empfehle jedoch nicht, den Root-Logger zu verwenden. Es ist einfach, etwas wie "Wenn Sie versuchen, die Protokolle eines von Ihnen geschriebenen Programms auszuspucken, erhalten Sie viele Protokolle aus anderen Bibliotheken, die Sie importieren, und Sie sind auf Disk Full gestorben."
Auf der anderen Seite sind Protokollierungs- und Protokollierungsbibliotheken so tiefgreifend, dass sie nicht einfach zu schreiben sind. Deshalb habe ich mich entschlossen, von Grund auf neu zu lernen und sie im Artikel zu belassen.
Links
Unten verschiedene Licks.
--logging --- Protokollierungsfunktion für Python
Recommended Posts