应避免用 reflect 直接提取日志字段,因其存在字段顺序不定、私有字段不可读、嵌套/指针易 panic、特殊类型输出不可读等问题;推荐实现 logfields 接口由业务自主控制日志内容,兼顾安全性、可读性与编译期检查。
为什么 reflect 不该直接用于日志字段提取
直接用 reflect 从结构体获取字段名+值来打日志,看似“通用”,实则容易踩坑:字段顺序不保证、私有字段无法读取、嵌套结构体或指针会 panic、time.Time 或 json.RawMessage 等类型输出不可读。真正需要的是可控的、可调试的日志上下文,而不是“把整个 struct 扔进 fmt.Printf”。
用 LogFields 接口替代反射遍历
定义一个轻量接口,让业务结构体自己决定哪些字段参与日志,避免反射黑盒:
type LogFields interface {
LogFields() map[string]interface{}
}
使用时只需实现该方法:
func (u User) LogFields() map[string]interface{} {
return map[string]interface{}{
"user_id": u.ID,
"email": u.Email,
"role": u.Role,
}
}
日志封装函数即可统一处理: