Zap - Golang learning step 6-2
- 公開日
- カテゴリ:Logging
- タグ:Golang,roadmap.sh,学習メモ
roadmap.sh > Go > Logging > Zap の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
- 開発環境
- 参考 URL
- Zap パッケージ
- インストール
- 基本的な使用例
- 開発向けの簡略ロガー (SugaredLogger)
- SugaredLogger と Logger の違い
- カスタム設定
- フィールドの型
- エラーハンドリング
- パフォーマンスの考慮点
- Logger と SugaredLogger の使い分け
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
参考 URL
Zap パッケージ
Zap は、Uber が開発した高速かつ構造化されたロギングを提供する Go 用のロギングライブラリです。
Zap の特徴
- 高パフォーマンス
- メモリアロケーションを最小限に抑えた設計
- JSON 形式のログ出力に最適化
- レベルベースのロギング
- Debug, Info, Warn, Error など一般的なログレベルをサポート
- 構造化ログ
- フィールドベースの柔軟なログ出力
- JSON 形式での出力が容易
- 柔軟な設定
- 開発モード用の「SugaredLogger」と、プロダクションモード用の「Logger」という2つのロガーを提供
インストール
Zap は go get コマンドを使ってインストールします。
go get -u go.uber.org/zap
基本的な使用例
以下のコードは、Zap の基本的な使い方を示しています。
package main
import (
"go.uber.org/zap"
)
func main() {
// デフォルトの Logger を作成
logger, _ := zap.NewProduction() // プロダクション用
defer logger.Sync() // ログをフラッシュしてリソースを解放
// 構造化ログの記録
logger.Info("これは情報レベルのログです",
zap.String("key", "value"),
zap.Int("attempt", 3),
zap.Duration("backoff", 2),
)
// エラーレベルのログ
logger.Error("エラーが発生しました",
zap.String("原因", "無効な入力"),
)
}
出力:
# 構造化ログの記録
{"level":"info","ts":1733832023.54071,"caller":"main.go:16","msg":"これは情報レベルのログです","key":"value","attempt":3,"backoff":0.000000002}
# エラーレベルのログ
{"level":"error","ts":1733832023.5408251,"caller":"main.go:23","msg":"エラーが発生しました","原因":"無効な入力","stacktrace":"main.main01\n\t/path/to/root.main\n\t/path/to/root/main.go:6\nruntime.main\n\t/path/to/root/.goenv/versions/1.23.2/src/runtime/proc.go:272"}
開発向けの簡略ロガー (SugaredLogger)
開発時には、もう少し簡単に使える SugaredLogger が便利です。文字列のフォーマットを使ってログを記録できます。
package main
import (
"go.uber.org/zap"
)
func main() {
// Logger を作成
logger, _ := zap.NewDevelopment()
defer logger.Sync()
// Logger を SugaredLogger に変換
sugar := logger.Sugar()
// フォーマットを使ったログ
sugar.Infof("ユーザー %s が %d 回目の試行に成功しました", "Alice", 3)
// その他の簡略ログ
sugar.Infow("試行成功",
"user", "Alice",
"attempt", 3,
)
}
出力:
2024-12-10T21:13:20.783+0900 INFO main.go:58 ユーザー Alice が 3 回目の試行に成功しました
2024-12-10T21:13:20.783+0900 INFO main.go:61 試行成功 {"user": "Alice", "attempt": 3}
SugaredLogger と Logger の違い
SugaredLogger
:- フォーマット文字列(例: "%s")や非構造化なログをサポート。
- 使いやすいが、構造化ログのパフォーマンスが若干低下する場合がある。
Logger
:- 高性能な構造化ログ用。
- フィールド付きログ(例: zap.String("key", "value"))を使う。
// 通常の Logger
logger, _ := zap.NewProduction()
logger.Info("メッセージ", zap.Int("count", 5))
// SugaredLogger
sugar := logger.Sugar()
sugar.Infof("カウント: %d", 5)
カスタム設定
Logger の動作をカスタマイズする例:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
config := zap.Config{
Level: zap.NewAtomicLevelAt(zap.InfoLevel),
Development: false,
Encoding: "json",
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
MessageKey: "msg",
StacktraceKey: "stacktrace",
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeLevel: zapcore.LowercaseLevelEncoder,
},
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
}
logger, _ := config.Build()
defer logger.Sync()
logger.Info("アプリケーションが起動しました")
}
出力:
{"level":"info","time":"2024-12-10T21:22:01.560+0900","msg":"アプリケーションが起動しました"}
フィールドの型
Zap は様々な型のフィールドをサポートしています。
logger, _ := zap.NewDevelopment()
defer logger.Sync()
logger.Info("詳細情報",
zap.String("name", "テスト"),
zap.Int("age", 25),
zap.Bool("active", true),
zap.Float64("score", 85.5),
zap.Duration("elapsed", time.Second*10),
)
出力:
2024-12-10T21:25:39.371+0900 INFO main.go:81 詳細情報 {"name": "テスト", "age": 25, "active": true, "score": 85.5, "elapsed": "10s"}
エラーハンドリング
エラー情報をログ出力する例
package main
import (
"errors"
"go.uber.org/zap"
)
// エラーを返す処理を模擬するための関数
func doSomething() error {
return errors.New("何かしらのエラーが発生")
}
func someFunction() error {
// Logger の作成(実際のアプリケーションではグローバルに保持することが多い)
logger, err := zap.NewProduction()
if err != nil {
return err
}
defer logger.Sync()
err = doSomething()
if err != nil {
// エラー情報を構造化してログ出力
logger.Error("処理に失敗しました",
zap.Error(err),
zap.String("function", "someFunction"),
)
return err
}
return nil
}
func main() {
if err := someFunction(); err != nil {
// main 関数でのエラーハンドリング
// この例では単純にプログラムを終了させていますが、
// 実際のアプリケーションでは適切なエラーハンドリングを行ってください
return
}
}
このコードを実行すると、以下のような JSON 形式のエラーログが出力されます。
{
"level": "error",
"ts": 1702184400.123456,
"caller": "example/main.go:23",
"msg": "処理に失敗しました",
"error": "何かしらのエラーが発生",
"function": "someFunction"
}
パフォーマンスの考慮点
Logger
は通常アプリケーション起動時に 1 度だけ作成し、グローバル変数や依存性注入で使い回すdefer logger.Sync()
を使用してバッファされたログを確実にフラッシュする- 不要なログレベルは設定で無効化することでパフォーマンスを改善できる
Logger と SugaredLogger の使い分け
Logger と SugaredLogger はそれぞれ特徴があり、使用状況によって適切な選択が異なります。以下のポイントを考慮して選択してください。
Logger を選択すべき場合
- パフォーマンスが重要視される場合
- 高スループットが要求される本番環境
- 大量のログ出力が予想される場合
- メモリ使用量を最小限に抑えたい場合
- 構造化ログが必要な場合
- ログの解析や集計が必要な場合
- ELK スタックなどのログ分析基盤と連携する場合
- JSON 形式での出力が要件の場合
- 型安全性が重要な場合
- コンパイル時の型チェックを活用したい場合
- フィールド名のタイプミスを防ぎたい場合
まとめ
- Zap は高速かつ構造化されたロギングを提供する Go 用のライブラリ
- Logger と SugaredLogger の 2 種類のロガーを提供
- 高性能な構造化ログを JSON 形式で出力可能
- 開発向けの簡易ロガーとして SugaredLogger を利用可能
- 柔軟なカスタマイズが可能な設定オプションを提供
- フィールドベースのログで型安全性を確保
- ログレベルの設定で不要なログを抑制可能
- パフォーマンスと使いやすさを両立した設計
[Next] Step 6-3: Zerolog
[Prev] Step 6-1: log/slog