Filter 必须在日志写入前拦截敏感字段,利用 log.Record.Fields 的 Key 精准匹配白名单(如 phone、id_card),按类型安全脱敏,特别需显式处理 error 字段中的敏感信息。

Filter 必须在日志写入前拦截,不能靠业务层手动删字段
Kratos 的 logger.Info 或 logger.Error 一旦传入含 phone、id_card 的结构体,原始值就可能直接落盘。你不能指望在每个 log.With().String("phone", u.Phone) 前加一层 maskPhone(u.Phone) —— 这不仅漏掉 error stack 里的敏感上下文,还会破坏结构化日志的字段语义。
正确时机只有一个:Filter 是日志链路中唯一能访问 log.Record.Fields 并原地修改的环节。它在序列化为字符串之前执行,对所有日志调用生效,无需改业务代码。
- 别在
Helper层做字符串拼接脱敏(如"user: " + maskPhone(u.Phone)),会丢失字段名和类型信息 - 别在
Valuer里尝试脱敏——它只返回全局值,收不到当前日志的Fields - 别重写
Logger实现——那等于自己实现一套日志序列化逻辑,维护成本极高
白名单字段匹配比正则扫描更安全可靠
用 strings.ReplaceAll(msg, "138", "138****") 这类全局文本替换,极易误伤:订单号、traceID、IP 段都可能以 138 开头。Kratos 的 log.Record.Fields 是 []log.Field 切片,每个 Field 明确携带 Key 和 Value,应基于字段名做精准控制。
典型白名单定义:[]string{"phone", "id_card", "email", "bank_card"}。遍历 record.Fields 时只检查 field.Key 是否命中,命中后才调用对应掩码函数处理 field.Value。
- 同名但语义不同的字段(如
order_id)完全跳过,不参与脱敏 - 字段值类型需保持一致:若原值是
int64,掩码后也应转为int64(如掩码成0),避免类型断言 panic - 不要在 Filter 中做非确定性操作:比如去空格、删括号、格式标准化——那是业务层该干的事
Kratos 2.8.2 的 Filter 实现要注意字段类型和生命周期
Kratos 2.8.2 中 log.Field.Value 是 interface{},常见类型包括 string、int64、bool、time.Time,甚至嵌套结构体。直接调 field.Value.(string) 会 panic。
实操建议用类型断言+默认兜底:
switch v := field.Value.(type) {
case string:
if isSensitiveKey(field.Key) {
field.Value = maskString(v)
}
case int64:
if isSensitiveKey(field.Key) && field.Key == "phone" {
// 仅对明确语义的数字字段脱敏,如手机号存为 int64(少见但存在)
field.Value = int64(0)
}
default:
// 其他类型不处理,避免误改
}
- 不要对
error类型字段直接调.Error()后脱敏——error 可能含堆栈,字段名不是error就是err,需单独判断 - Filter 函数是无状态的,不要在其中缓存或修改外部变量,避免 goroutine 竞态
- 如果字段值是结构体指针(如
*User),Filter 不会自动递归遍历——需业务层提前展开或改用log.Object显式传入
error 上下文里的敏感数据最容易被忽略
业务代码里常写 logger.Error(err, "failed to create user"),而 err 本身可能封装了用户输入(比如数据库错误里带了原始 SQL 参数)。Kratos 的 log.Record 会把 err 作为 error 字段塞进 Fields,但它的 Value 是 error 接口,不是字符串。
必须显式处理 error 字段:
- 检查
field.Key == "error" || field.Key == "err" - 对
field.Value做err, ok := field.Value.(error)断言 - 若
ok为真,调用err.Error()得到字符串后,再按白名单规则提取并脱敏其中的已知字段(如从 message 里正则匹配手机号)——这是少数需要局部文本扫描的场景 - 注意:不要对整个
err.Error()做全局替换,只提取并替换明确字段值部分
这个点没覆盖,脱敏就等于没做——因为绝大多数真实泄露都发生在 error 日志里。


















