Go slog 完整指南:官方结构化日志库

> 发布时间:2026-03-14 | 标签:Golang, 日志, slog, 后端开发

一、什么是 slog?

log/slog 是 Go 1.21(2023年8月)正式引入的官方结构化日志库。在此之前,Go 标准库的 log 包功能极其简单——不支持日志级别、不支持结构化字段、不支持 JSON 输出,导致社区长期依赖 logruszapzerolog 等第三方库。

slog 的出现填补了这一空白,目标是成为:

二、快速上手

基本用法


import "log/slog"

// 直接使用默认 logger(输出到 stderr)
slog.Info("服务启动", "port", 8080)
slog.Warn("内存使用率偏高", "usage", "87%")
slog.Error("数据库连接失败", "err", err, "host", "localhost:5432")
slog.Debug("收到请求", "method", "GET", "path", "/api/users")

创建自定义 Logger


// Text Handler(开发环境,人类可读)
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// JSON Handler(生产环境,接入日志系统)
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

// 设置为全局默认
slog.SetDefault(logger)

JSON 输出示例


{"time":"2026-03-14T01:52:00.000Z","level":"INFO","msg":"用户登录","user_id":123,"ip":"192.168.1.1","latency_ms":12}

三、核心特性

3.1 日志级别


slog.Debug("调试信息")    // 开发时用,生产通常关闭
slog.Info("普通信息")     // 正常运行状态
slog.Warn("警告")        // 需要关注但不影响运行
slog.Error("错误", "err", err)  // 发生错误

3.2 Handler 配置


opts := &slog.HandlerOptions{
    Level:     slog.LevelWarn,   // 只输出 Warn 及以上
    AddSource: true,             // 在日志中加入文件名+行号
}

handler := slog.NewJSONHandler(os.Stdout, opts)
logger := slog.New(handler)

3.3 字段预设(With)


// 每次日志自动带上公共字段
logger := slog.With(
    "service", "payment",
    "version", "2.1.0",
    "env", "production",
)

logger.Info("支付成功", "order_id", "ORD-001", "amount", 199.00)
// {"service":"payment","version":"2.1.0","env":"production","msg":"支付成功","order_id":"ORD-001","amount":199}

3.4 Group 分组


logger.WithGroup("db").Info("查询完成",
    "table", "users",
    "rows", 100,
    "duration_ms", 12,
)
// 输出:{"msg":"查询完成","db":{"table":"users","rows":100,"duration_ms":12}}

分组让日志字段层次清晰,便于 Elasticsearch/Loki 等系统解析。

3.5 Context 集成


// 从 context 中自动提取 trace_id 等链路信息
slog.InfoContext(ctx, "处理请求",
    "user_id", userID,
    "handler", "CreateOrder",
)

结合 OpenTelemetry,可以自动将 trace_idspan_id 注入日志,实现日志与链路追踪的关联。

四、自定义 Handler

slog 的核心扩展点是 Handler 接口:


type Handler interface {
    Enabled(context.Context, Level) bool
    Handle(context.Context, Record) error
    WithAttrs(attrs []Attr) Handler
    WithGroup(name string) Handler
}

社区已有大量第三方 Handler:

Handler用途
`slog-multi`同时写多个目标(文件+控制台)
`slog-loki`直接推送到 Grafana Loki
`slog-sentry`Error 自动上报到 Sentry
`slog-datadog`集成 Datadog APM
`tint`终端彩色输出,开发友好

五、性能表现

slog 的设计目标是 zero-allocation,实测性能远超老牌的 logrus


BenchmarkZerolog   ~  57 ns/op    0 allocs/op
BenchmarkZap       ~ 118 ns/op    0 allocs/op
BenchmarkSlog      ~ 197 ns/op    0 allocs/op
BenchmarkLogrus    ~ 941 ns/op   15 allocs/op

对绝大多数业务来说,slog 的性能完全够用。极端性能场景(每秒百万级日志)再考虑 zerolog。

六、与第三方库对比

特性slogzapzerologlogrus
标准库
结构化字段
日志级别
JSON 输出
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
生态成熟度成长中成熟成熟老牌
Context 支持需插件有限
Handler 扩展

七、实际采用情况

普及速度:中等偏上

slog 推出两年多,新项目的采用率在快速上升,但整体仍不及 zap 和 zerolog。原因:

1. 存量项目惰性:大量 Go 项目在 1.21 前就已成熟,换日志库成本不低

2. 第三方库先发优势:zap 10 年积累、zerolog 的极致性能,都有忠实用户

3. 新项目首选:但 Go 1.21+ 的新项目,slog 正在成为首选

最有价值的场景:写库

这是 slog 最被推崇的使用场景。库的作者应该接受 *slog.Logger 作为参数,而不是硬编码某个第三方库:


// 推荐:让调用方决定日志实现
func NewService(logger *slog.Logger) *Service {
    return &Service{logger: logger}
}

// 不推荐:绑定了 zap
func NewService(logger *zap.Logger) *Service { ... }

这样调用方可以传入任何实现了 slog 接口的 handler,包括 zap/zerolog 的 slog 适配器。

八、最佳实践

8.1 生产配置模板


package main

import (
    "log/slog"
    "os"
)

func initLogger(env string) *slog.Logger {
    var handler slog.Handler

    opts := &slog.HandlerOptions{
        AddSource: true,
        Level:     slog.LevelInfo,
    }

    if env == "development" {
        // 开发环境:彩色文本,方便阅读
        opts.Level = slog.LevelDebug
        handler = slog.NewTextHandler(os.Stdout, opts)
    } else {
        // 生产环境:JSON,接入日志平台
        handler = slog.NewJSONHandler(os.Stdout, opts)
    }

    logger := slog.New(handler)
    slog.SetDefault(logger) // 设为全局默认
    return logger
}

8.2 统一错误日志格式


// 封装统一的错误记录
func logError(logger *slog.Logger, msg string, err error, attrs ...any) {
    args := append([]any{"error", err.Error()}, attrs...)
    logger.Error(msg, args...)
}

// 使用
logError(logger, "支付失败",
    err,
    "order_id", orderID,
    "user_id", userID,
    "amount", amount,
)

8.3 与 Viper 配置集成


// 根据配置文件决定日志级别
levelStr := viper.GetString("log.level")
var level slog.Level
level.UnmarshalText([]byte(levelStr))

opts := &slog.HandlerOptions{Level: level}

九、结论与建议

对于新 Go 项目,推荐直接使用 slog。 理由:

以下情况考虑第三方库:

slog 的出现不是要取代 zap/zerolog,而是提供一个标准化的最小公倍数。Go 生态的日志碎片化问题,会随着 slog 的普及逐步收敛。

参考资料:Go 官方文档 pkg.go.dev/log/slog | Go 1.21 Release Notes | github.com/golang/example/slog-handler-guide