構造化フィールド
DD は 20 種の型安全なフィールドコンストラクタ、統一された Field 型、およびオプションのフィールドキー検証メカニズムを提供し、構造化ログ出力に使用します。
Field 型
Field は構造化ログフィールド型で、internal.Field の型エイリアスとして外部に公開されます:
type Field = internal.Field
// 実際の構造(internal/fields.go)
type Field struct {
Key string // フィールドキー
Value any // フィールド値(任意の型)
}すべてのフィールドコンストラクタは Field 値を返します。フォーマッタ(internal.FormatFields)は Key=Value 形式で出力します。基本型(string / 数値 / bool / time.Duration / time.Time / nil)は高速パスを通り、スライス、配列、map、struct などの「複雑型」は JSON シリアライズにフォールバックします(internal.IsComplexValue で判定)、その他の型(fmt.Stringer や error インターフェースを実装する値など)は fmt.Fprint を通ります。
基本フィールド
| コンストラクタ | シグネチャ | 説明 |
|---|---|---|
Any | (key string, value any) Field | 任意の型 |
String | (key, value string) Field | 文字列 |
Bool | (key string, value bool) Field | 真偽値 |
Err | (err error) Field | エラー(key は "error" 固定;err == nil の場合 Value は nil、それ以外は err.Error()) |
ErrWithKey | (key string, err error) Field | カスタム key のエラー(Err と同じく err == nil の場合 Value は nil) |
ErrWithStack | (err error) Field | コールスタック付きエラー(key は "error"、err == nil の場合 Value は nil;スタックフレームは runtime/ と dd パッケージ内のフレームをフィルタリング、キャプチャにわずかなオーバーヘッドあり) |
数値フィールド
| コンストラクタ | 型 | 例 |
|---|---|---|
Int | int | dd.Int("count", 42) |
Int8 | int8 | dd.Int8("flags", 1) |
Int16 | int16 | dd.Int16("port", 8080) |
Int32 | int32 | dd.Int32("code", 200) |
Int64 | int64 | dd.Int64("id", 123456789) |
Uint | uint | dd.Uint("size", 1024) |
Uint8 | uint8 | dd.Uint8("level", 3) |
Uint16 | uint16 | dd.Uint16("year", 2026) |
Uint32 | uint32 | dd.Uint32("seq", 1000) |
Uint64 | uint64 | dd.Uint64("hash", 0xABCD) |
Float32 | float32 | dd.Float32("rate", 0.95) |
Float64 | float64 | dd.Float64("elapsed", 1.234) |
時間フィールド
| コンストラクタ | シグネチャ | 説明 |
|---|---|---|
Time | (key string, value time.Time) Field | タイムスタンプ(RFC3339 フォーマット) |
Duration | (key string, value time.Duration) Field | 所要時間(Duration.String() を呼び出し) |
エラーフィールド
// 標準エラーフィールド(key は "error" 固定、nil error → Value は nil)
dd.Err(err)
// カスタム key
dd.ErrWithKey("db_error", err)
// スタック情報付き(スタックフレームは runtime/ と dd 自身のフレームを除外)
dd.ErrWithStack(err)使用方法
InfoWith との組み合わせ
dd.InfoWith("ユーザーログイン",
dd.String("username", "admin"),
dd.Time("login_at", time.Now()),
dd.Bool("mfa", true),
dd.String("ip", "192.168.1.1"),
)WithFields チェーン呼び出し
entry := logger.WithFields(
dd.String("service", "api"),
dd.Int("pid", os.Getpid()),
)
entry.Info("サービス起動")Entry への追加
base := logger.WithFields(dd.String("req_id", id))
base.InfoWith("レスポンス",
dd.Int("status", 200),
dd.Duration("elapsed", took),
dd.Err(err),
)フィールド検証
DD はフィールドキー検証メカニズムを提供し、命名規則チェックとセキュリティ検証(Log4Shell インジェクション、ホモグラフ攻撃、overlong UTF-8)をサポートします。検証設定 FieldValidationConfig は Config.FieldValidation に設定して構築時に有効化するか、実行時に Logger.SetFieldValidation で動的に置き換えできます。*With の呼び出しごとに各フィールドの Key に対して ValidateFieldKey を呼び出し、Strict モードでは失敗時にログ形式でエラーを報告します(ログメソッド自体は error を返しません)。
FieldValidationMode
検証モード。検証失敗時の処理方法を決定します。
type FieldValidationMode int
const (
FieldValidationNone FieldValidationMode = iota // 検証を無効化(デフォルト、すべてのチェックをショートサーキット)
FieldValidationWarn // 命名不一致時に warning ログを 1 件記録
FieldValidationStrict // 命名不一致時に error ログを 1 件記録
)FieldValidationMode の String() メソッドは "none" / "warn" / "strict" を返します(未知の値は "unknown" を返します)。
FieldNamingConvention
命名規則。
type FieldNamingConvention int
const (
NamingConventionAny FieldNamingConvention = iota // 任意の有効なキーを受け付け(デフォルト)
NamingConventionSnakeCase // snake_case:user_id
NamingConventionCamelCase // camelCase:userId
NamingConventionPascalCase // PascalCase:UserId
NamingConventionKebabCase // kebab-case:user-id
)FieldNamingConvention の String() メソッドは "any" / "snake_case" / "camelCase" / "PascalCase" / "kebab-case" を返します(未知の値は "unknown" を返します)。
FieldValidationConfig
フィールド検証設定。
type FieldValidationConfig struct {
Mode FieldValidationMode // 検証モード
Convention FieldNamingConvention // 命名規則
AllowCommonAbbreviations bool // 一般的な略語を許可(ID、URL、HTTP、JSON など)
EnableSecurityValidation bool // セキュリティ検証を有効化(Log4Shell / ホモグラフ / overlong UTF-8)
}ゼロ値の落とし穴
リテラル FieldValidationConfig{} は EnableSecurityValidation=false となり、セキュリティ検証を暗黙に無効化します——DefaultFieldValidationConfig コンストラクタの使用を優先してください(同コンストラクタはこの項目を true に設定します)。また、Mode == FieldValidationNone の場合はセキュリティ検証の前にショートサーキットするため、EnableSecurityValidation を有効化していても実行されません。
プリセット設定
// デフォルト設定:命名検証を無効化、セキュリティ検証を有効化
func DefaultFieldValidationConfig() *FieldValidationConfig
// 厳格 snake_case
func StrictSnakeCaseConfig() *FieldValidationConfig
// 厳格 camelCase
func StrictCamelCaseConfig() *FieldValidationConfig3 つのプリセットはいずれも AllowCommonAbbreviations=true かつ EnableSecurityValidation=true とし、後者 2 つは Mode=FieldValidationStrict です。
ValidateFieldKey
func (c *FieldValidationConfig) ValidateFieldKey(key string) errorフィールドキーが設定に一致するか検証します。失敗時に原因を記述する error を返し、検証通過時は nil を返します。レシーバが nil または Mode == FieldValidationNone の場合は nil を直接返します。検証順序:
- 空キー →
"field key cannot be empty"を返す EnableSecurityValidation有効時にinternal.ValidateFieldKeyStrictを実行(Log4Shell / ホモグラフ / overlong UTF-8)Convention == NamingConventionAny→ 命名チェックをスキップAllowCommonAbbreviations有効かつキーが一般略語表にヒット(id/url/http/json/jwtなど、または_id/_url/_uri/_ip/_apiで終わる)→ 通過- 規則に従って項目ごとに検証:snake_case / camelCase / PascalCase / kebab-case
package main
import (
"fmt"
"github.com/cybergodev/dd"
)
func main() {
// 厳格 snake_case プリセット
cfg := dd.StrictSnakeCaseConfig()
if err := cfg.ValidateFieldKey("user_id"); err != nil {
fmt.Println("user_id:", err)
} else {
fmt.Println("user_id OK")
// 出力:user_id OK
}
if err := cfg.ValidateFieldKey("userId"); err != nil {
fmt.Println("userId:", err)
// 出力:userId: field key "userId" does not match snake_case convention
}
// 一般略語の除外:URL は snake_case に適合しないが、略語表にヒットするため通過
if err := cfg.ValidateFieldKey("URL"); err != nil {
fmt.Println("URL:", err)
} else {
fmt.Println("URL OK (略語除外)")
// 出力:URL OK (略語除外)
}
// デフォルト設定 Mode=None、命名を検証しない
defaultCfg := dd.DefaultFieldValidationConfig()
if err := defaultCfg.ValidateFieldKey("anyKey"); err != nil {
fmt.Println("anyKey:", err)
} else {
fmt.Println("anyKey OK (Mode=None)")
// 出力:anyKey OK (Mode=None)
}
}次のステップ
- Logger --
WithFields/InfoWith/SetFieldValidation - LoggerEntry -- プリセットフィールド チェーン呼び出し
- コンテキスト統合 --
ContextExtractorフィールド抽出 - 設定 --
Config.FieldValidation