コア概念
DD のコア概念を理解することは、本ライブラリを効率的に使用するための基礎となります。本章では Logger 体系、フィールドシステム、処理パイプライン、インターフェース階層について説明します。
Logger 体系
DD のログ記録は 3 つのコアタイプを中心に展開します:
Logger(ロガー)
│
├── 直接使用 → logger.Info("message")
│
└── WithFields() → LoggerEntry(プリセットフィールド付き Entry)
│
└── entry.Info("message") // プリセットフィールドを自動付与Logger
Logger はコアとなるロガーで、dd.New() で作成します:
logger, err := dd.New(dd.DefaultConfig())
if err != nil {
log.Fatal(err)
}
defer logger.Close()
logger.Info("サービス起動")
logger.InfoWith("リクエスト処理",
dd.String("method", "GET"),
dd.Int("status", 200),
)各 Logger は独立した設定、出力先、セキュリティフィルター、ライフサイクルを持ち、異なるモジュール間で安全に共有できます。
LoggerEntry
LoggerEntry は WithFields() で作成される、不変のプリセットフィールドコンテナです:
// プリセットフィールド付き Entry を作成
requestLog := logger.WithFields(
dd.String("service", "user-api"),
dd.String("version", "2.1.0"),
)
// 各呼び出しでプリセットフィールドが自動付与される
requestLog.Info("サービス起動")
// 出力:... サービス起動 service=user-api version=2.1.0
requestLog.InfoWith("ユーザーログイン",
dd.String("user", "alice"),
)
// 出力:... ユーザーログイン service=user-api version=2.1.0 user=alice不変設計
WithFields() を呼び出すたびに新しい LoggerEntry が作成され、元の Entry は影響を受けません。つまり、異なる goroutine で安全に同じ Entry を再利用できます。
グローバルロガー
DD はグローバルロガーを提供し、シンプルなシナリオや迅速なプロトタイピングに適しています:
// パッケージレベル関数を直接使用(グローバル Logger 経由)
dd.Info("グローバルログ")
// 以下と同等
dd.Default().Info("グローバルログ")フィールドシステム
Field 型
Field は構造化ログの基本単位で、キーと値のペアで構成されます:
// フィールドコンストラクタは全ての一般的な型をカバー
dd.String("method", "GET") // 文字列
dd.Int("status", 200) // 整数
dd.Float64("latency", 0.123) // 浮動小数点
dd.Bool("success", true) // 真偽値
dd.Duration("elapsed", 150*time.Millisecond) // 所要時間
dd.Time("timestamp", time.Now()) // タイムスタンプ
dd.Err(err) // エラー(key は "error")
dd.ErrWithKey("db_error", err) // エラー(カスタム key)
dd.Any("data", payload) // 任意の型フィールドのチェーン渡し
フィールドは Logger、Entry の間で階層的に渡すことができます:
// 第 1 層:サービスレベルフィールド
serviceLog := logger.WithFields(
dd.String("service", "api-gateway"),
)
// 第 2 層:リクエストレベルフィールド(サービスレベルに追加)
requestLog := serviceLog.WithFields(
dd.String("request_id", "req-001"),
dd.String("path", "/api/users"),
)
// 第 3 層:実際のログ(さらにフィールドを追加)
requestLog.InfoWith("処理完了",
dd.Int("status", 200),
dd.Duration("elapsed", 50*time.Millisecond),
)
// 出力に含まれる:service=api-gateway request_id=req-001 path=/api/users status=200 elapsed=50msログ処理パイプライン
各ログは以下の処理フローを経ます:
ユーザーが logger.InfoWith("msg", fields...) を呼び出し
│
▼
① レベルチェック ─── レベルが無効 → 即座に返却(オーバーヘッドなし)
│
▼
② セキュリティフィルタリング ─── メッセージとフィールドの機密データ → [REDACTED]
│
▼
③ コンテキスト抽出 ── 登録済みエクストラクタで静的/グローバルフィールドを付与(context.Background() で呼び出され、リクエストスコープの TraceID は読み取れない)
│
▼
④ BeforeLog フック
│
▼
⑤ フォーマット ──── テキストフォーマット または JSON フォーマット
│
▼
⑥ セキュリティサイズ制限 ─── Security.MaxMessageSize を超えると切り詰め(0 は制限なし)
│
▼
⑦ 書き込み ────── 1 つ以上の Writer に出力
│
▼
⑧ AfterLog フック
│
▼
⑨ Fatal 処理 ── LevelFatal のみ:非同期で Logger を Close し(最長 5s 待機、OnClose フックを発火し writer を flush)、その後 os.Exit(1) またはカスタム FatalHandler を呼び出しパフォーマンス設計
レベルチェック(ステップ ①)はアトミック操作を使用し、ロック不要でほぼゼロオーバーヘッドです。セキュリティフィルタリング(ステップ ②)はタイムアウト保護付きで、長時間のメインフロー阻断を防止します(大きい入力は goroutine + タイムアウトで最悪約 50ms 以内に返すことを保証)。Fatal 処理(ステップ ⑨)は非同期で Logger の Close(flush と OnClose フックを含む)を発火し、最長 5s 待機します;ユーザーの main の defer は依然実行されませんが、Logger 自体の Close は呼び出されます。
インターフェース階層
DD は 4 つのインターフェースを定義し、精密な依存性注入をサポートします:
CoreLogger ← 基本ログ:Debug/Info/Warn/Error/Fatal + WithFields
│
├── LevelLogger ← レベル管理:GetLevel/SetLevel/IsLevelEnabled(CoreLogger を埋め込み)
│
└── ConfigurableLogger ← 設定管理:Writer/セキュリティ/コンテキスト/フック(CoreLogger を埋め込み)
LogProvider ← 完全機能:全メソッドを含む独立したフラットインターフェース// 基本ログだけでいい?CoreLogger を注入
type Service struct {
log dd.CoreLogger
}
// レベルを動的に調整したい?LevelLogger を注入
type Handler struct {
log dd.LevelLogger
}ベストプラクティス
コンストラクタで最小限必要なインターフェースを受け取るようにしてください。具体的な型ではなくインターフェースを受け取ることで、コードがテストしやすく、柔軟になります。
スレッドセーフモデル
DD のコア設計原則:複数 goroutine で安全に使用可能、追加の同期は不要。
| コンポーネント | 安全メカニズム |
|---|---|
| Logger | すべてのメソッドが安全に並行呼び出し可能 |
| LoggerEntry | 不変、作成後は読み取り専用 |
| Config | Clone() メソッドで安全なコピー |
| Writers | アトミックポインタ、ロックフリー読み取り |
| SensitiveDataFilter | 読み書き分離、独立した goroutine |
| HookRegistry | RWMutex で登録と読み取りを保護(Logger は atomic.Value でそのポインタを保持) |
// 安全:複数の goroutine が同じ Logger を共有
var logger *dd.Logger // 一度だけ初期化
func handleRequest(w http.ResponseWriter, r *http.Request) {
// 安全:並行呼び出し
logger.InfoWith("リクエスト到着",
dd.String("path", r.URL.Path),
dd.String("method", r.Method),
)
}出力先体系
DD は 3 種類の出力先をサポートし、自由に組み合わせ可能:
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{
dd.ConsoleOutput(), // コンソール
dd.FileOutput("logs/app.log"), // ファイル(自動ローテーション)
dd.CustomOutput(customWriter), // カスタム io.Writer
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()組み込み Writer コンポーネント:
| コンポーネント | 用途 |
|---|---|
FileWriter | ファイル書き込み + サイズ/時間ローテーション + 圧縮 |
BufferedWriter | バッファ書き込み、I/O 回数を削減 |
MultiWriter | マルチ出力先ディスパッチ、複数の Writer に書き込み |
次のステップ
- 構造化ログ -- フィールドの使い方詳細
- ファイル出力とローテーション -- ファイルログ設定
- 機密データフィルタリング -- セキュリティフィルタリング実践
- API リファレンス -- 完全 API ドキュメント