slog 默认不输出日志因使用空 handler,需显式设置带选项的 TextHandler 或 JSONHandler 并调用 slog.SetDefault;Debug 级别默认被过滤,须通过 HandlerOptions.Level 配置 LevelVar 才生效。

slog 是 Go 1.21+ 官方推荐的日志方案,但直接用 slog.New 默认输出是无格式、无时间戳、无调用位置的,不适用于生产环境。
为什么默认的 slog.New 看起来像没生效
Go 标准库的 slog 默认使用 slog.Handler 的最简实现(slog.TextHandler 或 slog.JSONHandler)需要显式传入,否则 slog.New 返回的是一个“空 handler”——它会静默丢弃所有日志。
- 常见错误现象:
slog.Info("hello")运行后控制台完全没输出,也没报错 - 根本原因:没设置全局 logger,也没给
slog.New传 handler;slog.Default()在未初始化时也返回空 handler - 正确做法:必须显式构造带 handler 的 logger,并用
slog.SetDefault激活,或直接用该 logger 实例 - 示例:
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))
slog.Info("server started", "port", ":8080")
slog.TextHandler 和 slog.JSONHandler 怎么选
两者性能接近,差异主要在可读性、解析友好性和调试成本上。
-
slog.TextHandler:适合本地开发、CI 日志查看,人类可读,但字段顺序不固定,无法被结构化日志系统(如 Loki、Datadog)自动解析 -
slog.JSONHandler:字段名固定、嵌套清晰,能被大多数日志收集器识别;但单行 JSON 不易肉眼扫读,调试时需格式化 - 注意:
slog.JSONHandler默认不输出时间戳和程序位置,需手动加slog.HandlerOptions{AddSource: true, AddTime: true} - 示例(带时间 + 文件行号):
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
AddTime: true,
AddSource: true,
})
slog.SetDefault(slog.New(h))
如何加自定义字段(比如 request_id、trace_id)
slog 不支持全局 context 字段(不像 zap 的 With),必须每次调用时显式传入,或封装一层。
立即学习“go语言免费学习笔记(深入)”;
- 直接传参:每次写
slog.Info("msg", "request_id", rid, "trace_id", tid, ...)很冗长 - 推荐封装:
type Logger struct { *slog.Logger; reqID, traceID string },然后提供WithRequest(...)方法返回新实例 - 更轻量做法:用
slog.With创建子 logger(Go 1.22+ 支持),它会继承父 handler 并叠加属性 - 示例:
reqLogger := slog.With("request_id", "abc123", "trace_id", "t-789")
reqLogger.Info("handling request", "path", "/api/users")
为什么 slog.Debug 不输出,以及怎么开调试日志
slog 默认只输出 LevelInfo 及以上级别(即 Info / Warn / Error),Debug 被静默过滤。
- 常见错误现象:
slog.Debug("debug only")完全没输出,以为函数失效 - 解决方法:配置 handler 时传入
LevelVar或固定LevelDebug - 安全做法:用
slog.LevelVar动态控制,避免硬编码(尤其上线后想临时开 debug) - 示例:
var level = new(slog.LevelVar)
level.Set(slog.LevelDebug)
h := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level})
slog.SetDefault(slog.New(h))
真正容易被忽略的点是:handler 的 Level 设置必须作用于 handler 构造时,而不是 logger;且 slog.Debug 在非调试模式下不仅不输出,连参数求值都跳过(惰性求值),所以别依赖它的副作用。


















