slog.With 加不上属性是因为它只返回新 Logger 实例而不输出日志,必须链式调用如 .Info 才生效;值类型不匹配、自定义 Handler 未正确处理 Record.Attrs() 或嵌套 Group 也会导致属性丢失。

slog.With 为什么加不上属性?
因为 slog.With 本身不输出日志,它只返回一个带属性的新 Logger 实例。直接调用 slog.With(...) 而不接 .Info、.Error 等方法,属性就“消失”了——不是失效,是根本没被用上。
- 常见错误现象:
slog.With("user_id", 123)单独一行,日志里完全看不到user_id - 正确姿势:必须链式调用,比如
slog.With("user_id", 123).Info("login succeeded") - 注意:每次
With都生成新实例,原 logger 不变;适合在 handler 入口或中间件里封装复用
属性值类型不匹配导致字段丢失
slog 对属性值类型很敏感,非基本类型(如 struct、map、自定义类型)默认会被转成 fmt.Sprint 字符串,字段名保留但值变成不可读的 {...} 或空字符串。
- 典型表现:
slog.With("req", myHTTPReq).Info("start")日志里只看到req=""或req="{...}" - 解决办法:确保值实现
LogValuer接口,或用slog.Group显式展开结构体字段 - 简单场景下优先用基础类型:int、string、bool、time.Time;复杂数据建议提前提取关键字段,比如
"req_method","req_path"
在 HTTP handler 中安全添加请求上下文属性
Web 服务里最容易想当然地把整个 *http.Request 当属性传进去,结果要么字段丢失,要么泄露敏感头信息(如 Authorization)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 推荐做法:用
slog.With提前绑定稳定上下文,比如路由名、trace ID;动态属性用handler.ServeHTTP内部按需添加 - 避免:
slog.With("req", r).Info(...)——r是指针,且含大量未导出字段和闭包,序列化行为不可控 - 更稳妥:
slog.With("path", r.URL.Path, "method", r.Method, "trace_id", r.Header.Get("X-Trace-ID")).Info(...) - 性能提示:频繁创建带大量属性的 logger 可能有小开销,但比每次拼接字符串更轻量;不用过度优化,除非压测中真成了瓶颈
自定义 Handler 时属性被覆盖或丢失
如果你写了自定义 slog.Handler,但发现 With 添加的属性没出现在最终输出里,大概率是没正确处理 group 和 attrs 的合并逻辑。
- 关键点:Handler 的
Handle方法接收的Record已包含所有属性(含With带来的),但Record.Attrs()返回的是迭代器,不是扁平 map - 常见坑:直接遍历
Record.Attrs()时忽略嵌套Group,或没调用attr.Value.Resolve()获取真实值 - 调试建议:先用
slog.NewTextHandler(os.Stdout, nil)对照输出,确认属性是否真的传到了Record层面
属性不是“写进去就完事”,它依赖 logger 实例的生命周期、值类型的可序列化性、以及 handler 对 Record 的解析深度——这三个地方任一出问题,都会让属性静默消失。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










