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.Stringererror インターフェースを実装する値など)は 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)をサポートします。検証設定 FieldValidationConfigConfig.FieldValidation に設定して構築時に有効化するか、実行時に Logger.SetFieldValidation で動的に置き換えできます。*With の呼び出しごとに各フィールドの Key に対して ValidateFieldKey を呼び出し、Strict モードでは失敗時にログ形式でエラーを報告します(ログメソッド自体は error を返しません)。

FieldValidationMode

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

go
type FieldValidationMode int

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

FieldValidationModeString() メソッドは "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
)

FieldNamingConventionString() メソッドは "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)
    }
}

次のステップ