
Teilen Sie:
Renato is a backend software developer and a father of two amazing kids who won’t let him sleep so that he can enjoy spending nights connecting APIs around.
Erstellung eines Python-Fehlermeldewerkzeugs
Ganz gleich, wie sehr wir uns um Qualität und Tests bemühen, es ist fast garantiert, dass eine Software irgendwann einmal schief geht. Daher sind Überwachungsprotokolle, die den Zustand der Anwendung verfolgen, unerlässlich.
Sicherlich gibt es mehrere Dienste und Open-Source-Projekte, die sich um die Überwachung von Anwendungsprotokollen kümmern. Meiner Erfahrung nach sind sie jedoch in der Regel entweder teuer, zeitaufwändig zu integrieren oder mit Funktionen überladen, die ich kaum nutzen werde. Wenn ich kleine Projekte einsetze, die keine ausgefallene Überwachung erfordern, wünsche ich mir manchmal eine native Python-Lösung, um einfache Warnungen zu erhalten, wenn in meinem Code etwas schief läuft.
Der Zweck dieses Tutorials ist es, genau dieses Bedürfnis zu erfüllen. Wir werden ein einfaches und flexibles Python-Fehlermeldewerkzeug erstellen, das in jedes Projekt integriert werden kann. Ein protokollierendes HTTP-Handler-Objekt sendet asynchron Warnmeldungen über die Vonage SMS API auf unsere Telefone, wenn z. B. neue Fehler oder Warnungen eingehen.
Anforderungen
Wir werden im Tutorial Python 3.9.1 (die neueste stabile Version) verwenden, aber der Code sollte auch mit Python 3.6+ funktionieren. Python ist für Linux, macOS und Windows verfügbar. Zum Herunterladen und Installieren folgen Sie bitte den Anweisungen auf der offiziellen Website.
Sie benötigen außerdem einen Vonage Account, um Fehlerwarnungen per SMS zu erhalten. Erstellen Sie einen Account wenn Sie noch nicht registriert sind. Vonage stellt neuen Abonnenten ein Guthaben von 2,00 € zur Verfügung, um die APIs kostenlos zu testen.
Vonage API-Konto
Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.
Der Vonage-API-Schlüssel und das Geheimnis werden ebenfalls benötigt; stellen Sie sicher, dass Sie sie in den Dashboard-Einstellungen:

