Anmelden bei Python

Anmelden bei Python

Die Protokollierung ist ein sehr nützliches Werkzeug in der Toolbox eines Programmierers. Es kann Ihnen helfen, den Ablauf eines Programms besser zu verstehen und Szenarien zu entdecken, an die Sie bei der Entwicklung möglicherweise nicht einmal gedacht haben.

Protokolle bieten Entwicklern zusätzliche Augen, die ständig den Fluss betrachten, den eine Anwendung durchläuft. Sie können Informationen speichern, z. B. welcher Benutzer oder welche IP auf die Anwendung zugegriffen hat. Wenn ein Fehler auftritt, können sie mehr Einblicke als eine Stapelverfolgung liefern, indem sie Ihnen mitteilen, wie der Status des Programms war, bevor es in der Codezeile ankam, in der der Fehler aufgetreten ist.

Indem Sie nützliche Daten an den richtigen Stellen protokollieren, können Sie Fehler nicht nur einfach debuggen, sondern anhand der Daten auch die Leistung der Anwendung analysieren, um die Skalierung zu planen, oder Nutzungsmuster betrachten, um das Marketing zu planen.

Python bietet ein Protokollierungssystem als Teil seiner Standardbibliothek, sodass Sie Ihrer Anwendung schnell eine Protokollierung hinzufügen können. In diesem Artikel erfahren Sie, warum die Verwendung dieses Moduls die beste Möglichkeit ist, Ihrer Anwendung Protokollierung hinzuzufügen, und wie Sie schnell loslegen können, und erhalten eine Einführung in einige der verfügbaren erweiterten Funktionen.

Free Bonus:5 Thoughts On Python Mastery, ein kostenloser Kurs für Python-Entwickler, der Ihnen die Roadmap und die Denkweise zeigt, die Sie benötigen, um Ihre Python-Fähigkeiten auf die nächste Stufe zu bringen.

Das Protokollierungsmodul

Das Protokollierungsmodul in Python ist ein gebrauchsfertiges und leistungsstarkes Modul, das sowohl für Anfänger als auch für Unternehmensteams entwickelt wurde. Es wird von den meisten Python-Bibliotheken von Drittanbietern verwendet, sodass Sie Ihre Protokollnachrichten mit denen aus diesen Bibliotheken integrieren können, um ein homogenes Protokoll für Ihre Anwendung zu erstellen.

Das Hinzufügen der Protokollierung zu Ihrem Python-Programm ist so einfach:

import logging

Wenn das Protokollierungsmodul importiert ist, können Sie einen sogenannten "Logger" verwenden, um Nachrichten zu protokollieren, die Sie sehen möchten. Standardmäßig gibt es 5 Standardstufen, die den Schweregrad von Ereignissen angeben. Jedes hat eine entsprechende Methode, mit der Ereignisse mit diesem Schweregrad protokolliert werden können. Die definierten Stufen sind in der Reihenfolge zunehmender Schwere folgende:

  • DEBUGGEN

  • INFO

  • WARNUNG

  • ERROR

  • KRITISCH

Das Protokollierungsmodul bietet Ihnen einen Standardprotokollierer, mit dem Sie loslegen können, ohne viel Konfiguration vornehmen zu müssen. Die entsprechenden Methoden für jede Ebene können wie im folgenden Beispiel gezeigt aufgerufen werden:

import logging

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

Die Ausgabe des obigen Programms würde folgendermaßen aussehen:

WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message

Die Ausgabe zeigt den Schweregrad vor jeder Nachricht zusammen mitroot. Dies ist der Name, den das Protokollierungsmodul seinem Standardlogger gibt. (Logger werden in späteren Abschnitten ausführlich erläutert.) Dieses Format, in dem Ebene, Name und Nachricht durch einen Doppelpunkt (:) getrennt angezeigt werden, ist das Standardausgabeformat, das so konfiguriert werden kann, dass es beispielsweise Zeitstempel enthält. Zeilennummer und andere Details.

Beachten Sie, dass die Nachrichtendebug() undinfo()nicht protokolliert wurden. Dies liegt daran, dass das Protokollierungsmodul die Nachrichten standardmäßig mit einem Schweregrad vonWARNING oder höher protokolliert. Sie können dies ändern, indem Sie das Protokollierungsmodul so konfigurieren, dass Ereignisse aller Ebenen protokolliert werden, wenn Sie möchten. Sie können auch Ihre eigenen Schweregrade definieren, indem Sie die Konfigurationen ändern. Dies wird jedoch im Allgemeinen nicht empfohlen, da dies zu Verwechslungen mit den Protokollen einiger Bibliotheken von Drittanbietern führen kann, die Sie möglicherweise verwenden.

