Go slog 上手指南:结构化日志与零配置性能
log 包被诟病太久了。Go 1.21 带来官方新 slog,本文用最简方式带你完成从 log 到 slog 的升级。
一、为什么选 slog?
- 1.21 起官方推荐
- 同时提供结构化与文本输出
- 内置 logger pool、handler 路由
import"log/slog"logger:=slog.New(slog.NewJSONHandler(os.Stdout,nil))logger.Info("user login","user_id",1,"ip","1.2.3.4")二、Handler 自定义
typeFileHandlerstruct{chchan*slog.Record w io.Writer mu sync.Mutex}func(h*FileHandler)Handle(_context.Context,r slog.Record)error{bytes,_:=json.Marshal(struct{Timestring`json:"time"`Levelstring`json:"level"`Msgstring`json:"msg"`Attrs[]slog.Attr`json:"attrs"`}{r.Time.Format(time.RFC3339),r.Level.String(),r.Message,r.Attrs})h.mu.Lock()deferh.mu.Unlock()h.w.Write(bytes)returnnil}可以方便接入:
sl:=slog.New(handler)slog.SetDefault(sl)三、属性分组
slog.Group("user","id",42,"name","tom",)也可以用slog.Attr提前预编译:
userAttr:=slog.String("user","tom")slog.Info("action",userAttr)四、跨协程日志
slog 默认 handler 是 goroutine safe,但底层 writer 需要保护。File writer 不加锁可能交叉写入。
五、性能对比
slog vs logrus:
BenchmarkSlog-8 1000000 1500 ns/op 250 B/op 3 allocs/op BenchmarkLogrus-8 500000 3500 ns/op 800 B/op 14 allocs/opslog 快 2x。
六、踩坑清单
- 不要直接 slog.Info 字符串拼接:必须传属性
- JSON 字段顺序:slog 内部 map 顺序不定
- Handler panic:某些 handler 内部 panic,需要外面套 recover
- Trace 与 span 字段:用With方法预加载
七、实战:HTTP access log
funcLogger()gin.HandlerFunc{returnfunc(c*gin.Context){start:=time.Now()c.Next()slog.Info("access","method",c.Request.Method,"path",c.Request.URL.Path,"status",c.Writer.Status(),"cost",time.Since(start),"ip",c.ClientIP(),)}}八、总结与展望
slog 给 Go 后端日志一个清新选择。学会它能解决"日志太丑又写不出来"的尴尬。
未来:slog 会与 OpenTelemetry 集成增强,自动加上 trace_id、span_id。
九、参考文献
- Go 官方 blog: slog 介绍
- slog 标准库文档
- go.opentelemetry.io/otel/log