go 1.21+ 应优先使用 slog 做基础日志输出,它原生支持结构化键值对和层级,性能可控;需用 slog.new 配合 slog.newjsonhandler 或 slog.newtexthandler 输出标准格式,避免手写格式化或滥用 log.printf 导致字段丢失、无法被 loki 等系统解析。

用 log/slog 做基础日志输出,别再手写格式化
Go 1.21+ 默认推荐用 slog,它原生支持结构化、层级键值对,且性能比第三方库更可控。直接用 slog.New + slog.NewJSONHandler 或 slog.NewTextHandler 就能输出标准格式日志,无需自己拼接字符串或引入 zap 等重型依赖。
常见错误是还在用 log.Printf 打结构化日志——字段丢失、无法被转发系统(如 Loki、Fluent Bit)自动解析。改用 slog.Info 并传入 slog.String("user_id", "u123") 这类键值对,才是转发友好的起点。
-
slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true}):开启AddSource可带文件行号,调试时有用,但生产环境建议关掉(有性能开销) - 若日志要发到 Kafka 或 HTTP endpoint,先用
slog.New构建 logger,再把 handler 替换为自定义实现,而不是在每条日志里调用http.Post - 避免在
slog.Any里传未导出 struct 字段——会序列化为空对象,字段名不显式声明就丢数据
用 io.MultiWriter 同时写文件和网络,别用 goroutine 轮询
日志转发不是“先写文件再读文件再发”,而是实时写入多个目标。用 io.MultiWriter 把文件、HTTP client、Kafka producer 的 Write 方法串起来,让一条日志原子性地进所有出口。
常见错误是起一个 goroutine 定期扫描日志文件,再读取后转发——延迟高、重复发、断点续传逻辑复杂。真正低延迟的转发必须从写入源头切入。
- 构造
multi := io.MultiWriter(fileWriter, httpWriter, kafkaWriter),再传给slog.NewJSONHandler(multi, ...) -
httpWriter需包装成实现了io.Writer的类型,Write([]byte)方法里做 POST 请求;注意加超时和重试(比如用net/http的Timeout和简单指数退避) - Kafka producer 若不支持
io.Writer接口,可用 channel + 单 goroutine 消费(但不要每个日志启 goroutine)
用 context.Context 控制转发超时和取消,别让日志卡住主流程
转发失败不能阻塞业务逻辑。所有网络写入操作必须带上 context.WithTimeout,否则一次 DNS 故障或目标服务不可用,会导致整个请求 hang 住。
常见错误是把日志转发当成“后台任务”随便丢进 goroutine,却不设 context 控制——既无法感知失败,也无法主动中断。
- 在自定义
Write方法里,用ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond),并在 defer 中cancel() - HTTP 写入失败时,
return len(p), err即可;slog会忽略这个 error(默认静默),若需告警,得额外加回调钩子 - 别在
Write里做重试——重试应由上层组件(如 Kafka client 自带重试)或独立队列承担,否则易堆积内存
转发前加采样和过滤,别把 debug 日志全打到远端
生产环境日志量大,全量转发既浪费带宽又增加下游压力。用 slog.Handler 的 Handle 方法做前置判断,比在转发端过滤更高效。
常见错误是等日志到了 Loki 或 ES 再用查询语句过滤——已经花了传输和存储成本。
- 在自定义 handler 的
Handle方法开头加条件:if r.Level() == slog.LevelDebug && !isDevEnv() { return nil } - 采样可用简单哈希:
if hash(r.Attrs())%100 != 0 { return nil }实现 1% 采样,适用于高频 warn/error 场景 - 敏感字段(如
password、token)应在Handle中遍历r.Attrs()并擦除,而不是靠下游脱敏
真正难的不是把日志发出去,而是发什么、发多少、发失败了怎么办。转发模块一旦嵌入业务链路,它的稳定性就和 HTTP handler 一样关键——超时控制、错误静默、采样策略,每一处都得按生产级要求抠细节。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











