Logging in Go-Anwendungen: Best Practices für Anfänger
Logging ist nicht einfach nur das Schreiben von Nachrichten in die Konsole. In der Produktion helfen Logs dabei, Fehler zu verfolgen, die Leistung zu analysieren und das Systemverhalten zu verstehen. In diesem Artikel behandeln wir die besten Praktiken für das Logging in Go (Golang) für Anfänger: von der Standardbibliothek bis hin zu modernen strukturierten Ansätzen.
Warum ist Logging in Go wichtig?
Go ist eine Sprache für die Entwicklung hochbelasteter Serveranwendungen. Ohne qualitativ hochwertiges Logging können Sie nicht:
- Bugs in der Produktionsumgebung schnell lokalisieren
- Die Ereignisabfolge bei einem Fehler verstehen
- Ein Sicherheitsaudit durchführen
- Die Leistung optimieren
Gutes Logging sollte sein: strukturiert, mit Leveln (debug, info, warn, error), kontextbezogen (mit Anfrage-ID, Funktionsname) und verwaltbar (Rotation, Ausgabe in Datei).
1. Beginnen mit der Standardbibliothek log
Go bietet ein integriertes Paket log – einfach und ausreichend für kleine Projekte. So funktioniert es:
package main
import ( "log" "os")
func main() { // Basiskonfiguration: Ausgabe nach stderr log.SetOutput(os.Stderr) log.SetPrefix("INFO: ") log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("Anwendung gestartet") log.Printf("Benutzer %s authentifiziert", "admin") log.Fatal("Kritischer Fehler") // beendet das Programm mit Code 1}Wichtige Funktionen von log:
log.Println()– Ausgabe einer Zeilelog.Printf()– formatierte Ausgabelog.Fatal()– Ausgabe + Programmendelog.Panic()– Ausgabe + Panik
Aber der Standard-Logger hat Nachteile: keine Level (debug, info, error), keine strukturierte Ausgabe (JSON), keine Dateirotation.
2. Log-Level hinzufügen
Für Produktionsanwendungen ist es entscheidend, Logs nach Leveln zu trennen. Hier ist eine minimale Implementierung mit dem Standardpaket:
package main
import ( "log" "os")
type Logger struct { debug *log.Logger info *log.Logger warn *log.Logger error *log.Logger}
func NewLogger() *Logger { flags := log.Ldate | log.Ltime | log.Lshortfile return &Logger{ debug: log.New(os.Stdout, "DEBUG: ", flags), info: log.New(os.Stdout, "INFO: ", flags), warn: log.New(os.Stderr, "WARN: ", flags), error: log.New(os.Stderr, "ERROR: ", flags), }}
func (l *Logger) Debug(msg string, args ...interface{}) { l.debug.Printf(msg, args...)}
func (l *Logger) Info(msg string, args ...interface{}) { l.info.Printf(msg, args...)}
func (l *Logger) Error(msg string, args ...interface{}) { l.error.Printf(msg, args...)}
func main() { log := NewLogger() log.Info("Server gestartet auf Port %d", 8080) log.Debug("Anfrage von %s", "192.168.1.1") log.Error("Verbindung zur DB fehlgeschlagen: %v", "timeout")}Tipp: Verwenden Sie in der Produktion os.Stderr für Fehler und os.Stdout für normale Logs – das vereinfacht die Log-Sammlung über systemd oder Docker.
3. Strukturiertes Logging (JSON)
In modernen Systemen werden Logs oft in zentralen Speichern (Elasticsearch, Loki) gesammelt. Dafür ist eine strukturierte Ausgabe, z.B. im JSON-Format, erforderlich. Beliebte Bibliotheken:
- logrus – flexibel, mit Unterstützung für Formatierer
- zap – extrem schnell, von Uber
- zerolog – leichtgewichtig und schnell
Beispiel mit logrus:
package main
import ( "os" "gi