Grundkonfigurationen

Sie können die Methode+basicConfig(**+` + kwargs + +) + `verwenden, um die Protokollierung zu konfigurieren:

„Sie werden feststellen, dass das Protokollierungsmodul den PEP8-Styleguide verletzt und die Namenskonventionen voncamelCaseverwendet. Dies liegt daran, dass es von Log4j, einem Protokollierungsdienstprogramm in Java, übernommen wurde. Es ist ein bekanntes Problem im Paket, aber als beschlossen wurde, es zur Standardbibliothek hinzuzufügen, wurde es bereits von den Benutzern übernommen, und eine Änderung an die PEP8-Anforderungen würde zu Abwärtskompatibilitätsproblemen führen. “ (Source)

Einige der häufig verwendeten Parameter fürbasicConfig() sind die folgenden:

  • level: Der Root-Logger wird auf den angegebenen Schweregrad gesetzt.

  • filename: Gibt die Datei an.

  • filemode: Wennfilename angegeben ist, wird die Datei in diesem Modus geöffnet. Der Standardwert ista, was Anhängen bedeutet.

  • format: Dies ist das Format der Protokollnachricht.

Mit dem Parameterlevel können Sie festlegen, auf welcher Ebene von Protokollnachrichten Sie aufzeichnen möchten. Dies kann durch Übergeben einer der in der Klasse verfügbaren Konstanten erfolgen. Auf diese Weise können alle Protokollierungsaufrufe auf oder über dieser Ebene protokolliert werden. Hier ist ein Beispiel:

import logging

logging.basicConfig(level=logging.DEBUG)
logging.debug('This will get logged')
DEBUG:root:This will get logged

Alle Ereignisse auf oder überDEBUGwerden jetzt protokolliert.

In ähnlicher Weise können für die Protokollierung in einer Datei anstelle der Konsolefilename undfilemode verwendet werden, und Sie können das Format der Nachricht mitformat festlegen. Das folgende Beispiel zeigt die Verwendung aller drei:

import logging

logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
logging.warning('This will get logged to a file')
root - ERROR - This will get logged to a file

Die Nachricht sieht folgendermaßen aus, wird jedoch in eine Datei mit dem Namenapp.log anstelle der Konsole geschrieben. Der Dateimodus ist aufw eingestellt, was bedeutet, dass die Protokolldatei bei jedem Aufruf vonbasicConfig() im Schreibmodus geöffnet wird und bei jedem Programmlauf die Datei neu geschrieben wird. Die Standardkonfiguration für den Dateimodus ista, die angehängt wird.

Sie können den Root-Logger noch weiter anpassen, indem Sie mehr Parameter fürbasicConfig() verwenden, diehere enthalten.

Es ist zu beachten, dass das Aufrufen vonbasicConfig() zum Konfigurieren des Root-Loggers nur funktioniert, wenn der Root-Logger zuvor noch nicht konfiguriert wurde. Basically, this function can only be called once.

debug(),info(),warning(),error() undcritical() rufen auchbasicConfig() ohne Argumente automatisch auf, wenn sie zuvor nicht aufgerufen wurden. Dies bedeutet, dass Sie nach dem ersten Aufruf einer der oben genannten Funktionen den Root-Logger nicht mehr konfigurieren können, da er die FunktionbasicConfig() intern aufgerufen hätte.

Die Standardeinstellung inbasicConfig() besteht darin, den Logger so einzustellen, dass er im folgenden Format auf die Konsole schreibt:

ERROR:root:This is an error message

Formatieren der Ausgabe

Während Sie jede Variable, die als Zeichenfolge aus Ihrem Programm dargestellt werden kann, als Nachricht an Ihre Protokolle übergeben können, gibt es einige grundlegende Elemente, die bereits Teil derLogRecord sind und problemlos zum Ausgabeformat hinzugefügt werden können. Wenn Sie die Prozess-ID zusammen mit der Ebene und der Nachricht protokollieren möchten, können Sie Folgendes tun:

import logging

logging.basicConfig(format='%(process)d-%(levelname)s-%(message)s')
logging.warning('This is a Warning')
18472-WARNING-This is a Warning

format kann eine Zeichenfolge mitLogRecord Attributen in einer beliebigen Anordnung verwenden. Die gesamte Liste der verfügbaren Attribute finden Sie inhere.

Hier ist ein weiteres Beispiel, in dem Sie Datums- und Uhrzeitinformationen hinzufügen können:

import logging

logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
logging.info('Admin logged in')
2018-07-11 20:12:06,288 - Admin logged in

%(asctime)s addiert den Zeitpunkt der Erstellung derLogRecord. Das Format kann mit dem Attributdatefmt geändert werden, das dieselbe Formatierungssprache wie die Formatierungsfunktionen im datetime-Modul verwendet, z. B.time.strftime():

import logging

logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
logging.warning('Admin logged out')
12-Jul-18 20:53:19 - Admin logged out

Sie finden die Anleitunghere.

Variable Daten protokollieren

In den meisten Fällen möchten Sie dynamische Informationen aus Ihrer Anwendung in die Protokolle aufnehmen. Sie haben gesehen, dass die Protokollierungsmethoden eine Zeichenfolge als Argument verwenden, und es scheint natürlich, eine Zeichenfolge mit variablen Daten in einer separaten Zeile zu formatieren und an die Protokollmethode zu übergeben. Dies kann jedoch tatsächlich direkt erfolgen, indem eine Formatzeichenfolge für die Nachricht verwendet und die variablen Daten als Argumente angehängt werden. Hier ist ein Beispiel:

import logging

name = 'John'

logging.error('%s raised an error', name)
ERROR:root:John raised an error

Die an die Methode übergebenen Argumente würden als variable Daten in die Nachricht aufgenommen.

Während Sie jeden Formatierungsstil verwenden können, sind die in Python 3.6 eingeführtenf-stringseine hervorragende Möglichkeit, Zeichenfolgen zu formatieren, da sie dazu beitragen können, die Formatierung kurz und leicht lesbar zu halten:

import logging

name = 'John'

logging.error(f'{name} raised an error')
ERROR:root:John raised an error

Erfassen von Stapelspuren

Mit dem Protokollierungsmodul können Sie auch die vollständigen Stapelspuren in einer Anwendung erfassen. Exception information kann erfasst werden, wenn der Parameterexc_info alsTrue übergeben wird und die Protokollierungsfunktionen wie folgt aufgerufen werden:

import logging

a = 5
b = 0

try:
  c = a / b
except Exception as e:
  logging.error("Exception occurred", exc_info=True)
ERROR:root:Exception occurred
Traceback (most recent call last):
  File "exceptions.py", line 6, in 
    c = a / b
ZeroDivisionError: division by zero
[Finished in 0.2s]

Wennexc_info nicht aufTrue gesetzt ist, sagt uns die Ausgabe des obigen Programms nichts über die Ausnahme aus, die in einem realen Szenario möglicherweise nicht so einfach ist wieZeroDivisionError) s. Stellen Sie sich vor, Sie versuchen, einen Fehler in einer komplizierten Codebasis mit einem Protokoll zu debuggen, das nur Folgendes anzeigt:

