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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GO语言make和new关键字的区别

    GO语言make和new关键字的区别

    本篇文章来介绍一道非常常见的面试题,到底有多常见呢?可能很多面试的开场白就是由此开始的。那就是 new 和 make 这两个内置函数的区别,希望对大家有所帮助
    2023-04-04
  • golang提示dial tcp 172 .217.163.49:443: connectex: A connection attempt failed解决

    golang提示dial tcp 172 .217.163.49:443: connectex: A con

    这篇文章主要为大家介绍了golang提示dial tcp 172 .217.163.49:443: connectex: A connection attempt failed解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • golang整合jwt的实现示例

    golang整合jwt的实现示例

    json web tokens(jwt)已成为大多数web api设计中的常见身份验证和授权方案之一,本文主要介绍了golang整合jwt的实现示例,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • 详解Go module的介绍及使用

    详解Go module的介绍及使用

    module是一个相关Go包的集合,它是源代码更替和版本控制的单元。这篇文章主要介绍了Go module的介绍及使用,需要的朋友可以参考下
    2020-10-10
  • Go语言学习之context包的用法详解

    Go语言学习之context包的用法详解

    日常Go开发中,Context包是用的最多的一个了,几乎所有函数的第一个参数都是ctx,那么我们为什么要传递Context呢,Context又有哪些用法,底层实现是如何呢?相信你也一定会有探索的欲望,那么就跟着本篇文章,一起来学习吧
    2022-10-10
  • Go语言泛型使用及说明

    Go语言泛型使用及说明

    这篇文章主要介绍了Go语言泛型使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2026-01-01
  • 一文详解Golang 定时任务库 gron 设计和原理

    一文详解Golang 定时任务库 gron 设计和原理

    这篇文章主要介绍了一文详解Golang 定时任务库 gron 设计和原理,gron是一个比较小巧、灵活的定时任务库,可以执行定时的、周期性的任务。gron提供简洁的、并发安全的接口
    2022-08-08
  • Golang学习之内存逃逸分析

    Golang学习之内存逃逸分析

    内存逃逸分析是go的编译器在编译期间,根据变量的类型和作用域,确定变量是堆上还是栈上。本文将带大家分析一下Golang中的内存逃逸,需要的可以了解一下
    2023-01-01
  • 基于Go goroutine实现一个简单的聊天服务

    基于Go goroutine实现一个简单的聊天服务

    对于聊天服务,想必大家都不会陌生,因为在我们的生活中经常会用到,本文我们用 Go 并发来实现一个聊天服务器,这个程序可以让一些用户通过服务器向其它所有用户广播文本消息,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Go设计模式之代理模式图文详解

    Go设计模式之代理模式图文详解

    这篇文章将通过图文讲解给大家详细的介绍一下Go代理模式,代理模式是一种结构型设计模式,代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理,感兴趣的同学跟着小编一起来看看吧
    2023-07-07

最新评论