用slog统一日志格式需从根logger显式配置handler:json格式用slog.newjsonhandler(os.stdout, &slog.handleroptions{addsource: true}),文本格式用slog.newtexthandler(os.stdout, &slog.handleroptions{addtime: true, level: slog.levelinfo});全局字段通过slog.with预设复用,动态字段用logger.with注入;第三方库日志通过slog.newloglogger桥接,老项目需逐步替换log.printf。

日志格式不统一,怎么用 log/slog 统一控制输出结构
Go 1.21+ 默认推荐用 slog,它原生支持结构化日志,不用额外依赖。关键不是“加字段”,而是从根 Logger 就定好 Handler 行为。默认的 slog.NewTextHandler(os.Stdout, nil) 输出是键值对但无时间、无级别前缀、字段顺序不稳定——这不是 bug,是设计:它把格式交给 Handler 决定。
正确做法是显式构造带选项的 Handler:
-
slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, Level: slog.LevelInfo})→ 输出 JSON,含"source"字段和行号 -
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, AddTime: true, TimeFormat: time.DateTime})→ 文本格式带 ISO 时间戳 - 所有日志调用(
slog.Info、slog.Error等)自动继承这些配置,无需每处重复写时间/级别
自定义字段(如 trace_id、service_name)怎么全局注入
不能靠每次 slog.With("trace_id", id).Info(...) 手动加——漏一处就断链。要用 slog.With 创建子 logger 并复用,或者用 slog.WithGroup 隔离上下文。
推荐在服务启动时一次性封装:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
logger := slog.With(
slog.String("service_name", "user-api"),
slog.String("env", os.Getenv("ENV")),
slog.Group("meta", slog.String("version", "v1.2.0")),
)
后续所有 logger.Info、logger.Error 自动带上这些字段。注意:slog.With 返回新 logger,原 logger 不变;如果要动态加字段(比如 HTTP 请求进来的 trace_id),在中间件里用 r.Context().Value() 提取后 logger.With("trace_id", tid) 再传下去。
为什么 slog.Debug 在生产环境没输出
不是代码问题,是 HandlerOptions.Level 没设对。默认 Level 是 slog.LevelInfo,Debug 被静默丢弃。别改代码加 if debug { slog.Debug(...) } —— 这破坏结构化日志的可过滤性。
- 开发环境:启动时设
Level: slog.LevelDebug - 生产环境:保持
Level: slog.LevelInfo或slog.LevelWarn,需要临时开 debug?用slog.LevelVar动态调整 -
LevelVar示例:var level = new(slog.LevelVar) level.Set(slog.LevelInfo) logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}))后续运行时调level.Set(slog.LevelDebug)即可热启用
第三方库日志(如 database/sql、http.Server)怎么接入统一格式
它们不认 slog,但多数提供 SetLogger 或类似钩子。重点不是“改库”,而是桥接:
-
http.Server的ErrorLog字段接受*log.Logger→ 用slog.NewLogLogger把slog.Logger转成*log.Logger -
database/sql没直接支持,但可通过sql.OpenDB+&sql.ConnPool自定义 driver,或用中间件 wrapper 在 query 前后打slog日志 - 最稳妥的是:让第三方库输出到
io.Writer(如os.Stderr),再用slog.NewTextHandler包一层,但会丢失结构——所以优先选有SetLogger接口的库
真正难的是混用 log 和 slog 的老项目:必须逐步替换 log.Printf 为 slog 调用,否则格式永远割裂。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










