Go 结构化日志实战:用标准库 slog 输出可检索、可分级的日志
test12026-09-303 次阅读
为什么放弃 fmt.Println
开发期用 fmt.Println 调试很顺手,但到了生产环境,散落各处的字符串日志既不便检索,也难区分级别。Go 1.21 起标准库自带 slog,用键值对输出结构化日志,配合 JSON handler 能直接被 ELK、Loki 这类系统采集和过滤。
用 slog 标准库输出结构化日志
最简方式是用 slog.SetDefault 设定一个 JSON handler,之后全局调用 slog.Info / slog.Warn 都会带上统一格式和附加字段。
package main
import (
"context"
"log/slog"
"os"
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(logger)
slog.Info("服务启动",
slog.String("module", "gateway"),
slog.Int("port", 8080),
)
ctx := context.Background()
// 把请求级字段塞进 logger,下游直接取用,不必层层传参
reqLogger := slog.With(slog.String("trace_id", "abc123"))
reqLogger.WarnContext(ctx, "慢查询", slog.Float64("cost_ms", 320.5))
}
在 HTTP 中间件里串联请求日志
把每次请求的方法、路径、耗时作为一条结构化日志记录,是排查线上问题的第一手资料。
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
slog.Info("http_request",
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
slog.Duration("cost", time.Since(start)),
)
})
}
级别与字段使用要点
- 用 slog.String / slog.Int / slog.Duration 等类型化方法加字段,比拼接字符串更利于日志系统按字段检索。
- 不要在日志里打印密钥、 token、身份证号等敏感信息,结构化之后这些字段更容易被明文采集。
- 高并发场景 JSON handler 比 text handler 更利于机器解析,也方便统一接入采集管道。
- 把 trace_id、user_id 这类请求级字段通过 slog.With 绑定到子 logger,全链路日志都能带上,方便串联。
T
test1
文章作者
为什么放弃 fmt.Println 开发期用 fmt.Println 调试很顺手,但到了生产环境,散落各处的字符串日志...
- 分类
- 技术
- 发布时间
- 2026-09-30
- 字数
- 约 1364 字
- 阅读
- 3 次
