Ich kann wetten, dass quick jeder Python-Entwickler manchmal „Drucken“ zum Debuggen verwendet. Beim Prototyping ist daran nichts auszusetzen, aber für die Produktion gibt es viel effektivere Möglichkeiten, mit den Protokollen umzugehen. In diesem Artikel zeige ich fünf praktische Gründe, warum Python-Logging viel flexibler und leistungsfähiger ist und warum Sie es unbedingt verwenden sollten, wenn Sie noch nicht damit begonnen haben.
Lasst uns darauf eingehen.
Code
Um die Sache praktischer zu gestalten, betrachten wir ein Spielzeugbeispiel. Ich habe eine kleine Anwendung erstellt, die eine lineare Regression für zwei Python-Pay attention berechnet:
import numpy as np
from sklearn.linear_model import LinearRegression
from typing import Checklist, Non-compulsorydef do_regression(arr_x: Checklist, arr_y: Checklist) -> Non-compulsory[List]:
""" LinearRegression for X and Y lists """
attempt:
x_in = np.array(arr_x).reshape(-1, 1)
y_in = np.array(arr_y).reshape(-1, 1)
print(f"X: {x_in}")
print(f"y: {y_in}")
reg = LinearRegression().match(x_in, y_in)
out = reg.predict(x_in)
print(f"Out: {out}")
print(f"Rating: {reg.rating(x_in, arr_y)}")
print(f"Coef: {reg.coef_}")
return out.reshape(-1).tolist()
besides ValueError as err:
print(f"ValueError: {err}")
return None
if __name__ == "__main__":
print("App began")
ret = do_regression([1,2,3,4], [5,6,7,8])
print(f"LinearRegression outcome: {ret}")
Dieser Code funktioniert, aber können wir ihn besser machen? Das können wir natürlich. Sehen wir uns fünf Vorteile der Verwendung von „ anProtokollierung“ anstatt „drucken“ in diesem Code.
1. Protokollierungsstufen
Ändern wir unseren Code ein wenig:
import loggingdef do_regression(arr_x: Checklist, arr_y: Checklist) -> Non-compulsory[List]:
"""LinearRegression for X and Y Python lists"""
attempt:
x_in = np.array(arr_x).reshape(-1, 1)
y_in = np.array(arr_y).reshape(-1, 1)
logging.debug(f"X: {x_in}")
...
besides ValueError as err:
logging.error(f"ValueError: {err}")
return None
if __name__ == "__main__":
logging.basicConfig(stage=logging.DEBUG, format='%(message)s')
logging.information("App began")
ret = do_regression([1,2,3,4], [5,6,7,8])
logging.information(f"LinearRegression outcome: {ret}")
Hier habe ich „Print“-Aufrufe durch „Logging“-Aufrufe ersetzt. Wir haben eine kleine Änderung vorgenommen, die die Ausgabe jedoch viel flexibler macht. Über den Parameter „Stage“ können wir nun verschiedene einstellen Protokollierungsstufen. Wenn wir zum Beispiel „stage=logging.DEBUG“, dann sind alle Ausgaben sichtbar. Wenn wir sicher sind, dass unser Code für die Produktion bereit ist, können wir die Ebene auf „logging.INFO“und Debugging-Meldungen werden nicht mehr angezeigt:
Und was wichtig ist, ist, dass außer der Initialisierung der Protokollierung selbst keine Codeänderung erforderlich ist!
Alle verfügbaren Konstanten finden Sie übrigens im logging/__init__.py Datei:
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0
Wie wir sehen können, ist die Stufe „FEHLER“ die höchste; Durch die Aktivierung der Protokollebene „ERROR“ können wir alle anderen Meldungen unterdrücken und es werden nur Fehler angezeigt.
2. Formatierung
Wie wir auf dem letzten Screenshot sehen können, ist es einfach, die Protokollierungsausgabe zu steuern. Aber wir können noch viel mehr tun, um das zu verbessern. Wir können die Ausgabe auch anpassen, indem wir „Format“ Zeichenfolge. Beispielsweise kann ich die Formatierung wie folgt festlegen:
logging.basicConfig(stage=logging.DEBUG,
format='[%(asctime)s] %(filename)s:%(lineno)d: %(message)s')
Ohne weitere Codeänderungen kann ich Zeitstempel, Dateinamen und sogar die Zeilennummern in der Ausgabe sehen:
Es stehen etwa 20 verschiedene Parameter zur Verfügung, die im Abschnitt „LogRecord-Attribute” Absatz des Handbuchs.
3. Protokolle in einer Datei speichern
Die Python-Protokollierung ist ein sehr flexibles Modul, dessen Funktionalität leicht erweitert werden kann. Nehmen wir an, wir möchten alle unsere Protokolle zur späteren Analyse in einer Datei speichern. Dazu müssen wir nur zwei Codezeilen hinzufügen:
logging.basicConfig(stage=logging.DEBUG,
format='[%(asctime)s] %(message)s',
handlers=[logging.FileHandler("debug.log"),
logging.StreamHandler()])
Wie wir sehen können, habe ich einen neuen Parameter „Handler“ hinzugefügt. A StreamHandler zeigt das Protokoll auf der Konsole an und die FileHandlerwie wir aus dem Namen erraten können, speichert die gleiche Ausgabe in der Datei.
Dieses System ist wirklich flexibel. In Python sind viele verschiedene „Handler“-Objekte verfügbar, und ich ermutige die Leser dazu Schauen Sie im Handbuch nach alleine. Und wie wir bereits wissen, funktioniert die Protokollierung quick automatisch; Es sind keine weiteren Codeänderungen erforderlich.
4. Rotierende Protokolldateien
Das Speichern von Protokollen in einer Datei ist eine gute Possibility, aber leider ist der Speicherplatz nicht unbegrenzt. Wir können dieses Downside leicht lösen, indem wir die rotierende Protokolldatei verwenden:
from logging.handlers import TimedRotatingFileHandler...
if __name__ == "__main__":
file_handler = TimedRotatingFileHandler(
filename="debug.log",
when="midnight",
interval=1,
backupCount=3,
)
logging.basicConfig(stage=logging.DEBUG,
format='[%(asctime)s] %(message)s',
handlers=[file_handler, logging.StreamHandler()])
Alle Parameter sind selbsterklärend. A TimedRotatingFileHandler Das Objekt erstellt eine Protokolldatei, die jede Mitternacht geändert wird, und nur die letzten drei Protokolldateien werden gespeichert. Die vorherigen Dateien werden automatisch in etwa „debug.log.2023.03.03“ umbenannt und nach einem dreitägigen Intervall gelöscht.
5. Protokolle über Socket senden
Die Protokollierung von Python ist überraschend flexibel. Wenn wir Protokolle nicht in einer lokalen Datei speichern möchten, können wir einfach einen Socket-Handler hinzufügen, der Protokolle über eine bestimmte IP und einen bestimmten Port an einen anderen Dienst sendet:
from logging.handlers import SocketHandlerlogging.basicConfig(stage=logging.DEBUG, format='[%(asctime)s] %(message)s',
handlers=[SocketHandler(host="127.0.0.1", port=15001),
logging.StreamHandler()])
Das ist es; Es sind keine weiteren Codeänderungen erforderlich!
Wir können auch eine andere Anwendung erstellen, die denselben Port überwacht:
import socket
import logging
import pickle
import struct
from logging import LogRecordport = 15001
stream_handler = logging.StreamHandler()
def create_socket() -> socket.socket:
"""Create the socket"""
sock = socket.socket(socket.AF_INET)
sock.settimeout(30.0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return sock
def read_socket_data(conn_in: socket.socket):
"""Learn knowledge from socket"""
whereas True:
knowledge = conn_in.recv(4) # Information: 4 bytes size + physique
if len(knowledge) > 0:
body_len = struct.unpack(">L", knowledge)[0]
knowledge = conn_in.recv(body_len)
file: LogRecord = logging.makeLogRecord(pickle.masses(knowledge))
stream_handler.emit(file)
else:
logging.debug("Socket connection misplaced")
return
if __name__ == "__main__":
logging.basicConfig(stage=logging.DEBUG, format='[%(asctime)s] %(message)s',
handlers=[stream_handler])
sock = create_socket()
sock.bind(("127.0.0.1", port)) # Native connections solely
sock.hear(1) # One shopper will be linked
logging.debug("Logs listening thread began")
whereas True:
attempt:
conn, _ = sock.settle for()
logging.debug("Socket connection established")
read_socket_data(conn)
besides socket.timeout:
logging.debug("Socket listening: no knowledge")
Der schwierige Teil hier ist die Verwendung von emittieren Methode, die alle von einem Socket empfangenen Distant-Daten einem aktiven StreamHandler hinzufügt.
6. Bonus: Protokollfilter
Zum Schluss noch ein kleiner Bonus für Leser, die bis zu diesem Teil aufmerksam genug waren, um zu lesen. Es ist auch einfach, benutzerdefinierte Filter zu Protokollen hinzuzufügen. Nehmen wir an, wir möchten für zukünftige Analysen nur X- und Y-Werte in der Datei protokollieren. Es ist einfach, eine neue Filterklasse zu erstellen, die nur Zeichenfolgen im Protokoll speichert, die „x:“- oder „y:“-Datensätze enthalten:
from logging import LogRecord, Filterclass DataFilter(Filter):
"""Filter for logging messages"""
def filter(self, file: LogRecord) -> bool:
"""Save solely filtered knowledge"""
return "x:" in file.msg.decrease() or "y:" in file.msg.decrease()
Dann können wir diesen Filter einfach zum Dateiprotokoll hinzufügen. Unsere Konsolenausgabe bleibt erhalten, aber die Datei enthält nur die Werte „x:“ und „y:“.
file_handler = logging.FileHandler("debug.log")
file_handler.addFilter(DataFilter())logging.basicConfig(stage=logging.DEBUG,
format='[%(asctime)s] %(message)s',
handlers=[file_handler, logging.StreamHandler()])
Abschluss
In diesem kurzen Artikel haben wir mehrere einfache Möglichkeiten kennengelernt, Protokolle in die Python-Anwendung zu integrieren. Die Anmeldung in Python ist ein sehr flexibles Framework, und es lohnt sich auf jeden Fall, etwas Zeit damit zu verbringen, seine Funktionsweise zu untersuchen.
Vielen Dank fürs Lesen und viel Glück bei zukünftigen Experimenten.
Wenn Ihnen diese Geschichte gefallen hat, zögern Sie nicht zu abonnieren auf Medium, und Sie erhalten Benachrichtigungen, wenn meine neuen Artikel veröffentlicht werden, sowie vollen Zugriff auf Tausende von Geschichten anderer Autoren.