如果用户类型实现了方法
String() string
则在使用该值的fmt函数中(例如,fmt.Println,fmt.Printf("%v"),log包)会调用该方法以获取字符串表示。
应该为用户实体类型实现String(),以便获得有意义的可读输出,而不是fmt的标准输出(%v)。建议使输出简洁,符合使用上下文,并在可能的情况下保持安全(不泄露敏感数据)。
实现示例:
type Order struct { ID string Amount int } func (o Order) String() string { return fmt.Sprintf("Order[%s]: %d", o.ID, o.Amount) } func main() { o := Order{"A123", 99} fmt.Println(o) } // 输出: Order[A123]: 99
如果方法实现得不当,会导致日志中出现无意义或危险的输出,从而使错误排查变得困难。
如果您的类型实现了String()方法,但您将其作为指针而不是值使用,会调用String()吗?
回答: 如果指针类型实现了String(),则将调用该方法。如果仅在值类型上实现了,但在代码中使用的是指针类型,则由于Go对没有指针接收者的方法进行自动解引用,方法会被调用。通常这不是问题,但如果该方法仅在指针接收者上实现,而您为值调用它,则会出现编译错误。
示例:
type X struct{} func (x *X) String() string { return "ptr" } fmt.Println(X{}) // 错误:X未实现String fmt.Println(&X{}) // 正确,将调用该方法
故事
在一个大型项目中,错误类型仅在指针接收者上实现了String()(
func (err *MyErr) String() string)。因此,在日志中返回(非指针)错误值时输出的无用信息{},而不是有用的信息。这个bug长期未被发现。
故事
其中一个类型存储了敏感数据,在错误实现String()时(明文输出字段)用户密码开始出现在生产日志中。进行了安全审计。
故事
使用自动生成的String()导致在更新结构时,新的输出格式与之前的不匹配,日志变得不可读,第三方系统的日志解析受到影响。