Skip to content

日志采样

在高吞吐场景(如 HTTP 请求日志、事件流处理)中,逐条记录所有日志会产生大量数据。DD 的采样功能按比例保留日志,在控制日志体积的同时仍能反映整体趋势。

采样原理

DD 采用计数器采样策略:

┌─────────────────────────────────────────────────────┐
│  请求 1-100   →  全部记录(Initial 阶段)            │
│  请求 101     →  跳过                                │
│  请求 102     →  跳过                                │
│  ...                                                │
│  请求 110     →  记录(每 Thereafter=10 条记 1 条)   │
│  请求 111     →  跳过                                │
│  ...                                                │
│  请求 120     →  记录                                │
│                                                     │
│  (Tick 到期 → 计数器归零,重新进入 Initial 阶段)     │
└─────────────────────────────────────────────────────┘
参数说明典型值
Enabled是否启用采样true
Initial始终记录的前 N 条100
Thereafter之后每 N 条记录 1 条10
Tick计数器重置周期(0 = 不重置)1s / 1m

快速使用

配置时启用

go
package main

import (
    "log"
    "time"

    "github.com/cybergodev/dd"
)

func main() {
    cfg := dd.DefaultConfig()
    cfg.Sampling = &dd.SamplingConfig{
        Enabled:    true,
        Initial:    100,             // 前 100 条始终记录
        Thereafter: 10,              // 之后每 10 条记录 1 条
        Tick:       time.Second,     // 每秒重置计数器
    }

    logger, err := dd.New(cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer logger.Close()

    // 模拟高吞吐日志
    for i := 0; i < 1000; i++ {
        logger.InfoWith("请求处理",
            dd.Int("seq", i),
        )
    }
    // 实际输出:前 100 条 + 后 900 条中的 90 条 = 190 条
}

运行时动态切换

go
// 启用采样
logger.SetSampling(&dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 20,
    Tick:       0, // 不自动重置
})

// 关闭采样(恢复全量记录)
logger.SetSampling(nil)

// 查询当前采样配置
sc := logger.GetSampling()
if sc != nil {
    fmt.Printf("采样: Initial=%d, Thereafter=%d\n", sc.Initial, sc.Thereafter)
}

全局 Logger 采样

包级函数 dd.SetSampling()dd.GetSampling() 直接操作全局 Logger。

参数详解

Initial:初始全量窗口

Initial 保证系统启动或 Tick 重置后的前 N 条日志全部记录,确保:

  • 启动阶段的初始化日志不丢失
  • 短时间内的突发流量有完整记录
  • Tick 重置后能看到周期起始状态

Thereafter:采样率

Thereafter 控制采样密度:

Thereafter效果日志保留率(Initial 后)
1每条都记(等于关闭采样)100%
10每 10 条记 1 条10%
100每 100 条记 1 条1%
0Initial 后不再记录0%

Thereafter=0

Thereafter=0 表示 Initial 阶段后完全停止记录。这在某些场景下有用(如只需启动日志),但请确保不会遗漏重要信息。

Tick:周期重置

go
// 方案 A:每秒重置(适合突发流量检测)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 100, Thereafter: 10,
    Tick: time.Second,
}

// 方案 B:不重置(全局计数,适合长期降量)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 1000, Thereafter: 100,
    Tick: 0,
}

Tick 重置后,计数器归零,重新进入 Initial 全量阶段。这对于按周期观察流量模式非常有用。

典型配置场景

场景 1:HTTP 请求日志

go
// 高流量 API:前 100 条全量,之后 10% 采样,每秒重置
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    100,
    Thereafter: 10,
    Tick:       time.Second,
}

场景 2:后台任务日志

go
// 批处理任务:前 50 条全量,之后每 100 条记 1 条,不重置
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 100,
    Tick:       0,
}

场景 3:调试模式切换

go
// 正常运行时:采样降量
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

// 排查问题时:关闭采样,全量记录
logger.SetSampling(nil)

// 排查完毕:恢复采样
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

线程安全

采样功能使用原子操作(atomic.Int64)实现计数器,Tick 重置使用互斥锁保护。在多 goroutine 并发记录日志时无需额外同步。

Fatal 日志不受采样限制

即使启用了采样,Fatal 级别的日志始终被记录——因为 Fatal 必须在程序退出前写入,不应被采样跳过。

下一步