slog.LogValuer需显式实现并传入,非自动识别敏感字段;logrus过滤唯一安全点是JSONFormatter.Format()内深拷贝处理;Gin日志脱敏须单独处理body、header、query;zap需结构体实现MarshalLogObject接口。

slog.LogValuer 接口必须显式使用,不是自动生效
直接把原始结构体或 map 传给 slog.Info,哪怕字段名是 "password" 或 "token",slog 也不会识别、不会调用任何脱敏逻辑。它只在值本身实现了 LogValuer 接口时才触发 LogValue() 方法。
常见错误是这样写:
slog.Info("login", "user", user) // user 是普通 struct,LogValuer 不起作用
正确做法是:把敏感字段单独包装成新类型,并显式传入:
- 定义
SensitiveToken类型,实现LogValue()返回"[REDACTED]" - 调用时写
slog.Info("login", "token", SensitiveToken(user.Token)),而不是"token", user.Token - 不能对
*string直接实现接口——必须是新命名类型(如type Password string),且接收者用值类型(func (p Password) LogValue()) - 嵌套结构中,只要某个字段是
LogValuer类型,slog就会自动调用,无需手动展开
logrus.JSONFormatter.Format() 是唯一安全的过滤入口
很多人给 logrus 加 Hook 想删 password 字段,结果日志里还是明文——因为 Hook 在 Format() 之后执行,此时 entry.Data 已转成 JSON 字节或字符串,再删只能正则替换,不可靠还破坏结构。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
真正有效的过滤点只有一个:JSONFormatter.Format() 方法内部,在 entry.Data 还是 map[string]interface{} 时动手。
- 必须深拷贝
entry.Data(make(logrus.Fields)+ 循环赋值),否则并发写入会 panic - 用
delete(data, key)清理平级敏感键,如"auth_token"、"credit_card" - 别直接改
entry.Data,要新建entryCopy并替换其Data,避免影响下游Hook - 这个方案不处理嵌套值(如
user.password),如需递归,得用reflect遍历,但多数服务只需平级过滤就足够
Gin 中间件打的日志为啥还是带敏感字段
因为 Gin 默认访问日志走的是 gin.DefaultWriter,和你配置的 logrus 或 slog 实例完全无关。你在中间件里调 slog.Info("req", "body", c.Request.Body),如果没提前读取并重放 body,c.Request.Body 可能是空的;如果读了但没重设,后续 c.ShouldBindJSON() 就失败。
- 对非 GET/HEAD 请求,先用
http.MaxBytesReader包裹r.Body防 OOM - 用
io.ReadAll(r.Body)读取后,立刻r.Body = io.NopCloser(bytes.NewReader(bodyBytes)) - 同步更新
r.ContentLength = int64(len(bodyBytes))和r.Header.Set("Content-Length", ...) - Header 脱敏要单独做:
r.Header.Get("Authorization")判断前缀,http.ParseCookie解析 Cookie 后按 key 过滤 - Query 参数用
r.URL.Query()获取 map,遍历 key 匹配"token"、"auth"等,再调r.URL.RawQuery = ...更新
zap 结构体字段脱敏必须实现 MarshalLogObject
zap.Any("user", user) 会把 user.Password 原样打出来——zap 不扫描字段名,也不看变量名。唯一可控路径是让结构体实现 zapcore.ObjectMarshaler 接口。
- 必须用导出字段(
Password string,不是password string) - 接收器必须是指针(
func (u *User) MarshalLogObject(enc zapcore.ObjectEncoder) error),否则 nil 结构体 panic - 方法内避免未判空解引用,比如
u.Credentials.Token要先检查u.Credentials != nil - 对敏感字段统一用
enc.AddString("password", "[REDACTED]"),不用空字符串,防止omitempty导致字段消失 - 如果字段是嵌套结构(如
User.Profile),优先手动控制每个字段,不依赖自动反射遍历

















