核心概念
理解 DD 的核心概念是高效使用本库的基础。本章介绍 Logger 体系、字段系统、处理管道和接口层次。
Logger 体系
DD 的日志记录围绕三个核心类型展开:
text
Logger(日志记录器)
│
├── 直接使用 → logger.Info("message")
│
└── WithFields() → LoggerEntry(带预设字段的 Entry)
│
└── entry.Info("message") // 自动携带预设字段Logger
Logger 是核心日志记录器,由 dd.New() 创建:
go
logger, err := dd.New(dd.DefaultConfig())
if err != nil {
log.Fatal(err)
}
defer logger.Close()
logger.Info("服务启动")
logger.InfoWith("请求处理",
dd.String("method", "GET"),
dd.Int("status", 200),
)每个 Logger 拥有独立的配置、输出目标、安全过滤器和生命周期,可在不同模块间安全共享。
LoggerEntry
LoggerEntry 通过 WithFields() 创建,是不可变的预设字段容器:
go
// 创建带预设字段的 Entry
requestLog := logger.WithFields(
dd.String("service", "user-api"),
dd.String("version", "2.1.0"),
)
// 每次调用自动携带预设字段
requestLog.Info("服务启动")
// 输出:... 服务启动 service=user-api version=2.1.0
requestLog.InfoWith("用户登录",
dd.String("user", "alice"),
)
// 输出:... 用户登录 service=user-api version=2.1.0 user=alice不可变设计
每次调用 WithFields() 都会创建新的 LoggerEntry,原有 Entry 不受影响。这意味着你可以在不同 goroutine 中安全地复用同一个 Entry。
全局日志记录器
DD 提供全局日志记录器,适合简单场景或快速原型:
go
// 直接使用包级函数(通过全局 Logger)
dd.Info("全局日志")
// 等价于
dd.Default().Info("全局日志")字段系统
Field 类型
Field 是结构化日志的基本单元,由键值对构成:
go
// 字段构造器覆盖所有常用类型
dd.String("method", "GET") // 字符串
dd.Int("status", 200) // 整数
dd.Float64("latency", 0.123) // 浮点数
dd.Bool("success", true) // 布尔
dd.Duration("elapsed", 150*time.Millisecond) // 时间段
dd.Time("timestamp", time.Now()) // 时间戳
dd.Err(err) // 错误(key 固定为 "error")
dd.ErrWithKey("db_error", err) // 错误(自定义 key)
dd.Any("data", payload) // 任意类型字段链式传递
字段可以在 Logger、Entry 之间层层传递:
go
// 第一层:服务级字段
serviceLog := logger.WithFields(
dd.String("service", "api-gateway"),
)
// 第二层:请求级字段(追加到服务级)
requestLog := serviceLog.WithFields(
dd.String("request_id", "req-001"),
dd.String("path", "/api/users"),
)
// 第三层:实际日志(再追加字段)
requestLog.InfoWith("处理完成",
dd.Int("status", 200),
dd.Duration("elapsed", 50*time.Millisecond),
)
// 输出包含:service=api-gateway request_id=req-001 path=/api/users status=200 elapsed=50ms日志处理管道
每条日志经过以下处理流程:
text
用户调用 logger.InfoWith("msg", fields...)
│
▼
① 级别检查 ─── 级别未启用 → 直接返回(零开销)
│
▼
② 安全过滤 ─── 消息和字段中的敏感数据 → [REDACTED]
│
▼
③ 上下文提取 ── 调用已注册的提取器附加静态/全局字段(用 context.Background() 调用,无法读取请求作用域的 TraceID)
│
▼
④ BeforeLog 钩子
│
▼
⑤ 格式化 ──── 文本格式 或 JSON 格式
│
▼
⑥ 安全大小限制 ─── 超过 Security.MaxMessageSize 则截断(0 表示不限制)
│
▼
⑦ 写入 ────── 输出到一个或多个 Writer
│
▼
⑧ AfterLog 钩子
│
▼
⑨ Fatal 处理 ── 仅 LevelFatal:先异步 Close Logger(最长等待 5s,触发 OnClose 钩子并 flush writer),再调用 os.Exit(1) 或自定义 FatalHandler性能设计
级别检查(步骤 ①)使用原子操作,无需加锁,几乎零开销。安全过滤(步骤 ②)带超时保护,防止长时间阻塞主流程(大输入通过 goroutine + 超时保证最坏约 50ms 内返回)。Fatal 处理(步骤 ⑨)会异步触发 Logger 的 Close(含 flush 与 OnClose 钩子),最长等待 5s;用户 main 中的 defer 仍不执行,但 Logger 自身的 Close 会被调用。
接口层次
DD 定义了四个接口,支持精确的依赖注入:
text
CoreLogger ← 基础日志:Debug/Info/Warn/Error/Fatal + WithFields
│
├── LevelLogger ← 级别管理:GetLevel/SetLevel/IsLevelEnabled(嵌入 CoreLogger)
│
└── ConfigurableLogger ← 配置管理:Writer/安全/上下文/钩子(嵌入 CoreLogger)
LogProvider ← 完整功能:独立扁平接口,包含所有方法go
// 只需基础日志?注入 CoreLogger
type Service struct {
log dd.CoreLogger
}
// 需要动态调整级别?注入 LevelLogger
type Handler struct {
log dd.LevelLogger
}最佳实践
在构造函数中接受最小必需的接口,而不是具体类型。这使代码更易测试、更灵活。
线程安全模型
DD 的核心设计原则:多 goroutine 安全使用,无需额外同步。
| 组件 | 安全机制 |
|---|---|
| Logger | 所有方法可安全并发调用 |
| LoggerEntry | 不可变,创建后只读 |
| Config | Clone() 方法用于安全复制 |
| Writers | 原子指针,无锁读取 |
| SensitiveDataFilter | 读写分离,独立 goroutine |
| HookRegistry | RWMutex 保护注册与读取(Logger 通过 atomic.Value 持有其指针) |
go
// 安全:多个 goroutine 共享同一个 Logger
var logger *dd.Logger // 初始化一次
func handleRequest(w http.ResponseWriter, r *http.Request) {
// 安全:并发调用
logger.InfoWith("请求到达",
dd.String("path", r.URL.Path),
dd.String("method", r.Method),
)
}输出目标体系
DD 支持三种输出目标,可任意组合:
go
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{
dd.ConsoleOutput(), // 控制台
dd.FileOutput("logs/app.log"), // 文件(自动轮换)
dd.CustomOutput(customWriter), // 自定义 io.Writer
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()内置 Writer 组件:
| 组件 | 用途 |
|---|---|
FileWriter | 文件写入 + 大小/时间轮换 + 压缩 |
BufferedWriter | 缓冲写入,减少 I/O 次数 |
MultiWriter | 多目标分发,写入到多个 Writer |