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

 更新时间:2026年02月21日 08:06:19   作者:golang学习记  
Go 1.21终于带来了官方结构化日志log/slog,本文将带你从零上手 slog,并通过几个小例子展示它如何让日志变得更结构化、可查询、可维护,感兴趣的朋友跟随小编一起看看吧

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

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

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

🧱 一、slog 的三大核心:Logger、Handler、Record

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

⚠️ 二、一个致命陷阱:别再用key, value了!

很多人图省事这么写:

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

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

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

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

✅ 正确姿势:用slog.Attr

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

🛠️ 三、实战:HTTP 请求自动带上 trace ID

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

场景:每个 HTTP 请求自动生成唯一 ID,并自动注入日志

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
✅ 无需手动传递!
✅ 故障排查时一键过滤整个请求链路!

🔐 四、敏感信息防护:用LogValuer控制输出

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

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

✅ 解决方案:实现LogValuer接口

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)分配对象
zerolog3801
slog248042

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

🚀 六、进阶建议

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

✅ 总结

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

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

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

相关文章

  • golang1.23版本之前 Timer Reset方法无法正确使用

    golang1.23版本之前 Timer Reset方法无法正确使用

    在Go 1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避免潜在的问题,然而,这在实际代码中难以实现,因为设置定时器状态和发送channel的操作并不是原子的,在某些情况下,这会导致timer在不应该触发时提前触发
    2025-01-01
  • Golang sync包中errgroup的使用详解

    Golang sync包中errgroup的使用详解

    Go 语言为我们提供了丰富的并发原语,且大多数都位于 sync 包下,今天我们来探讨一下该库下的原语之一:errgroup,感兴趣的小伙伴可以了解一下
    2023-05-05
  • Go语言数据结构之插入排序示例详解

    Go语言数据结构之插入排序示例详解

    这篇文章主要为大家介绍了Go语言数据结构之插入排序示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Go语言中定时任务库Cron使用方法介绍

    Go语言中定时任务库Cron使用方法介绍

    cron的意思计划任务,说白了就是定时任务。我和系统约个时间,你在几点几分几秒或者每隔几分钟跑一个任务(job),今天通过本文给大家介绍下Go语言中定时任务库Cron使用方法,感兴趣的朋友一起看看吧
    2022-03-03
  • 使用go语言解析xml的实现方法(必看篇)

    使用go语言解析xml的实现方法(必看篇)

    下面小编就为大家带来一篇使用go语言解析xml的实现方法(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • gRPC的发布订阅模式及REST接口和超时控制

    gRPC的发布订阅模式及REST接口和超时控制

    这篇文章主要为大家介绍了gRPC的发布订阅模式及REST接口和超时控制,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go操作etcd的实现示例

    Go操作etcd的实现示例

    etcd是近几年比较火热的一个开源的、分布式的键值对数据存储系统,提供共享配置、服务的注册和发现,本文主要介绍etcd的安装和使用,感兴趣的可以了解一下
    2021-09-09
  • 如何使用Go操作SQLite

    如何使用Go操作SQLite

    文章介绍了如何使用Go语言操作SQLite数据库,推荐使用github.com/mattn/go-sqlite3驱动,涵盖了安装驱动、连接数据库、表的创建与删除、数据的增删改查操作,并提供了一个完整的示例代码,这为学习Go语言操作SQLite提供了一个基础案例,感兴趣的朋友一起看看吧
    2025-10-10
  • go doudou开发gRPC服务快速上手实现详解

    go doudou开发gRPC服务快速上手实现详解

    这篇文章主要为大家介绍了go doudou开发gRPC服务快速上手实现过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Go mod包管理工具详解

    Go mod包管理工具详解

    Go mod作为Go语言的官方包管理工具,可以帮助开发者更好地管理包和依赖,提高开发效率和项目可维护性,本文将介绍Go语言的包和依赖管理,以及Go mod的作用和优势,需要的朋友可以参考下
    2023-05-05

最新评论