Zap:Goにおけるログ機能の全貌を引き出す
Min-jun Kim
Dev Intern · Leapcell

概要
Zapは、Uberによって開発された、非常に高速で構造化された、ログレベル対応のGoロギングライブラリです。Uber - go Zapのドキュメントによると、同様の構造化ロギングパッケージよりも優れたパフォーマンスを発揮し、標準ライブラリよりも高速です。具体的なパフォーマンステストはGitHubで確認できます。
GitHubアドレス:https://github.com/uber - go/zap
インスタンスの作成
zap.NewProduction()/zap.NewDevelopment()またはzap.Example()を呼び出してLoggerを作成します。これらの3つのメソッドの違いは、記録される情報にあり、パラメータはstring型のみ可能です。
// コード var log *zap.Logger log = zap.NewExample() log, _ := zap.NewDevelopment() log, _ := zap.NewProduction() log.Debug("これはDEBUGメッセージです") log.Info("これはINFOメッセージです")
// 出力例 {"level":"debug","msg":"This is a DEBUG message"} {"level":"info","msg":"This is an INFO message"}
// 開発環境での出力 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
// 本番環境での出力 {"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}
3つの作成メソッドの比較:
- ExampleとProductionはどちらもJSON形式で出力しますが、Developmentは行ごとの出力形式を使用します。
- Development
- 警告レベル以上からスタックまで出力して追跡します。
- 常にパッケージ/ファイル/行(メソッド)を出力します。
- 行末に追加のフィールドをJSON文字列として追加します。
- レベル名を大文字で出力します。
- タイムスタンプをISO8601形式(ミリ秒)で出力します。
- Production
- デバッグレベルのメッセージはログに記録されません。
- エラーおよびDpanicレベルの記録の場合、ファイルはスタックで追跡されますが、Warnは追跡されません。
- 常に呼び出し元をファイルに追加します。
- 日付をタイムスタンプ形式で出力します。
- レベル名を小文字で出力します。
フォーマットされた出力
Zapには、zap.Loggerとzap.SugaredLoggerの2つの型があります。それらの唯一の違いは、メインロガーの.Sugar()メソッドを呼び出すことでSugaredLoggerを取得し、SugaredLoggerを使用してprintf形式でステートメントを記録できることです。例:
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) }
ファイルへの書き込み
デフォルトでは、ログはアプリケーションのコンソールインターフェイスに出力されます。ただし、簡単にクエリできるように、ログをファイルに書き込むことができます。ただし、以前に述べた3つのインスタンス作成メソッドは使用できなくなります。代わりに、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") // ログファイルの保存ディレクトリ encoderConfig := zap.NewProductionEncoderConfig() // 時間形式の指定 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // エンコーダの取得、NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 log = zap.New(core,zap.AddCaller()) // AddCaller()は、ファイル名と行番号を表示するために使用されます。 log.Info("hello world") log.Error("hello world") }
// ログファイルの出力結果: 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
コンソールとファイルの両方への出力
コンソールとファイルの両方に出力する必要がある場合は、zapcore.NewCoreを変更するだけです。例:
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { // エンコーダの取得、NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 時間形式の指定 encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // ファイルwriteSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // ログファイルの保存ディレクトリ MaxSize: 1, // ファイルサイズの制限、単位MB MaxBackups: 5, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか }) fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 log = zap.New(fileCore, zap.AddCaller()) // AddCaller()は、ファイル名と行番号を表示するために使用されます。 log.Info("hello world") log.Error("hello world") }
ファイル分割
ログファイルは時間の経過とともに大きくなります。ハードディスクの容量を使い果たさないように、特定の条件に従ってログファイルを分割する必要があります。Zapパッケージ自体にはファイル分割機能は提供されていませんが、Zapが推奨するlumberjackパッケージを使用して処理できます。
// ファイルwriteSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // ログファイルの保存ディレクトリ。フォルダが存在しない場合は、自動的に作成されます。 MaxSize: 1, // ファイルサイズの制限、単位MB MaxBackups: 5, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか })
レベル別のファイルへの書き込み
管理担当者のクエリの便宜のために、通常、エラーレベル未満のログをinfo.logに、エラーレベル以上のログをerror.logファイルに入れる必要があります。zapcore.NewCoreメソッドの3番目のパラメータを変更し、ファイルWriteSyncerをinfoとerrorに分割するだけです。例:
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 // エンコーダの取得 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 時間形式の指定 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // レベルに応じて異なる色を表示します。不要な場合は、zapcore.CapitalLevelEncoderを使用します。 //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // ファイルのフルパスを表示します encoder := zapcore.NewConsoleEncoder(encoderConfig) // ログレベル highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // エラーレベル return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // 情報レベルとデバッグレベル、デバッグレベルが最低レベル return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // 情報ファイルwriteSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // ログファイルの保存ディレクトリ。フォルダが存在しない場合は、自動的に作成されます。 MaxSize: 1, // ファイルサイズの制限、単位MB MaxBackups: 5, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 // エラーファイルwriteSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // ログファイルの保存ディレクトリ MaxSize: 1, // ファイルサイズの制限、単位MB MaxBackups: 5, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller()は、ファイル名と行番号を表示するために使用され、省略できます。 log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
このような変更の後、infoレベルとdebugレベルのログはinfo.logに保存され、errorレベルのログは個別にerror.logファイルに保存されます。
レベル別のコンソールでの色の表示
エンコーダのEncodeLevelを指定するだけです。
// エンコーダの取得 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 時間形式の指定 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // レベルに応じて異なる色を表示します。不要な場合は、zapcore.CapitalLevelEncoderを使用します。 encoder := zapcore.NewConsoleEncoder(encoderConfig)
ファイルパスと行番号の表示
前に述べたように、ファイルパスと行番号を表示するには、zap.Newメソッドにパラメータzap.AddCaller()を追加するだけです。フルパスを表示する場合は、エンコーダ構成で指定する必要があります。
// エンコーダの取得 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 時間形式の指定 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // レベルに応じて異なる色を表示します。不要な場合は、zapcore.CapitalLevelEncoderを使用します。 encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // ファイルのフルパスを表示します encoder := zapcore.NewConsoleEncoder(encoderConfig)
完全なコード
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 // エンコーダの取得 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()はJSON形式で出力、NewConsoleEncoder()はプレーンテキスト形式で出力 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 時間形式の指定 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // レベルに応じて異なる色を表示します。不要な場合は、zapcore.CapitalLevelEncoderを使用します。 //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // ファイルのフルパスを表示します encoder := zapcore.NewConsoleEncoder(encoderConfig) // ログレベル highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // エラーレベル return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // 情報レベルとデバッグレベル、デバッグレベルが最低レベル return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // 情報ファイルwriteSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // ログファイルの保存ディレクトリ。フォルダが存在しない場合は、自動的に作成されます。 MaxSize: 2, // ファイルサイズの制限、単位MB MaxBackups: 100, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 // エラーファイルwriteSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // ログファイルの保存ディレクトリ MaxSize: 1, // ファイルサイズの制限、単位MB MaxBackups: 5, // 保持するログファイルの最大数 MaxAge: 30, // ログファイルを保持する日数 Compress: false, // 圧縮するかどうか }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // 3番目以降のパラメータは、ファイルに書き込むログのレベルです。ErrorLevelモードでは、エラーレベルのログのみが記録されます。 coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller()は、ファイル名と行番号を表示するために使用され、省略できます。 log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
Leapcell:Golangアプリのホスティング、非同期タスク、およびRedisに最適なサーバーレスプラットフォーム
最後に、Golangサービスのデプロイに最適なプラットフォームLeapcellをお勧めします。
1. 多言語サポート
- JavaScript、Python、Go、またはRustで開発します。
2. 無制限のプロジェクトを無料でデプロイ
- 使用量に応じてのみ支払い - リクエストも料金も発生しません。
3. 比類のないコスト効率
- アイドル料金なしの従量課金制。
- 例:$25で平均応答時間60msで694万件のリクエストをサポートします。
4. 合理化された開発者エクスペリエンス
- 楽なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察のためのリアルタイムのメトリックとロギング。
5. 簡単なスケーラビリティと高いパフォーマンス
- 高い同時実行性を簡単に処理するための自動スケーリング。
- 運用上のオーバーヘッドゼロ - 構築に集中するだけです。
Leapcell Twitter:https://x.com/LeapcellHQ