Go结构化日志slog入门与实战指南附避坑秘籍

Written by

in

文章目录
  • slog 的设计哲学很清晰:前端 API + 后端实现分离。 Logger:你每天打交道的“前台”,调用 Info()、Error() 的地方。 Handler:幕后英雄,决定日志长什么样、写到哪(比如 JSON 还是文本?stdout 还是文件?)。 Record:中间传话筒,封装了时间、级别、消息、属性等原始数据。 想象一下:Logger 是你点外卖的 App,Handler 是后厨,Record 就是你点的“宫保鸡丁 + 微辣 + 不要花生”。
  • 很多人图省事这么写: logger.Warn(“权限不足”, “user_id”, 123, “resource”) // ❌ 少了个值! 结果?slog 不会报错,而是默默生成一个 !BADKEY 字段: {“!BADKEY”:”resource”, …} 😱 想象一下:线上故障,你查日志发现关键字段变成了 !BADKEY,而老板正盯着你……
  • 在微服务中,请求链路追踪离不开 correlation_id 或 trace_id。slog 配合社区库 slog-context 能轻松实现。
  • 直接打日志可能泄露密码、身份证等敏感信息: type User struct { ID string Email string Password string // ❌ 千万别打出来! }
  • slog 的性能确实不如 zerolog 或 zap(大约慢 5~6 倍),但对绝大多数应用完全够用。 Logger 时间 (ns/op) 分配对象 zerolog 380 1 slog 2480 42 如果你的服务每秒处理 10 万请求,且每条都打日志——那你可能需要 zap。但如果你只是普通 Web 服务?放心用 slog,省下的心智负担远超那几微秒。
  • 日志级别动态调整:用 slog.LevelVar 实现不停机调 DEBUG。 多输出:用 slog-multi 同时写 stdout + 文件 + 网络。 避免重复 key:用 slog-dedup 处理冲突。
  • 优势 说明 官方支持 无需引入第三方依赖(基础场景) 结构化 JSON 输出天然适配现代日志平台 可扩展 Handler 可插拔,轻松集成 安全可控 LogValuer 防止敏感信息泄露 上下文友好 与 context 深度集成 结论:除非你有极端性能要求,否则 slog 是 Go 应用日志的首选方案。 到此这篇关于Go 结构化日志slog入门与实战指南 附避坑秘籍的文章就介绍到这了,更多相关go 结构化日志slog内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Go slog 日志打印最佳实践案例 Go 1.21.0 新增结构化日志记录标准库log/slog使用详解 golang结构化日志log/slog包之slog.Record的用法简介 golang结构化日志log/slog包之LogValuer的用法简介 Go语言结构化日志slog的用法解析 golang结构化日志slog的用法简介 go1.21中slog日志包用法入门 Golang结构化日志包log/slog的使用详解
  • 目录
    • 🧱 一、slog 的三大核心:Logger、Handler、Record
      • 最简示例
    • ⚠️ 二、一个致命陷阱:别再用key, value了!
      • ✅ 正确姿势:用slog.Attr
    • 🛠️ 三、实战:HTTP 请求自动带上 trace ID
      • 场景:每个 HTTP 请求自动生成唯一 ID,并自动注入日志
    • 🔐 四、敏感信息防护:用LogValuer控制输出
      • ✅ 解决方案:实现LogValuer接口
    • 📊 五、性能怎么样?能用在生产吗?
      • 🚀 六、进阶建议
        • ✅ 总结

          “日志不是写给机器看的,是写给人类在半夜三点 debug 时救命用的。”

          Go 1.21 终于带来了官方结构化日志包 —— log/slog。从此,我们不再需要在 zapzerologlogrus 之间反复横跳,也不用担心团队里有人偷偷用 fmt.Println 写日志了(好吧,可能还是会有)。

          本文将带你从零上手 slog,并通过几个小例子展示它如何让日志变得更结构化、可查询、可维护——甚至还能帮你保住头发。

          slog 的设计哲学很清晰:前端 API + 后端实现分离

          • Logger:你每天打交道的“前台”,调用 Info()Error() 的地方。
          • Handler:幕后英雄,决定日志长什么样、写到哪(比如 JSON 还是文本?stdout 还是文件?)。
          • Record:中间传话筒,封装了时间、级别、消息、属性等原始数据。

          想象一下:Logger 是你点外卖的 App,Handler 是后厨,Record 就是你点的“宫保鸡丁 + 微辣 + 不要花生”。

          package main
          import (
          	"log/slog"
          	"os"
          )
          func main() {
          	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
          	logger.Info("用户登录成功", "user_id", 123)
          }

          输出:

          {"time":"2026-01-28T10:00:00Z","level":"INFO","msg":"用户登录成功","user_id":123}

          ✅ 结构化!✅ 可被日志系统解析!✅ 没有乱七八糟的 fmt.Printf

          很多人图省事这么写:

          logger.Warn("权限不足", "user_id", 123, "resource") // ❌ 少了个值!
          

          结果?slog 不会报错,而是默默生成一个 !BADKEY 字段:

          {"!BADKEY":"resource", ...}
          

          😱 想象一下:线上故障,你查日志发现关键字段变成了 !BADKEY,而老板正盯着你……

          logger.Warn("权限不足",
          	slog.Int("user_id", 123),
          	slog.String("resource", "/api/admin"),
          )
          

          这样,编译器就能帮你抓 bug,而不是等到凌晨三点才暴露问题。

          💡 小贴士:配合 golangci-lint + sloglint 插件,强制全项目使用 slog.Attr,彻底杜绝 !BADKEY

          # .golangci.yml
          linters:
            enable:
              - sloglint
          settings:
            sloglint:
              attr-only: true

          在微服务中,请求链路追踪离不开 correlation_idtrace_idslog 配合社区库 slog-context 能轻松实现。

          package main
          import (
          	"log/slog"
          	"net/http"
          	"os"
          	"github.com/google/uuid"
          	slogctx "github.com/veqryn/slog-context"
          )
          func requestID(next http.Handler) http.Handler {
          	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
          		cid := r.Header.Get("X-Correlation-ID")
          		if cid == "" {
          			cid = uuid.New().String()
          		}
          		ctx := slogctx.Prepend(r.Context(), slog.String("cid", cid))
          		r = r.WithContext(ctx)
          		w.Header().Set("X-Correlation-ID", cid)
          		next.ServeHTTP(w, r)
          	})
          }
          func hello(w http.ResponseWriter, r *http.Request) {
          	slog.InfoContext(r.Context(), "处理请求")
          	w.Write([]byte("Hello, slog!"))
          }
          func main() {
          	handler := slogctx.NewHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
          	slog.SetDefault(slog.New(handler))
          	mux := http.NewServeMux()
          	mux.HandleFunc("/", hello)
          	http.ListenAndServe(":8080", requestID(mux))
          }

          访问 curl http://localhost:8080,你会看到:

          {
            "time": "...",
            "level": "INFO",
            "msg": "处理请求",
            "cid": "a1b2c3d4-...",
            ...
          }

          ✅ 所有日志自动带上 cid
          ✅ 无需手动传递!
          ✅ 故障排查时一键过滤整个请求链路!

          直接打日志可能泄露密码、身份证等敏感信息:

          type User struct {
          	ID       string
          	Email    string
          	Password string // ❌ 千万别打出来!
          }
          

          func (u *User) LogValue() slog.Value {
          	return slog.GroupValue(
          		slog.String("id", u.ID),
          		slog.String("email", u.Email),
          		// 故意不输出 Password
          	)
          }
          

          现在:

          logger.Info("创建用户", slog.Any("user", &user))
          

          输出只有安全字段:

          {
            "msg": "创建用户",
            "user": {
              "id": "u123",
              "email": "alice@example.com"
            }
          }
          

          🛡️ 安全是底线,日志也不例外!

          slog 的性能确实不如 zerologzap(大约慢 5~6 倍),但对绝大多数应用完全够用

          Logger 时间 (ns/op) 分配对象
          zerolog 380 1
          slog 2480 42

          如果你的服务每秒处理 10 万请求,且每条都打日志——那你可能需要 zap
          但如果你只是普通 Web 服务?放心用 slog,省下的心智负担远超那几微秒。

          • 日志级别动态调整:用 slog.LevelVar 实现不停机调 DEBUG
          • 多输出:用 slog-multi 同时写 stdout + 文件 + 网络。
          • 避免重复 key:用 slog-dedup 处理冲突。

          优势 说明
          官方支持 无需引入第三方依赖(基础场景)
          结构化 JSON 输出天然适配现代日志平台
          可扩展 Handler 可插拔,轻松集成
          安全可控 LogValuer 防止敏感信息泄露
          上下文友好 context 深度集成

          结论:除非你有极端性能要求,否则 slog 是 Go 应用日志的首选方案

          到此这篇关于Go 结构化日志slog入门与实战指南 附避坑秘籍的文章就介绍到这了,更多相关go 结构化日志slog内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

          您可能感兴趣的文章:

          • Go slog 日志打印最佳实践案例
          • Go 1.21.0 新增结构化日志记录标准库log/slog使用详解
          • golang结构化日志log/slog包之slog.Record的用法简介
          • golang结构化日志log/slog包之LogValuer的用法简介
          • Go语言结构化日志slog的用法解析
          • golang结构化日志slog的用法简介
          • go1.21中slog日志包用法入门
          • Golang结构化日志包log/slog的使用详解

          站内搜索