Skip to content

構造化フィールド ​

DD は 20 種の型安全なフィールドコンストラクタ、統一された Field 型、およびオプションのフィールドキー検証メカニズムを提供し、構造化ログ出力に使用します。

Field 型 ​

Field は構造化ログフィールド型で、internal.Field の型エイリアスとして外部に公開されます:

go
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 パッケージ内のフレームをフィルタリング、キャプチャにわずかなオーバーヘッドあり)

数値フィールド ​

コンストラクタ型例
Intintdd.Int("count", 42)
Int8int8dd.Int8("flags", 1)
Int16int16dd.Int16("port", 8080)
Int32int32dd.Int32("code", 200)
Int64int64dd.Int64("id", 123456789)
Uintuintdd.Uint("size", 1024)
Uint8uint8dd.Uint8("level", 3)
Uint16uint16dd.Uint16("year", 2026)
Uint32uint32dd.Uint32("seq", 1000)
Uint64uint64dd.Uint64("hash", 0xABCD)
Float32float32dd.Float32("rate", 0.95)
Float64float64dd.Float64("elapsed", 1.234)

時間フィールド ​

コンストラクタシグネチャ説明
Time(key string, value time.Time) Fieldタイムスタンプ(RFC3339 フォーマット)
Duration(key string, value time.Duration) Field所要時間(Duration.String() を呼び出し)

エラーフィールド ​

go
// 標準エラーフィールド(key は "error" 固定、nil error → Value は nil)
dd.Err(err)

// カスタム key
dd.ErrWithKey("db_error", err)

// スタック情報付き(スタックフレームは runtime/ と dd 自身のフレームを除外)
dd.ErrWithStack(err)

使用方法 ​

InfoWith との組み合わせ ​

go
dd.InfoWith("ユーザーログイン",
    dd.String("username", "admin"),
    dd.Time("login_at", time.Now()),
    dd.Bool("mfa", true),
    dd.String("ip", "192.168.1.1"),
)

WithFields チェーン呼び出し ​

go
entry := logger.WithFields(
    dd.String("service", "api"),
    dd.Int("pid", os.Getpid()),
)
entry.Info("サービス起動")

Entry への追加 ​

go
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 ​

検証モード。検証失敗時の処理方法を決定します。

go
type FieldValidationMode int

const (
    FieldValidationNone   FieldValidationMode = iota // 検証を無効化(デフォルト、すべてのチェックをショートサーキット)
    FieldValidationWarn                              // 命名不一致時に warning ログを 1 件記録
    FieldValidationStrict                            // 命名不一致時に error ログを 1 件記録
)

FieldValidationMode の String() メソッドは "none" / "warn" / "strict" を返します(未知の値は "unknown" を返します)。

FieldNamingConvention ​

命名規則。

go
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 ​

フィールド検証設定。

go
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 を有効化していても実行されません。

プリセット設定 ​

go
// デフォルト設定:命名検証を無効化、セキュリティ検証を有効化
func DefaultFieldValidationConfig() *FieldValidationConfig

// 厳格 snake_case
func StrictSnakeCaseConfig() *FieldValidationConfig

// 厳格 camelCase
func StrictCamelCaseConfig() *FieldValidationConfig

3 つのプリセットはいずれも AllowCommonAbbreviations=true かつ EnableSecurityValidation=true とし、後者 2 つは Mode=FieldValidationStrict です。

ValidateFieldKey ​

go
func (c *FieldValidationConfig) ValidateFieldKey(key string) error

フィールドキーが設定に一致するか検証します。失敗時に原因を記述する error を返し、検証通過時は nil を返します。レシーバが nil または Mode == FieldValidationNone の場合は nil を直接返します。検証順序:

  1. 空キー → "field key cannot be empty" を返す
  2. EnableSecurityValidation 有効時に internal.ValidateFieldKeyStrict を実行(Log4Shell / ホモグラフ / overlong UTF-8)
  3. Convention == NamingConventionAny → 命名チェックをスキップ
  4. AllowCommonAbbreviations 有効かつキーが一般略語表にヒット(id/url/http/json/jwt など、または _id/_url/_uri/_ip/_api で終わる)→ 通過
  5. 規則に従って項目ごとに検証:snake_case / camelCase / PascalCase / kebab-case
go
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)
    }
}

次のステップ ​