ERROR:root:Exception occurred

Hier ein kurzer Tipp: Wenn Sie von einem Ausnahmebehandler aus protokollieren, verwenden Sie die Methodelogging.exception(), mit der eine Nachricht mit der StufeERROR protokolliert und der Nachricht Ausnahmeinformationen hinzugefügt werden. Einfacher ausgedrückt ist das Aufrufen vonlogging.exception() wie das Aufrufen vonlogging.error(exc_info=True). Da diese Methode jedoch immer Ausnahmeinformationen ausgibt, sollte sie nur von einem Ausnahmehandler aufgerufen werden. Schauen Sie sich dieses Beispiel an:

import logging

a = 5
b = 0
try:
  c = a / b
except Exception as e:
  logging.exception("Exception occurred")
ERROR:root:Exception occurred
Traceback (most recent call last):
  File "exceptions.py", line 6, in 
    c = a / b
ZeroDivisionError: division by zero
[Finished in 0.2s]

Die Verwendung vonlogging.exception() würde ein Protokoll auf der Ebene vonERROR anzeigen. Wenn Sie dies nicht möchten, können Sie eine der anderen Protokollierungsmethoden vondebug() biscritical() aufrufen und den Parameterexc_info alsTrue übergeben.

Klassen und Funktionen

Bisher haben wir den Standardloggerroot gesehen, der vom Protokollierungsmodul verwendet wird, wenn seine Funktionen direkt wie folgt aufgerufen werden:logging.debug(). Sie können (und sollten) Ihren eigenen Logger durchcreating an object derLogger-Klasse definieren, insbesondere wenn Ihre Anwendung mehrere Module enthält. Schauen wir uns einige der Klassen und Funktionen im Modul an.

