Wenn ein Python-Skript lokal noch brav läuft, aber auf dem Server plötzlich „irgendwie komisch“ reagiert, fehlt meist eines: durchdachtes Logging. Statt überall schnell ein print() einzubauen, lohnt es sich, das eingebaute logging-Modul sauber zu nutzen. Das spart Zeit bei der Fehlersuche und macht Anwendungen langfristig wartbar.
Python Logging Grundlagen: Logger, Handler, Formatter
Das Python-Logging-Modul wirkt auf den ersten Blick komplex, ist aber klar strukturiert. Hilfreich ist ein Bild im Kopf, wie die Teile zusammenspielen.
Was macht das logging-Modul eigentlich?
Das Modul logging sammelt Meldungen aus dem Code, versieht sie mit Metadaten (Zeit, Level, Quelle) und leitet sie an Ausgabekanäle weiter. Im Kern gibt es drei wichtige Bausteine:
- Logger: Die „Eingangstür“ im Code, über die Meldungen erzeugt werden, z. B.
logger.info("Benutzer eingeloggt"). - Handler: Leiten Meldungen weiter, z. B. auf die Konsole, in eine Datei oder per HTTP.
- Formatter: Legen das Textformat der Meldungen fest, z. B. mit Zeitstempel und Modulnamen.
Diese Trennung macht Logging flexibel: Ein Logger kann mehrere Handler haben (Konsole und Datei), und jeder Handler kann ein eigenes Format verwenden.
Logging-Level verstehen: DEBUG bis CRITICAL
Logging-Meldungen haben ein Level. Das ist ein grober Schweregrad, der hilft, im Alltag nicht in Details zu ertrinken. Die üblichen Level:
DEBUG: Sehr detaillierte Infos für die Entwicklung.INFO: Normale Statusmeldungen, z. B. „Job gestartet“.WARNING: Unerwartetes, das aber noch nicht kritisch ist.ERROR: Ein Fehler, der eine Aktion scheitern lässt.CRITICAL: Sehr ernste Fehler, z. B. wenn der Dienst insgesamt ausfällt.
Logger und Handler können einen Mindestlevel haben. Wird z. B. WARNING gesetzt, erscheinen keine DEBUG– und INFO-Meldungen mehr. So bleibt der Log im Live-Betrieb übersichtlich.
Warum print() kein gutes Logging ersetzt
print() ist verlockend, weil es schnell ist. Es hat aber Nachteile:
- Kein Level: Es ist nicht zu erkennen, was wichtig ist und was nur Debug-Info.
- Kein Zeitstempel oder Kontext: Ohne Format geht die Einordnung verloren.
- Schwer zu filtern: Auf dem Server lassen sich Meldungen nicht gezielt ein- oder ausblenden.
Mit logging lässt sich dagegen zentral steuern, wie laut eine Anwendung spricht – und wohin.
Einfaches Python Logging konfigurieren
Für viele Skripte reicht eine kompakte Grundkonfiguration, die Konsole, Level und Format festlegt. Diese Basis lässt sich später leicht erweitern.
Minimalbeispiel für logging.basicConfig
Ein typischer Startpunkt in einer kleinen Anwendung:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
logger.info("Anwendung gestartet")
Die wichtigsten Platzhalter im Formatstring:
%(asctime)s: Zeitstempel.%(name)s: Name des Loggers (oft Modulname).%(levelname)s: Level als Text, z. B. INFO.%(message)s: Eigentliche Nachricht.
Mit basicConfig wird ein Standard-Handler eingerichtet, der auf die Konsole schreibt. Für kleine Projekte ist das ausreichend, für größere braucht es eine feinere Aufteilung in Logger und Handler.
Benannte Logger pro Modul nutzen
Statt überall den Root-Logger zu verwenden, lohnt sich ein benannter Logger pro Datei:
logger = logging.getLogger(__name__)
__name__ sorgt dafür, dass jede Moduldatei einen eigenen Namen im Log bekommt. Das hilft, Fehler schnell der richtigen Stelle im Code zuzuordnen.
Logging-Level im Alltag sinnvoll wählen
Eine einfache, praxistaugliche Einteilung:
DEBUGfür innere Details, z. B. Zwischenstände in einer Schleife.INFOfür wichtige Schritte, z. B. „Datenimport gestartet/abgeschlossen“.WARNINGbei ungewöhnlichen, aber noch tolerierbaren Situationen.ERRORbei Ausnahmen, die eine Funktion abbrechen.
Wer sich mit Fehlerszenarien beschäftigt, profitiert zusätzlich von sauberem Error Handling. Ein Blick in saubere Fehlerbehandlung in JavaScript, wie in JavaScript Error Handling – saubere Fehlermeldungen im Frontend erklärt, zeigt ähnliche Prinzipien: klare Meldungen, klare Kontexte.
Logging in Dateien schreiben und rotieren
Spätestens wenn ein Script als Dienst läuft, reicht die Konsole nicht mehr. Logs müssen in Dateien landen, idealerweise mit automatischer Rotation, damit sie nicht endlos wachsen.
FileHandler und RotatingFileHandler einsetzen
Ein eigener Handler für eine Logdatei sieht zum Beispiel so aus:
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("meine_app")
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
"app.log", maxBytes=1_000_000, backupCount=5
)
formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - %(name)s - %(message)s"
)
handler.setFormatter(formatter)
logger.addHandler(handler)
Die wichtigsten Parameter:
maxBytes: Maximale Größe einer Logdatei.backupCount: Wie viele alte Dateien behalten werden.
Ist die Datei voll, wird sie umbenannt und eine neue Datei gestartet. So bleiben Logs überschaubar und fressen nicht unendlich Speicherplatz.
Trennung von Fehler- und Anwendungs-Logs
In vielen Projekten ist es hilfreich, normale Ablaufmeldungen und Fehler getrennt zu protokollieren. Das geht mit zwei Handlern am gleichen Logger:
- Ein Handler schreibt alle
INFOund höher inapp.log. - Ein anderer Handler schreibt nur
ERRORundCRITICALinerrors.log.
So lassen sich Fehler schneller sichten, ohne sich durch alle Routine-Meldungen blättern zu müssen.
Strukturierte Logs für spätere Auswertung
Wer Logs später mit Tools analysieren will, profitiert von einem strukturierten Format wie JSON. Ein Formatter kann z. B. %(asctime)s, %(levelname)s und %(message)s in eine JSON-Zeile verwandeln. Dadurch werden Filter und Suchabfragen auf Log-Management-Plattformen deutlich einfacher.
Logging sauber in den Code integrieren
Gutes Logging ist nicht nur eine Konfigurationsfrage, sondern auch eine Frage des Stils im Code. Überall unüberlegt Logs einzustreuen, überflutet schnell die Dateien.
Was sollte geloggt werden – und was nicht?
Hilfreiche Faustregeln:
- Ereignisse loggen, keine reinen Daten: „Benutzer X hat sich angemeldet“, nicht die komplette Benutzertabelle.
- Grenzen loggen: Start und Ende von wichtigen Jobs, inklusive Dauer.
- Ausnahmen loggen: Fehler mit Traceback sind Gold wert für die Analyse.
- Keine sensiblen Daten loggen: Keine Passwörter, keine vollständigen Kreditkartennummern, möglichst keine Klartext-Personendaten, wenn nicht zwingend nötig.
Gerade in Webanwendungen mit Nutzerkonten ist der Schutz personenbezogener Daten wichtig – auch in Logs.
Exceptions mit logger.exception erfassen
Ein häufiger Fehler ist, Ausnahmen nur als Text zu loggen und den eigentlichen Stacktrace zu verlieren. Besser sieht es so aus:
try:
daten_verarbeiten()
except Exception:
logger.exception("Fehler bei der Datenverarbeitung")
logger.exception entspricht einem ERROR, hängt aber automatisch die Rückverfolgung (Traceback) an. Das spart Zeit bei der Ursachenforschung.
Konfigurierbares Logging mit Umgebungsvariablen
In der Entwicklung sind ausführliche DEBUG-Logs nützlich, im Produktivbetrieb eher nicht. Ein einfacher Ansatz:
- In Entwicklung: Level
DEBUG, Konsole als Hauptausgabe. - In Produktion: Level
INFOoderWARNING, Dateien mit Rotation.
Das lässt sich z. B. über eine Umgebungsvariable LOG_LEVEL steuern, die beim Start der Anwendung gelesen wird. So ist kein Code-Ändern nötig, um die Lautstärke des Logs anzupassen.
Logging mit logging.config: dictConfig und INI-Dateien
Sobald mehrere Module, verschiedene Handler und komplexere Formate im Spiel sind, wird die Konfiguration im Code schnell unübersichtlich. Hier hilft eine zentrale Konfiguration.
dictConfig: Logging-Konfiguration als Python-Dict
Mit logging.config.dictConfig kann die gesamte Struktur in einem Dictionary definiert werden. Das hält Setup-Code schlank und einheitlich:
import logging.config
LOGGING = {
"version": 1,
"formatters": {
"standard": {
"format": "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "standard",
"level": "INFO"
}
},
"loggers": {
"meine_app": {
"handlers": ["console"],
"level": "DEBUG"
}
}
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger("meine_app")
Die Vorteile:
- Struktur ist zentral sichtbar: Welche Logger gibt es? Welche Handler hängen wo?
- Änderungen sind ohne Eingriff in viele Stellen im Code möglich.
- Die Konfiguration kann bei Bedarf aus einer separaten Datei eingelesen werden.
INI-Style mit fileConfig für einfache Szenarien
Alternativ kann logging.config.fileConfig eine INI-Datei lesen. Das ist z. B. dann praktisch, wenn Konfiguration aus Deployment- oder Systemumgebungen kommt. Für moderne Python-Projekte ist dictConfig aber meist flexibler, weil es direkt mit Python-Strukturen arbeitet.
Logging in größeren Anwendungen organisieren
In komplexeren Projekten lohnt es sich, ein gemeinsames Logging-Konzept zu definieren – ähnlich wie bei einem Designsystem im UI-Bereich. So wie Konsistenz im Design mit einem Designsystem für Typografie entsteht, sorgt ein durchdachtes Logging-„System“ für konsistente Meldungen und Level über alle Module hinweg.
So geht’s: Schritt-für-Schritt zu sauberem Python Logging
Zum Abschluss eine kompakte Umsetzungs-Checkliste, die sich gut für neue oder bestehende Projekte eignet.
- Bestehende
print()-Ausgaben identifizieren und durchlogging-Aufrufe ersetzen. - Ein zentrales Setup bauen (z. B.
logging_setup.py) und dort Logger, Handler und Formatter definieren. - Pro Modul einen benannten Logger mit
getLogger(__name__)nutzen. - Levelregeln festlegen: Wann wird
DEBUG, wannINFO, wannWARNINGgenutzt? - Dateilogging mit Rotation einrichten, damit Logdateien nicht endlos wachsen.
- Umgebungsvariablen einführen, um Level zwischen Entwicklung und Produktion zu steuern.
- Bei Exceptions konsequent
logger.exception()verwenden, um Tracebacks im Log zu haben.
Wer dann noch regelmäßig in die Logs schaut und auffällige Meldungen ernst nimmt, baut Schritt für Schritt eine robuste Grundlage für stabile Anwendungen auf – egal ob es sich um ein kleines Script oder einen größeren Webdienst handelt.

