slog.Handler, slog.Loggerの役割
code:go
// 1. 設定からハンドラーを作成
handler := logger.NewLogger(c.Logger) // slog.Handler
// 2. ハンドラーからロガーを作成
logger := slog.New(handler) // *slog.Logger
// 3. ログ出力
logger.Info("starting server", "port", c.HTTP.Port)
設計パターン
- Handler: ログの出力方式(JSON、テキスト等)と出力先を定義
- Logger: 実際のログ出力APIを提供
- 分離の利点: 出力形式の変更時はNewLoggerのみ修正すれば良い
code:go
なぜ分離されているのか:
1. 出力方式を自由に変更できる
// JSON形式で出力
handler1 := slog.NewJSONHandler(os.Stdout, nil)
// テキスト形式で出力
handler2 := slog.NewTextHandler(os.Stdout, nil)
2. 出力先を自由に変更できる
// 標準出力
slog.NewJSONHandler(os.Stdout, nil)
// ファイル
slog.NewJSONHandler(file, nil)
// ネットワーク
slog.NewJSONHandler(network, nil)
// どちらも同じLoggerで使える
logger1 := slog.New(handler1)
logger2 := slog.New(handler2)
logger.Info()を呼ぶと内部で何が起きるか:
1. Logger → 「port=8080のログを作って」
2. Handler → 「JSON形式で標準出力に書く」
ログの書き方(Logger)と出力方法(Handler)を独立して変更
slog.Loggerがポインタ型である理由
code:go
1. 内部状態を持つ: Logger構造体はHandlerへの参照など内部状態を保持
2. メモリ効率: 大きな構造体をコピーせず、参照で渡す
3. 一貫性: 同じLoggerインスタンスを複数箇所で共有可能
4. Go慣例: 構造体メソッドがレシーバーを変更する場合はポインタ型を使用
4. 実際の使用場面
// 型を明示的に書く場合
var logger *slog.Logger = slog.New(handler)