Die im Protokollierungsmodul am häufigsten verwendeten Klassen sind die folgenden:

  • Logger: Dies ist die Klasse, deren Objekte im Anwendungscode direkt zum Aufrufen der Funktionen verwendet werden.

  • LogRecord: Logger erstellen automatischLogRecord Objekte, die alle Informationen zum zu protokollierenden Ereignis enthalten, z. B. den Namen des Loggers, die Funktion, die Zeilennummer, die Nachricht und mehr.

  • Handler: Handler senden dieLogRecord an das gewünschte Ausgabeziel, z. B. die Konsole oder eine Datei. Handler ist eine Basis für Unterklassen wieStreamHandler,FileHandler,SMTPHandler,HTTPHandler und mehr. Diese Unterklassen senden die Protokollausgaben an entsprechende Ziele wiesys.stdout oder eine Festplattendatei.

  • Formatter: Hier geben Sie das Format der Ausgabe an, indem Sie ein Zeichenfolgenformat angeben, in dem die Attribute aufgeführt sind, die die Ausgabe enthalten soll.

Von diesen beschäftigen wir uns hauptsächlich mit den Objekten der KlasseLogger, die mit der Funktionlogging.getLogger(name) auf Modulebene instanziiert werden. Bei mehreren Aufrufen vongetLogger() mit demselbenname wird ein Verweis auf dasselbeLogger-Objekt zurückgegeben, sodass wir die Logger-Objekte nicht an jeden Teil übergeben müssen, an dem sie benötigt werden. Hier ist ein Beispiel:

import logging

logger = logging.getLogger('example_logger')
logger.warning('This is a warning')
This is a warning

Dadurch wird ein benutzerdefinierter Logger mit dem Namenexample_logger erstellt. Im Gegensatz zum Root-Logger ist der Name eines benutzerdefinierten Loggers jedoch nicht Teil des Standardausgabeformats und muss der Konfiguration hinzugefügt werden. Wenn Sie es in einem Format konfigurieren, in dem der Name des Loggers angezeigt wird, erhalten Sie eine Ausgabe wie folgt:

WARNING:example_logger:This is a warning

Im Gegensatz zum Root-Logger kann ein benutzerdefinierter Logger nicht mitbasicConfig() konfiguriert werden. Sie müssen es mit Handlern und Formatierern konfigurieren:

„Es wird empfohlen, Logger auf Modulebene zu verwenden, indem__name__ als Namensparameter angetLogger() übergeben wird, um ein Loggerobjekt zu erstellen, da der Name des Loggers selbst angibt, von wo die Ereignisse protokolliert werden . __name__ ist eine spezielle in Python integrierte Variable, die den Namen des aktuellen Moduls ergibt. “ (Source)

Handler verwenden

Handler kommen ins Spiel, wenn Sie Ihre eigenen Logger konfigurieren und die Logs bei ihrer Generierung an mehrere Stellen senden möchten. Handler senden die Protokollnachrichten an konfigurierte Ziele wie den Standardausgabestream oder eine Datei oder über HTTP oder über SMTP an Ihre E-Mail.

Ein von Ihnen erstellter Logger kann mehr als einen Handler haben. Dies bedeutet, dass Sie ihn so einrichten können, dass er in einer Protokolldatei gespeichert und auch per E-Mail gesendet wird.

Wie bei Loggern können Sie auch den Schweregrad in Handlern festlegen. Dies ist nützlich, wenn Sie mehrere Handler für denselben Logger festlegen möchten, für jeden jedoch unterschiedliche Schweregrade. Beispielsweise möchten Sie möglicherweise, dass Protokolle mit LevelWARNING und höher in der Konsole protokolliert werden, aber alles mit LevelERROR und höher sollte auch in einer Datei gespeichert werden. Hier ist ein Programm, das das macht:

# logging_example.py

import logging

# Create a custom logger
logger = logging.getLogger(__name__)

# Create handlers
c_handler = logging.StreamHandler()
f_handler = logging.FileHandler('file.log')
c_handler.setLevel(logging.WARNING)
f_handler.setLevel(logging.ERROR)

# Create formatters and add it to handlers
c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format)
f_handler.setFormatter(f_format)

# Add handlers to the logger
logger.addHandler(c_handler)
logger.addHandler(f_handler)

