Zap: Erschließt das volle Potenzial der Protokollierung in Go
Min-jun Kim
Dev Intern · Leapcell

Abstrakt
Zap ist eine sehr schnelle, strukturierte und protokollgesteuerte Go-Logging-Bibliothek, die von Uber entwickelt wurde. Laut der Uber - go Zap-Dokumentation ist sie leistungsfähiger als ähnliche strukturierte Logging-Pakete und auch schneller als die Standardbibliothek. Spezifische Leistungstests sind auf GitHub zu finden.
GitHub-Adresse: https://github.com/uber - go/zap
Erstellen einer Instanz
Erstellen Sie einen Logger durch Aufrufen von zap.NewProduction()/zap.NewDevelopment() oder zap.Example(). Der Unterschied zwischen diesen drei Methoden liegt in den Informationen, die sie aufzeichnen, und die Parameter können nur vom Typ String sein.
// Code var log *zap.Logger log = zap.NewExample() log, _ := zap.NewDevelopment() log, _ := zap.NewProduction() log.Debug("This is a DEBUG message") log.Info("This is an INFO message")
// Example Output {"level":"debug","msg":"This is a DEBUG message"} {"level":"info","msg":"This is an INFO message"}
// Development Output 2025-01-28T00:00:00.000+0800 DEBUG development/main.go:7 This is a DEBUG message 2025-01-28T00:00:00.000+0800 INFO development/main.go:8 This is an INFO message
// Production Output {"level":"info","ts":1737907200.0000000,"caller":"production/main.go:8","msg":"This is an INFO message"} {"level":"info","ts":1737907200.0000000,"caller":"production/main.go:9","msg":"This is an INFO message with fields","region":["us-west"],"id":2}
Vergleich der drei Erstellungsmethoden:
- Sowohl Example als auch Production verwenden das JSON-Format für die Ausgabe, während Development eine zeilenweise Ausgabeform verwendet.
- Development
- Gibt ab der Warnstufe aufwärts den Stack zur Verfolgung aus.
- Gibt immer das Paket/die Datei/Zeile (Methode) aus.
- Fügt alle zusätzlichen Felder am Ende der Zeile als JSON-String hinzu.
- Gibt den Level-Namen in Großbuchstaben aus.
- Gibt den Zeitstempel im ISO8601-Format in Millisekunden aus.
- Production
- Debug-Level-Meldungen werden nicht protokolliert.
- Für Error- und Dpanic-Level-Einträge wird die Datei im Stack verfolgt, Warn jedoch nicht.
- Fügt immer den Aufrufer zur Datei hinzu.
- Gibt das Datum im Zeitstempelformat aus.
- Gibt den Level-Namen in Kleinbuchstaben aus.
Formatierte Ausgabe
Zap hat zwei Typen, *zap.Logger und *zap.SugaredLogger. Der einzige Unterschied zwischen ihnen besteht darin, dass wir einen SugaredLogger erhalten können, indem wir die Methode .Sugar() des Hauptloggers aufrufen und dann den SugaredLogger verwenden, um Anweisungen im printf-Format aufzuzeichnen, zum Beispiel:
var sugarLogger *zap.SugaredLogger func InitLogger() { logger, _ := zap.NewProduction() sugarLogger = logger.Sugar() } func main() { InitLogger() defer sugarLogger.Sync() sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err) }
Schreiben in eine Datei
Standardmäßig werden Protokolle auf der Konsolenoberfläche der Anwendung ausgegeben. Für eine einfache Abfrage können Protokolle jedoch in eine Datei geschrieben werden. Wir können jedoch nicht mehr die drei Methoden zum Erstellen von Instanzen verwenden, die zuvor erwähnt wurden. Stattdessen verwenden wir zap.New().
package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { writeSyncer, _ := os.Create("./info.log") // Protokolldatei-Speicherverzeichnis encoderConfig := zap.NewProductionEncoderConfig() // Zeitformat angeben encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // Den Encoder abrufen, NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. log = zap.New(core,zap.AddCaller()) // AddCaller() wird verwendet, um den Dateinamen und die Zeilennummer anzuzeigen. log.Info("hello world") log.Error("hello world") }
// Ausgabe der Protokolldatei: 2025-01-28T00:00:00.000+0800 INFO geth/main.go:18 hello world 2025-01-28T00:00:00.000+0800 ERROR geth/main.go:19 hello world
Ausgabe sowohl in der Konsole als auch in der Datei
Wenn Sie sowohl in der Konsole als auch in der Datei ausgeben müssen, müssen Sie nur zapcore.NewCore ändern. Beispiel:
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { // Den Encoder abrufen, NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Zeitformat angeben encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // File writeSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // Protokolldatei-Speicherverzeichnis MaxSize: 1, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 5, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll }) fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. log = zap.New(fileCore, zap.AddCaller()) // AddCaller() wird verwendet, um den Dateinamen und die Zeilennummer anzuzeigen. log.Info("hello world") log.Error("hello world") }
Dateiaufteilung
Protokolldateien werden im Laufe der Zeit größer. Um zu vermeiden, dass der Festplattenspeicher voll ist, müssen Protokolldateien unter bestimmten Bedingungen aufgeteilt werden. Das Zap-Paket selbst bietet keine Funktion zur Dateiaufteilung, aber es kann mit dem von Zap empfohlenen lumberjack-Paket behandelt werden.
// File writeSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // Protokolldatei-Speicherverzeichnis. Wenn der Ordner nicht vorhanden ist, wird er automatisch erstellt. MaxSize: 1, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 5, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll })
Schreiben in Dateien nach Level
Für die Bequemlichkeit der Abfrage durch das Verwaltungspersonal müssen wir im Allgemeinen Protokolle unterhalb der Fehlerebene in info.log und Protokolle auf der Fehlerebene und darüber in die Datei error.log einfügen. Wir müssen nur den dritten Parameter der zapcore.NewCore-Methode ändern und dann den Datei-WriteSyncer in info und error aufteilen. Beispiel:
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { var coreArr []zapcore.Core // Den Encoder abrufen encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Zeitformat angeben encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // Zeigt verschiedene Farben entsprechend den Leveln an. Wenn nicht benötigt, verwenden Sie zapcore.CapitalLevelEncoder. //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // Zeigt den vollständigen Dateipfad an encoder := zapcore.NewConsoleEncoder(encoderConfig) // Protokollebenen highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // Fehlerebene return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // Info- und Debug-Level, Debug-Level ist der niedrigste return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // Info file writeSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // Protokolldatei-Speicherverzeichnis. Wenn der Ordner nicht vorhanden ist, wird er automatisch erstellt. MaxSize: 1, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 5, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. // Error file writeSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // Protokolldatei-Speicherverzeichnis MaxSize: 1, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 5, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller() wird verwendet, um den Dateinamen und die Zeilennummer anzuzeigen und kann weggelassen werden. log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
Nach einer solchen Änderung werden Info- und Debug-Level-Protokolle in info.log gespeichert, und Protokolle der Fehlerebene werden separat in der Datei error.log gespeichert.
Anzeigen von Farben in der Konsole nach Level
Geben Sie einfach den EncodeLevel des Encoders an.
// Den Encoder abrufen encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Zeitformat angeben encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // Zeigt verschiedene Farben entsprechend den Leveln an. Wenn nicht benötigt, verwenden Sie zapcore.CapitalLevelEncoder. encoder := zapcore.NewConsoleEncoder(encoderConfig)
Anzeigen von Dateipfad und Zeilennummer
Wie bereits erwähnt, fügen Sie einfach den Parameter zap.AddCaller() zur zap.New-Methode hinzu, um den Dateipfad und die Zeilennummer anzuzeigen. Wenn Sie den vollständigen Pfad anzeigen möchten, müssen Sie ihn in der Encoder-Konfiguration angeben.
// Den Encoder abrufen encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Zeitformat angeben encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // Zeigt verschiedene Farben entsprechend den Leveln an. Wenn nicht benötigt, verwenden Sie zapcore.CapitalLevelEncoder. encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // Zeigt den vollständigen Dateipfad an encoder := zapcore.NewConsoleEncoder(encoderConfig)
Vollständiger Code
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { var coreArr []zapcore.Core // Den Encoder abrufen encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder() gibt im JSON-Format aus, NewConsoleEncoder() gibt im Klartextformat aus encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Zeitformat angeben encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // Zeigt verschiedene Farben entsprechend den Leveln an. Wenn nicht benötigt, verwenden Sie zapcore.CapitalLevelEncoder. //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // Zeigt den vollständigen Dateipfad an encoder := zapcore.NewConsoleEncoder(encoderConfig) // Protokollebenen highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // Fehlerebene return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // Info- und Debug-Level, Debug-Level ist der niedrigste return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // Info file writeSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // Protokolldatei-Speicherverzeichnis. Wenn der Ordner nicht vorhanden ist, wird er automatisch erstellt. MaxSize: 2, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 100, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. // Error file writeSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // Protokolldatei-Speicherverzeichnis MaxSize: 1, // Dateigrößenbeschränkung, Einheit MB MaxBackups: 5, // Maximale Anzahl der beibehaltenen Protokolldateien MaxAge: 30, // Anzahl der Tage, die Protokolldateien aufbewahrt werden sollen Compress: false, // Ob komprimiert werden soll }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // Der dritte und die folgenden Parameter sind die Protokollebenen für das Schreiben in die Datei. Im ErrorLevel-Modus werden nur Protokolle der Fehlerebene aufgezeichnet. coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller() wird verwendet, um den Dateinamen und die Zeilennummer anzuzeigen und kann weggelassen werden. log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
Leapcell: Die beste Serverless-Plattform für das Hosten von Golang-Apps, asynchrone Aufgaben und Redis
Abschließend möchte ich die beste Plattform für die Bereitstellung von Golang-Diensten empfehlen: Leapcell
1. Mehrsprachige Unterstützung
- Entwickeln Sie mit JavaScript, Python, Go oder Rust.
2. Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
3. Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 $ unterstützen 6,94 Millionen Anfragen mit einer durchschnittlichen Antwortzeit von 60 ms.
4. Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
5. Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Null Betriebsaufwand – konzentrieren Sie sich einfach auf das Bauen.
Entdecken Sie mehr in der Dokumentation!
Leapcell Twitter: https://x.com/LeapcellHQ