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 的错误(同 Errerr == 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 日志
    FieldValidationStrict                            // 命名不匹配时记一条 error 日志
)

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

三种预设均将 AllowCommonAbbreviations=trueEnableSecurityValidation=true;后两者 Mode=FieldValidationStrict

ValidateFieldKey

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

校验字段键是否匹配配置。失败时返回描述原因的 error,校验通过返回 nil。接收者为 nilMode == 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)
    }
}

下一步