logger.warning('This is a warning')
logger.error('This is an error')
__main__ - WARNING - This is a warning
__main__ - ERROR - This is an error

Hier erstelltlogger.warning() einLogRecord, das alle Informationen des Ereignisses enthält, und übergibt sie an alle Handler, die es hat:c_handler undf_handler.

c_handler ist einStreamHandler mit der StufeWARNING und verwendet die Informationen ausLogRecord, um eine Ausgabe in dem angegebenen Format zu generieren und auf der Konsole zu drucken. f_handler ist einFileHandler mit LevelERROR und ignoriert diesesLogRecord, da sein LevelWARNING ist.

Wennlogger.error() aufgerufen wird, verhält sichc_handler genau wie zuvor, undf_handler erhältLogRecord auf der Ebene vonERROR, sodass nur eine Ausgabe generiert wird wiec_handler, aber anstatt es auf die Konsole zu drucken, schreibt es es in die angegebene Datei in diesem Format:

2018-08-03 16:12:21,723 - __main__ - ERROR - This is an error

Der Name des Loggers, der der Variablen__name__ entspricht, wird als__main__ protokolliert. Dies ist der Name, den Python dem Modul zuweist, in dem die Ausführung beginnt. Wenn diese Datei von einem anderen Modul importiert wird, entspricht die Variable__name__ ihrem Namenlogging_example. So würde es aussehen:

# run.py

import logging_example
logging_example - WARNING - This is a warning
logging_example - ERROR - This is an error

Andere Konfigurationsmethoden

Sie können die Protokollierung wie oben gezeigt mithilfe der Modul- und Klassenfunktionen konfigurieren oder indem Sie eine Konfigurationsdatei oderdictionary erstellen und mitfileConfig() bzw.dictConfig() laden. Diese sind nützlich, wenn Sie Ihre Protokollierungskonfiguration in einer laufenden Anwendung ändern möchten.

Hier ist eine Beispieldateikonfiguration:

[loggers]
keys=root,sampleLogger

[handlers]
keys=consoleHandler

[formatters]
keys=sampleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
propagate=0

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

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

In der obigen Datei befinden sich zwei Logger, ein Handler und ein Formatierer. Nachdem ihre Namen definiert wurden, werden sie konfiguriert, indem die Wörter Logger, Handler und Formatierer hinzugefügt werden, bevor ihre Namen durch einen Unterstrich getrennt werden.

Um diese Konfigurationsdatei zu laden, müssen SiefileConfig() verwenden:

import logging
import logging.config

logging.config.fileConfig(fname='file.conf', disable_existing_loggers=False)

# Get the logger specified in the file
logger = logging.getLogger(__name__)

logger.debug('This is a debug message')
2018-07-13 13:57:45,467 - __main__ - DEBUG - This is a debug message

Der Pfad der Konfigurationsdatei wird als Parameter an die MethodefileConfig() übergeben, und der Parameterdisable_existing_loggers wird verwendet, um die beim Aufruf der Funktion vorhandenen Logger beizubehalten oder zu deaktivieren. Der Standardwert istTrue, wenn nicht erwähnt.

Hier ist dieselbe Konfiguration in einem YAML-Format für den Wörterbuchansatz:

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:
  sampleLogger:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]

Hier ist ein Beispiel, das zeigt, wie die Konfiguration aus eineryaml-Datei geladen wird:

import logging
import logging.config
import yaml

with open('config.yaml', 'r') as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger = logging.getLogger(__name__)

logger.debug('This is a debug message')
2018-07-13 14:05:03,766 - __main__ - DEBUG - This is a debug message

Bleib ruhig und lies die Protokolle

Das Protokollierungsmodul wird als sehr flexibel angesehen. Das Design ist sehr praktisch und sollte sofort zu Ihrem Anwendungsfall passen. Sie können einem kleinen Projekt eine grundlegende Protokollierung hinzufügen oder sogar eigene Protokollebenen, Handlerklassen und mehr erstellen, wenn Sie an einem großen Projekt arbeiten.

Wenn Sie Ihre Anwendungen noch nicht angemeldet haben, ist jetzt ein guter Zeitpunkt, um zu beginnen. Wenn Sie es richtig machen, wird die Protokollierung sicherlich viel Reibung in Ihrem Entwicklungsprozess beseitigen und Ihnen helfen, Möglichkeiten zu finden, Ihre Anwendung auf die nächste Stufe zu bringen.