PyPI http-logging Bibliothek wird für das Zwischenspeichern von Protokollen und die asynchrone Kommunikation mit der Vonage-API. Sie verhindert, dass unsere Haupt-Python-Anwendung durch den Alarmierungsmechanismus unterbrochen wird.
Vorbereitung des lokalen Umfelds
Virtualenv und Abhängigkeiten
Erstellen Sie ein Verzeichnis für das Projekt:
Erstellen einer virtuellen Umgebung ist oft eine gute Praxis, also lassen Sie uns dies zuerst tun:
Auf einem Windows-Computer ersetzen Sie den source Befehl in der letzten Zeile oben durch:
Stellen Sie sicher, dass die Umgebung wie erwartet funktioniert:
Lassen Sie uns nun unsere Python-Abhängigkeitsdatei erstellen:
Öffnen Sie sie mit Ihrem bevorzugten Texteditor und fügen Sie die folgenden Zeilen ein:
http-logging
vonageSchließen Sie die Datei und installieren Sie die Abhängigkeiten mit dem pip install Befehl:
Umgebungsvariablen
Unsere benutzerdefinierte Logging-Logik erfordert einige Informationen, die über Umgebungsvariablen bereitgestellt werden.
Der Vonage API-Schlüssel ist für die Authentifizierung beim SMS-Dienst erforderlich. Für den Versand von SMS-Nachrichten ist außerdem eine Telefonnummer erforderlich.
Der Befehl export Befehl sollte unter Linux und macOS funktionieren. Unter Windows verwenden Sie set stattdessen. Wenn Sie eine PowerShell Konsole verwenden, dann sollte dieser Befehl die Arbeit erledigen:
$Env: VONAGE_API_KEY = "abc123"
$Env: VONAGE_API_SECRET = "xyz123"
$Env: export ALERT_PHONE_NUMBER = "+1234567890" HTTP-Protokollierungs-Handler
Wie bereits erwähnt, werden wir uns auf die http-logging Bibliothek, um unsere Protokolle mit den Vonage-APIs zu verbinden.
Ein nativer HTTP-Protokollierungs-Handler aus der Python-Standardbibliothek würde die Aufgabe ebenfalls erfüllen. Wir werden ihn jedoch nicht verwenden, da er blockierende HTTP-Anfragen erzeugt, was die Ausführung unserer Haupt-Python-Anwendung negativ beeinflussen kann.
Die http-Protokollierung Bibliothek läuft unauffällig in einem Hintergrund-Thread und ist außerdem in der Lage, Protokolle in einer lokalen SQLite-Datenbank zwischenzuspeichern, um die Anzahl der Netzwerkanfragen zu reduzieren. Aus diesen Gründen ist sie viel weniger störend als ein nativer HTTP-Handler.
Die Bibliothek basiert auf der Python Logstash Asyncbasiert, aber verallgemeinert wurde, um mit jedem Backend außer Logstash zu arbeiten (in unserem Tutorial werden wir Vonage verwenden). Lesen Sie mehr darüber in der Projektdokumentations-Wiki.
Vonage HTTP-Transport
Als Erstes müssen wir eine benutzerdefinierte HTTP-Transportklasse erstellen. Diese Klasse enthält Anweisungen, wie Protokolle an die Vonage-API gesendet werden können.
Bevor wir uns damit befassen, erstellen wir eine neue Python-Datei, die unseren benutzerdefinierten Protokollierungscode enthält:
Öffnen Sie jetzt diese Datei - es ist Zeit für etwas Python-Spaß!
Unsere eigene HTTP-Transportklasse erbt von der [http_logging.AsyncHttpTransport](https://github.com/hacktlib/py-async-http-logging/wiki/3.-HTTP-Transport-Class) erben. Importieren Sie zunächst die erforderlichen Bibliotheken am Anfang der Datei und deklarieren Sie dann eine neue Klasse wie unten gezeigt:
import logging
import os
from vonage import Sms
from http_logging import HttpHost, SupportClass
from http_logging.handler import AsyncHttpHandler
from http_logging.transport import AsyncHttpTransport
class VonageHttpTransport(http_logging.transport.AsyncHttpTransport):
passIm Moment verhält sich diese Klasse genau wie das Original. Fügen wir ihr einige benutzerdefinierte Funktionen hinzu. Die AsyncHttpTransport implementiert eine send Methode, die für das Senden von Protokollen an einen entfernten Host verantwortlich ist. Anfänglich verwendet sie die Anfragen Bibliothek für diese Aufgabe. In unserem Fall haben wir die Vonage SDKzur Verfügung, das uns das Leben sehr erleichtert und die Langeweile des HTTP-Protokolls beseitigt.
Ok, genug geredet. Beginnen wir mit der Codierung mit dem Vonage SDK durch die Deklaration einer neuen send Methode:
class VonageHttpTransport(AsyncHttpTransport):
def send(self, events: dict, **kwargs) -> None:
batches = self._HttpTransport__batches(events)
sms_logs = ', '.join([
f"{log['level']['name']}: {log['message']}"
for batch in batches
for log in batch
])
sms_message = f'[Python Logger {self.logger_name}] {sms_logs}'
sms_client = Sms(
key=self.vonage_api_key,
secret=self.vonage_api_secret,
)
response = sms_client.send_message({
'from': f'Python Logger {self.logger_name}',
'to': self.alert_phone_number,
'text': sms_message,
})
if not response['messages'][0]['status'] == 0:
raise ConnectionError(response["messages"][0].get("error-text"))
Die Methode send Methode nimmt ein events Argument; eine Liste, die in einen Stapel von Protokollen mit der HttpTransport.__batches Methode umgewandelt wird. Die Stapel werden dann verarbeitet, um grundlegende Datenpunkte in einen Protokollstring zu extrahieren.
Jeder Protokollstrang enthält nur den Namen der Protokollstufe (z. B. "Warnung" oder "Fehler") und eine Protokollmeldung. SMS steht für Short Message Service (Kurznachrichtendienst), daher wollen wir unsere Warnmeldungen kurz halten. Unser primäres Ziel ist die Warnung, nicht die Unterstützung der vollständigen Fehlersuche per SMS. Es werden nur minimale Informationen gesendet, um den Kontext zu liefern und dem Entwickler bei der Fehlersuche zu helfen.
Die Protokolle werden dann mit der Methode string.join Methode verkettet und mit dem Namen des Loggers versehen, um Informationen über den Anwendungskontext zu erhalten (dies ist hilfreich, wenn mehrere Projekte dieses Warnwerkzeug verwenden).
Zum Schluss instanziieren wir einen vonage.Sms Client aus dem Vonage SDK und verwenden ihn, um die SMS-Nachricht an unser Telefon zu senden. Der Antwortstatus wird überprüft, und wenn er nicht "OK" ist, wird eine ConnectionError. Diese Fehlermeldung stellt sicher, dass der Log-Alarm-Mechanismus später erneut versucht wird und unsere Haupt-Python-Anwendung nicht unterbrochen wird, da die VonageHttpTransport Klasse in einem Hintergrund-Thread laufen wird.
Beachten Sie, dass wir einige Klassenattribute in der neuen send Methode verwenden: logger_name, vonage_api_key, vonage_api_secret, alert_phone_number. Überschreiben wir die __init__ Methode, um sicherzustellen, dass diese bei der Instanziierung der Klasse richtig gesetzt werden:
class VonageHttpTransport(AsyncHttpTransport):
def __init__(
self,
logger_name: str,
vonage_api_key: str,
vonage_api_secret: str,
alert_phone_number: str,
*args,
**kwargs,
) -> None:
self.logger_name = logger_name
self.vonage_api_key = vonage_api_key
self.vonage_api_secret = vonage_api_secret
self.alert_phone_number = alert_phone_number
super().__init__(*args, **kwargs)
Unsere neue HTTP-Transportklasse ist nun fertig. Bevor wir jedoch mit der eigentlichen Logging-Aktion beginnen, müssen wir zunächst die Logik erstellen, die einen tatsächlichen Logger Objekt unter Verwendung der neuen VonageHttpTransport Klasse.
Vonage Log Handler
Die VonageHttpTransport Klasse sieht gut aus, aber sie kann nicht allein in die Schlacht ziehen. Wir sind nicht in der Lage, mit ihr irgendetwas in unseren Applications zu protokollieren, also gehen wir einen Schritt weiter und machen sie kampftauglich.
Das fehlende Teil in unserem Puzzle ist eine echte HTTP-Handler-Klasse. Diese sollte eine http_logging.AsyncHttpHandlersein, aber sicherlich mit der benutzerdefinierten VonageHttpTransport.
Erstellen wir eine getLogger Funktion innerhalb von logging_vonage.pyum das Verhalten von Python zu imitieren logging.getLogger Verhalten nachzuahmen:
def getLogger(name: str) -> logging.Logger:
pass
Wie die native Python-eigene getLogger Funktion nimmt unsere einen Namensstring als Argument und gibt eine Instanz der logging.Logger Klasse zurück. Als Nächstes werden wir die Funktionalität für diese Funktion Schritt für Schritt aufbauen.
Wir beginnen mit der Instanziierung einer HttpHost. Dies wird nicht wirklich von der VonageHttpTransportbenötigt, da wir HTTP-Anfragen an das Vonage SDK delegieren, aber es ist ein erforderlicher Teil der API-Signatur der http-Logging-Bibliothek:
def getLogger(name: str) -> logging.Logger:
host = HttpHost(name='vonage.com')
Als nächstes brauchen wir ein SupportClass das unser HTTP-Transportobjekt enthält:
support_class = SupportClass(
http_host=host,
_transport=VonageHttpTransport(
http_host=host,
logger_name=name,
vonage_api_key=os.environ.get('VONAGE_API_KEY'),
vonage_api_secret=os.environ.get('VONAGE_API_SECRET'),
alert_phone_number=os.environ.get('ALERT_PHONE_NUMBER'),
),
)Dieses SupportClass Objekt wird dann zur Instanziierung unserer AsyncHttpHandler:
vonage_handler = AsyncHttpHandler(
http_host=host,
support_class=support_class,
)Schließlich instanziieren wir ein logging.Logger Objekt, fügen den vonage_handler als seinen Handler ein und geben es zurück:
logger = logging.getLogger(name)
logger.addHandler(vonage_handler)
return loggerAm Ende sollte unsere getLogger Funktion sollte wie folgt aussehen:
def getLogger(name: str) -> logging.Logger:
host = HttpHost(name='vonage.com')
support_class = SupportClass(
http_host=host,
_transport=VonageHttpTransport(
http_host=host,
logger_name=name,
vonage_api_key=os.environ.get('VONAGE_API_KEY'),
vonage_api_secret=os.environ.get('VONAGE_API_SECRET'),
alert_phone_number=os.environ.get('ALERT_PHONE_NUMBER'),
),
)
vonage_handler = AsyncHttpHandler(
http_host=host,
support_class=support_class,
)
logger = logging.getLogger(name)
logger.addHandler(vonage_handler)
return logger
Beachten Sie, dass der API-Schlüssel, das Geheimnis und die Telefonnummer aus den Umgebungsvariablen abgerufen werden, die wir zu Beginn des Tutorials festgelegt haben. Dies bietet Flexibilität für den Fall, dass wir diesen Code in mehreren Projekten verwenden möchten, und vermeidet außerdem die feste Kodierung von API-Geheimnissen, was normalerweise keine gute Idee ist ;)
Mehrere Handler
Die Logging-Maschinerie von Python ist sehr leistungsfähig, und das logging.Logger Objekt ist flexibel genug, um es mit mehreren Handlern zu erweitern.
Wie oben erläutert, sendet die VonageHttpTransport Klasse aufgrund der inhärenten Textlängenbeschränkung des SMS-Systems nur minimale Informationen über Protokolle senden. Im Falle eines Fehlers, der eine weitere Fehlersuche erfordert, wollen wir natürlich den gesamten Stack-Trace, Informationen darüber, welche Codezeile fehlgeschlagen ist, genaue Zeitstempel usw. erfassen.
Wir können diesen Bedarf an detaillierten Protokollen erfüllen, indem wir die Logger.addHandler und das Hinzufügen eines oder mehrerer zusätzlicher Handler zu dem Vonage Logger Objekt hinzufügen.
Um zum Beispiel Protokolle nicht nur an unser Telefon, sondern auch an die Konsole zu senden, können wir die logging.StreamHandlerverwenden, wie unten gezeigt:
import logging
import logging_vonage
logger = logging_vonage.getLogger('')
logger.addHandler(logging.StreamHandler())Alles, was mit dem oben genannten logger Objekt protokolliert wird, wird auf der Konsole ausgegeben und über die Vonage SMS API an unser Telefon gesendet.
A logging.FileHandler kann verwendet werden, um Protokolle im lokalen Dateisystem zu speichern, wenn dies in einer Implementierung sinnvoll ist. Sie könnten auch das gleiche http_logging.AsyncHttpHandler wieder verwenden, aber in diesem Fall die Protokolle an einen anderen Backend-Host als die Vonage-API senden. Testen mit einer Beispielanwendung Nun ist es an der Zeit, ein paar reale Aktionen mit Schnickschnack zu sehen. Scherz beiseite, wir sind gerade dabei, unsere Telefone mit der Vonage SMS API zum Piepen zu bringen :D
Erstellen Sie eine neue Datei im Projektverzeichnis mit dem Namen sample_app:
Öffnen Sie es und fügen Sie den folgenden Inhalt hinzu:
import logging
import logging_vonage
logger = logging_vonage.getLogger('sampleapp')
logger.addHandler(logging.StreamHandler())
logger.debug('Debugging...')
logger.warning('You\'ve been warned!')
logger.error('This is a test error')
try:
1/0
except ArithmeticError as exc:
logger.exception(exc)Wir instanziieren ein logger Objekt aus dem logging_vonage Modul, das wir zuvor erstellt haben. Die logging.StreamHandler() wird auch verwendet, damit die vollständigen Spuren auf unserer Konsole protokolliert und nicht nur an unser Telefon gesendet werden.
Führen Sie dieses Skript in der Konsole mit aus:
Die folgende Ausgabe sollte auf der Konsole ausgegeben werden:
You've been warned!
This is a test error
division by zero
Traceback (most recent call last):
File "/home/vonage-alerts/sample_app.py", line 14
1/0
ZeroDivisionError: division by zeroWenn Sie alles richtig eingerichtet haben (Vonage Account und API-Schlüssel/Geheimnis), sollten Sie in Kürze eine SMS mit folgendem Text erhalten:
[Python Logger sampleapp] WARNING: You've been warned!, ERROR: This is a test error, ERROR: division by zero
Beachten Sie, dass die Debug-Meldung 'Debugging...' nicht auf der Konsole ausgegeben und auch nicht in die SMS-Nachricht eingefügt wurde. Das liegt daran, dass die Standardprotokollebene in der Python-Protokollierungsbibliothek WARNING. Die DEBUG Ebene wird als niedriger als WARNING und wird daher verworfen.
Wenn Sie möchten, dass die DEBUG Nachricht aufgezeichnet werden soll, stellen Sie den Pegel wie unten gezeigt entsprechend ein:
Führen Sie das sample_app.py Führen Sie das Skript erneut aus, und Sie sollten sehen, dass die Debug-Meldung auf der Konsole ausgegeben und auch in die SMS-Nachricht eingefügt wird.
Beachten Sie, dass trotz unserer logger Abhängigkeit von einem benutzerdefinierten Handler (http_logging.AsyncHttpHandler) und einer benutzerdefinierten Transport (logging_vonage.VonageHttpTransport) Klasse, verhält es sich genau wie jedes andere Python Logger Objekt. Dadurch ist es vollständig kompatibel und kann als Ersatz für ein beliebiges Python-Projekt verwendet werden, falls Sie den SMS-Benachrichtigungsmechanismus, den wir gerade entwickelt haben, in Ihren Stack und in ein zukünftiges Projekt integrieren möchten.
Einpacken
Das war's! Wir verfügen nun über ein einfaches und nicht aufdringliches Python-Warnsystem, mit dem wir die Entwicklung der von uns bereitgestellten Applications im Auge behalten können. Es erweitert die grundlegenden Python logging um dieselbe API, an die wir gewöhnt sind, und läuft überall dort, wo unsere Python-Anwendungen ausgeführt werden. Finanziell gesehen hat es keine festen Kosten und ist relativ günstig in der Wartung (nur Gebühren für SMS-Nachrichten).
Die http-Logging-Bibliothek speichert einen lokalen Zwischenspeicher für die Protokolle, so dass unser Logger im Falle eines Ausfalls der Vonage-API oder des Mobilfunkanbieters oder einer Netzinstabilität den Versand der SMS-Warnungen einige Zeit später wiederholen kann.
