Skip to content

Structured Fields ​

DD provides 20 type-safe field constructors, a unified Field type, and an optional field-key validation mechanism for structured log output.

Field Type ​

Field is the structured-log field type, exposed as a type alias of internal.Field:

go
type Field = internal.Field

// Actual struct (internal/fields.go)
type Field struct {
    Key   string  // Field key
    Value any     // Field value (any type)
}

All field constructors return a Field value; the formatter (internal.FormatFields) outputs them as Key=Value. Primitive types (string / numeric / bool / time.Duration / time.Time / nil) take a fast path; "complex types" such as slices, arrays, maps, and structs fall back to JSON serialization (decided by internal.IsComplexValue); other types (such as values implementing the fmt.Stringer or error interface) go through fmt.Fprint.

Basic Fields ​

ConstructorSignatureDescription
Any(key string, value any) FieldAny type
String(key, value string) FieldString
Bool(key string, value bool) FieldBoolean
Err(err error) FieldError (key is fixed to "error"; when err == nil Value is nil, otherwise err.Error())
ErrWithKey(key string, err error) FieldError with custom key (same as Err; when err == nil Value is nil)
ErrWithStack(err error) FieldError with call stack (key is "error"; when err == nil Value is nil; stack frames filter out runtime/ and dd-package frames; capture has a small cost)

Numeric Fields ​

ConstructorTypeExample
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 Fields ​

ConstructorSignatureDescription
Time(key string, value time.Time) FieldTimestamp (formatted as RFC3339)
Duration(key string, value time.Duration) FieldDuration (calls Duration.String())

Error Fields ​

go
// Standard error field (key is fixed to "error"; nil error -> Value is nil)
dd.Err(err)

// Custom key
dd.ErrWithKey("db_error", err)

// With stack info (stack frames filter out runtime/ and dd-package frames)
dd.ErrWithStack(err)

Usage ​

With InfoWith ​

go
dd.InfoWith("user login",
    dd.String("username", "admin"),
    dd.Time("login_at", time.Now()),
    dd.Bool("mfa", true),
    dd.String("ip", "192.168.1.1"),
)

With WithFields Chaining ​

go
entry := logger.WithFields(
    dd.String("service", "api"),
    dd.Int("pid", os.Getpid()),
)
entry.Info("service started")

Appending to an Entry ​

go
base := logger.WithFields(dd.String("req_id", id))
base.InfoWith("response",
    dd.Int("status", 200),
    dd.Duration("elapsed", took),
    dd.Err(err),
)

Field Validation ​

DD provides a field-key validation mechanism supporting naming-convention checks and security validation (Log4Shell injection, homograph attacks, overlong UTF-8). The validation config FieldValidationConfig can be attached to Config.FieldValidation to take effect at construction time, or dynamically replaced at runtime via Logger.SetFieldValidation. Each *With call invokes ValidateFieldKey on each field's Key; in Strict mode, failures are reported as log errors (the log method itself does not return an error).

FieldValidationMode ​

Validation mode, deciding how validation failures are handled.

go
type FieldValidationMode int

const (
    FieldValidationNone   FieldValidationMode = iota // Disable validation (default, short-circuits all checks)
    FieldValidationWarn                              // Log a warning entry on naming mismatch
    FieldValidationStrict                            // Log an error entry on naming mismatch
)

The String() method of FieldValidationMode returns: "none" / "warn" / "strict" (unknown values return "unknown").

FieldNamingConvention ​

Naming convention.

go
type FieldNamingConvention int

const (
    NamingConventionAny         FieldNamingConvention = iota // Accept any valid key (default)
    NamingConventionSnakeCase                                // snake_case: user_id
    NamingConventionCamelCase                                // camelCase: userId
    NamingConventionPascalCase                               // PascalCase: UserId
    NamingConventionKebabCase                                // kebab-case: user-id
)

The String() method of FieldNamingConvention returns: "any" / "snake_case" / "camelCase" / "PascalCase" / "kebab-case" (unknown values return "unknown").

FieldValidationConfig ​

Field validation configuration.

go
type FieldValidationConfig struct {
    Mode                     FieldValidationMode    // Validation mode
    Convention               FieldNamingConvention  // Naming convention
    AllowCommonAbbreviations bool                   // Allow common abbreviations (ID, URL, HTTP, JSON, etc.)
    EnableSecurityValidation bool                   // Enable security validation (Log4Shell / homograph / overlong UTF-8)
}

Zero-value pitfall

A literal FieldValidationConfig{} sets EnableSecurityValidation=false, silently disabling security validation — prefer the DefaultFieldValidationConfig constructor (which sets it to true). Additionally, when Mode == FieldValidationNone, validation short-circuits before security checks; even with EnableSecurityValidation enabled, security validation will not run.

Preset Configs ​

go
// Default config: disable naming validation but enable security validation
func DefaultFieldValidationConfig() *FieldValidationConfig

// Strict snake_case
func StrictSnakeCaseConfig() *FieldValidationConfig

// Strict camelCase
func StrictCamelCaseConfig() *FieldValidationConfig

All three presets set AllowCommonAbbreviations=true and EnableSecurityValidation=true; the latter two set Mode=FieldValidationStrict.

ValidateFieldKey ​

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

Validates whether a field key matches the configuration. Returns an error describing the reason on failure, nil on success. Returns nil directly when the receiver is nil or Mode == FieldValidationNone. Validation order:

  1. Empty key -> returns "field key cannot be empty"
  2. When EnableSecurityValidation is enabled, runs internal.ValidateFieldKeyStrict (Log4Shell / homograph / overlong UTF-8)
  3. Convention == NamingConventionAny -> skip naming check
  4. If AllowCommonAbbreviations is on and the key hits the common-abbreviation table (id/url/http/json/jwt, etc., or ends with _id/_url/_uri/_ip/_api) -> pass
  5. Per-convention checks: snake_case / camelCase / PascalCase / kebab-case
go
package main

import (
    "fmt"

    "github.com/cybergodev/dd"
)

func main() {
    // Strict snake_case preset
    cfg := dd.StrictSnakeCaseConfig()

    if err := cfg.ValidateFieldKey("user_id"); err != nil {
        fmt.Println("user_id:", err)
    } else {
        fmt.Println("user_id OK")
        // Output: user_id OK
    }

    if err := cfg.ValidateFieldKey("userId"); err != nil {
        fmt.Println("userId:", err)
        // Output: userId: field key "userId" does not match snake_case convention
    }

    // Common-abbreviation exemption: URL is not snake_case but hits the abbreviation table, so it passes
    if err := cfg.ValidateFieldKey("URL"); err != nil {
        fmt.Println("URL:", err)
    } else {
        fmt.Println("URL OK (abbreviation exemption)")
        // Output: URL OK (abbreviation exemption)
    }

    // Default config Mode=None, no naming validation
    defaultCfg := dd.DefaultFieldValidationConfig()
    if err := defaultCfg.ValidateFieldKey("anyKey"); err != nil {
        fmt.Println("anyKey:", err)
    } else {
        fmt.Println("anyKey OK (Mode=None)")
        // Output: anyKey OK (Mode=None)
    }
}

Next Steps ​