测试异步日志应降级为同步执行以验证 handler 构造逻辑,用 slogtest.Recorder 捕获条目;压测需替换 I/O 为 io.Discard,监控 goroutine 与内存泄漏,控频模拟突发流量。

用 testing/slogtest 捕获异步日志条目再验证
标准库 slog 自带的 testing/slogtest 不直接支持异步处理器,但可以绕过 channel 消费环节,把异步写入逻辑“降级”为同步执行来测——关键不是测 goroutine 调度,而是测你封装的 handler 是否正确构造了日志条目。
做法是:让异步 handler 的 Handle 方法不发到 channel,而是直接调用 rec.Record()(slogtest.Recorder 实现了 slog.Handler),这样所有日志都进内存队列,后续可断言内容、字段、级别。
- 必须在测试中显式调用
logger.With(...).Info(...)触发 handler,不能只启动后台 goroutine 就完事 - 避免在
Handle里做耗时操作(如json.Marshal),否则slogtest会卡住或超时 - 如果用了自定义字段(如
request_id),需确认slog.Group或slog.Attr构造方式和 recorder 解析逻辑一致
压测前先关掉真实 I/O,用 io.Discard 替代文件写入
真实写磁盘会污染性能数据,且结果不可复现。把异步日志后端的 os.File 或 bufio.Writer 替换为 io.Discard,就能专注测 channel 投递、缓冲区填充、批量 flush 这几层开销。
示例中常见错误是忘记替换 file.WriteString 的目标,导致压测时实际在刷 SSD,延迟全被 I/O 吞掉,误判为“异步没生效”。
- 初始化 logger 时传入
io.Discard而非os.OpenFile(...) - 若用了
bufio.NewWriter,确保buf.Flush()调用路径仍存在,否则 buffer 不清空,日志条目数对不上 - channel 容量设为
1000时,压测并发 2000 goroutine 很可能触发default分支丢日志,得提前检查丢弃逻辑是否开启
用 runtime.ReadMemStats 和 pprof 看 goroutine 与内存泄漏
异步日志最隐蔽的问题不是慢,而是 goroutine 堆积或 channel 缓冲区持续增长。光看 QPS 和延迟不够,得盯住运行时指标。
在压测前后各调一次 runtime.ReadMemStats,重点看 Mallocs(是否逃逸严重)、NumGoroutine(后台协程是否被阻塞卡住)、HeapInuse(缓冲区是否越攒越多)。
- 如果
NumGoroutine每轮压测后+1,大概率是 channel 接收端 panic 退出,但发送端还在发 - 若
HeapInuse持续上涨且不回落,检查LogEntry结构体是否含指针(如*time.Time),或fmt.Sprintf导致字符串逃逸 - 用
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2直接看 goroutine 堆栈,确认日志协程是否 stuck 在file.Write或chan send
模拟突发流量时别只靠 for i := range,要控频控量
真实服务的日志不是匀速来的,而是请求一来就集中打十几条。单纯用 for i := 0; i 发日志,测不出缓冲区击穿或背压失效的问题。
应该用 time.AfterFunc 或 time.Ticker 控制每秒打多少条,并在单次 tick 内批量发(比如一次塞 50 条进 channel),才能暴露 logChan 容量不足、buf.Available() == 0 判断失效等边界问题。
- channel 容量设
1000,但突发 2000 条/秒,default分支会频繁触发,得确认降级逻辑(如打到 stderr)是否真被执行 - 批量 flush 逻辑若按时间触发(如
time.Tick(200 * time.Millisecond)),需验证在低频日志下是否出现长延迟(比如最后一条等 200ms 才落盘) - 不要在压测循环里反复创建
LogEntry{...},应复用结构体变量或预分配 slice,否则 GC 压力会掩盖真实写入瓶颈
真正难测的是异步链路里的“隐性阻塞点”:channel 满了不丢日志反而死等、buffer flush 频率和大小配置错导致小包泛滥、序列化过程逃逸出大量堆对象——这些都不会报错,但会让日志在某个流量拐点突然失联。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











