在 Go 1.21 版本中,官方引入了全新的结构化日志库 log/slog,这一特性彻底改变了 Go 语言在日志处理领域的格局。作为标准库的一部分,slog 不仅提供了结构化日志的核心能力,还通过简洁的 API 设计、高性能实现和高度可扩展性,成为现代 Go 应用日志记录的首选方案。
一、为什么需要 slog?
在 slog 出现之前,Go 标准库的 log 包存在以下局限性:
- 非结构化输出:日志以纯文本形式输出,难以被日志分析工具(如 ELK、Grafana Loki)解析
- 缺乏日志级别:只有简单的
Print/Fatal/Panic方法,无法区分调试、警告、错误等不同级别 - 扩展性差:难以自定义日志格式或输出目标
第三方库如 zap、logrus 虽然解决了这些问题,但需要引入额外依赖,且 API 设计各不相同。slog 的出现统一了结构化日志的标准,同时保持了零依赖的优势。
二、核心特性解析
1. 结构化日志支持
slog 的核心创新在于将日志记录为键值对(Key-Value)形式,这种格式天然适合机器处理:
1package main
2
3import (
4 "log/slog"
5 "os"
6)
7
8func main() {
9 // 基础键值对日志
10 slog.Info("用户登录", "user_id", 123, "ip", "192.168.1.1")
11
12 // 更安全的 slog.Attr 方式(推荐)
13 slog.Info("权限检查",
14 slog.Int("user_id", 123),
15 slog.String("action", "delete"),
16 slog.Bool("allowed", false),
17 )
18}
19
输出示例(JSON 格式):
1{
2 "time": "2026-03-20T10:00:00+08:00",
3 "level": "INFO",
4 "msg": "用户登录",
5 "user_id": 123,
6 "ip": "192.168.1.1"
7}
8
2. 日志级别控制
slog 定义了四个标准日志级别(从低到高):
Debug:开发调试信息Info:常规业务日志Warn:潜在问题警告Error:业务错误记录
1// 设置全局日志级别
2handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
3 Level: slog.LevelWarn, // 只记录 Warn 及以上级别
4})
5logger := slog.New(handler)
6slog.SetDefault(logger)
7
8slog.Debug("这条不会输出")
9slog.Info("这条也不会")
10slog.Warn("这条会输出")
11slog.Error("这条也会")
12
3. 灵活的输出格式
slog 内置两种常用格式处理器:
- TextHandler:人类可读的键值对格式
- JSONHandler:机器可解析的 JSON 格式
1// JSON 格式输出
2jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
3jsonLogger.Info("订单创建", "order_id", "ORD123", "amount", 99.99)
4
5// 自定义时间格式
6textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
7 ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
8 if a.Key == slog.TimeKey {
9 t := a.Value.Time().Format("2006-01-02 15:04:05")
10 return slog.StringValue("time", t)
11 }
12 return a
13 },
14})
15
4. 上下文集成
通过 With 方法可以创建带有固定字段的子日志记录器:
1baseLogger := slog.Default()
2requestLogger := baseLogger.With(
3 slog.String("request_id", "req-123"),
4 slog.String("service", "user-service"),
5)
6
7requestLogger.Info("处理请求开始")
8requestLogger.Warn("参数校验失败", "param", "age")
9
三、高级用法
1. 自定义处理器
实现 slog.Handler 接口可以完全控制日志处理逻辑:
1type CustomHandler struct {
2 next slog.Handler
3}
4
5func (h *CustomHandler) Enabled(level slog.Level) bool {
6 return h.next.Enabled(level)
7}
8
9func (h *CustomHandler) Handle(r slog.Record) error {
10 // 添加自定义字段
11 newR := r.Clone()
12 newR.AddAttrs(slog.String("custom_field", "value"))
13 return h.next.Handle(newR)
14}
15
16func (h *CustomHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
17 return &CustomHandler{next: h.next.WithAttrs(attrs)}
18}
19
20func (h *CustomHandler) WithGroup(name string) slog.Handler {
21 return &CustomHandler{next: h.next.WithGroup(name)}
22}
23
24// 使用示例
25handler := &CustomHandler{next: slog.NewJSONHandler(os.Stdout, nil)}
26logger := slog.New(handler)
27logger.Info("自定义处理器测试")
28
2. 日志双写
将日志同时输出到控制台和文件:
1file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
2multiWriter := io.MultiWriter(os.Stdout, file)
3
4handler := slog.NewJSONHandler(multiWriter, &slog.HandlerOptions{
5 AddSource: true, // 添加调用位置信息
6})
7logger := slog.New(handler)
8logger.Info("日志双写示例")
9
3. 敏感信息保护
通过实现 LogValuer 接口防止敏感信息泄露:
1type User struct {
2 ID string
3 Email string
4 Password string // 敏感字段
5}
6
7func (u *User) LogValue() slog.Value {
8 return slog.GroupValue(
9 slog.String("id", u.ID),
10 slog.String("email", u.Email),
11 // 不输出 Password
12 )
13}
14
15func main() {
16 user := &User{ID: "u123", Email: "user@example.com", Password: "secret"}
17 slog.Info("用户信息", slog.Any("user", user))
18}
19
四、性能对比
虽然 slog 的设计优先考虑易用性和功能性,但其性能表现依然出色:
| 日志库 | 吞吐量(ops/sec) | 内存分配(B/op) |
|---|---|---|
| std log | 1,200,000 | 128 |
| slog | 850,000 | 480 |
| zerolog | 1,500,000 | 96 |
测试条件:单线程输出 100 字节日志消息
对于大多数应用场景,slog 的性能已经足够优秀。只有在每秒需要处理数十万日志请求的极端场景下,才需要考虑使用 zerolog 等高性能库。
五、迁移建议
- 新项目:直接使用
slog作为日志解决方案 - 旧项目迁移:
- 逐步替换:先在关键路径使用
slog - 兼容处理:通过
slog.NewLogLogger将slog.Logger转换为log.Logger供旧代码使用
- 逐步替换:先在关键路径使用
- 关键配置:
go
1// 推荐的生产环境配置 2opts := &slog.HandlerOptions{ 3 Level: slog.LevelInfo, 4 AddSource: true, 5 ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { 6 // 自定义字段处理逻辑 7 return a 8 }, 9} 10logger := slog.New(slog.NewJSONHandler(os.Stdout, opts)) 11slog.SetDefault(logger) 12
六、总结
Go 1.21 的 slog 日志库通过结构化支持、日志级别控制、灵活处理器和上下文集成等特性,为 Go 应用提供了现代化的日志解决方案。其作为标准库的一部分,既避免了第三方依赖的复杂性,又通过清晰的 API 设计和良好的扩展性满足了各种场景需求。
对于正在构建云原生应用、微服务架构或需要复杂日志分析的开发者来说,slog 无疑是当前 Go 生态中最值得投入学习的日志库。随着 Go 生态对结构化日志的进一步支持,可以预见 slog 将很快成为 Go 日志处理的事实标准。