GoChassis 难以遏制的人因差错 -Go 的日志工具之痛

tianxiaoliang · 2020年09月02日 · 最后由 tianxiaoliang 回复于 2020年09月03日 · 184 次阅读

Go 的日志工具之痛

go 生态一直没能有一个强势的日志工具,优秀的日志有 zap,zerolog,但是 API 各有各的设计理念,自然是不兼容的

openlog 和 seclog 的初衷

倒退 5 年,go 的日志工具更加不成熟,我们 folk lager 项目,进行安全整改后成为自己的项目https://github.com/go-chassis/seclog

然而他的老旧的 API 设计已经被我诟病很久。就是以 f 为结尾的函数调用

openlogging.GetLogger().Debugf("shuffler %d %d", i, v)

后来,为了应对以后的需求变化(合规检查),我新建了一个仓库https://github.com/go-chassis/openlog

是一个适配层对下面的日志工具做适配,保证所有工程都只调用此库,不对具体的日志工具实现产生耦合,这样 seclog 可以适配,调用方就与 seclog 解耦了。

然而 “f 类函数” 的问题还没解决,一直期望重构一次,日志工具存在于工程的各个角落,花费了大量时间精力来重构,而最终还会导致用户使用上的不兼容和整改,是否值得?

问题

先来看看原始 issue

https://github.com/go-chassis/go-chassis/issues/889

来看看我们写一个日志用 f 类 API, 省去了 fmt.Sprintf 大大简化啰嗦的语句,开发人员喜欢

openlogging.GetLogger().Debugf("shuffler %d %d", i, v)

然而如果你这么去写,直到编译期也是完全不会有问题的,哪怕上了生产也要等待问题被触发。可想而知,我们会丢失重要的日志信息,甚至无法定位生产环境问题,f 类函数本质上封装了 fmt.Sprintf 方便了开发,却躲过了 IDE 告警和静态检查

openlogging.GetLogger().Debugf("shuffler", i, v)

是的,最大的问题在于事后纠正,而不是事前预防,你可以任意的滥用 format 参数和后面的参数,而无论 IDE,静态检查对此都无能为力,代码合入后,只能在运行时肉眼排查。

我们再来看一个整改后的原形毕露……,不仔细看还真的看不出来 “%s:%s%:s” 这个是有问题的写法,应当是 “%s:%s:%s”

openlog.Error(fmt.Sprintf("can not close client %s:%s%:s, err [%s]", protocol, service, endpoint, err.Error()))

把它放到 IDE 里,可以看到清晰地告警提示,然而你调用 f 类函数不会,关键日志信息就这么的丢掉了

可以以下面的提交为例,我在重构过程中发现很多 f 类方法被滥用

https://github.com/go-chassis/go-archaius/pull/121

由于日益增多的代码,很难只通过人眼 code review 来杜绝错误写法。

本次的重构将解决这类问题,提升代码的质量

总结

拉出来一个 5 年前的老代码进行鞭尸,我们学到了什么

go 简洁的语言,主张不啰嗦,然而这样的简洁,开发人员的喜爱是否是最重要的?还是软件质量最重要?当我提出整改时,也遇到了团队研发的反对,可能除了因为整改成本比较高,更多是觉得 f 类写法极简,毕竟大家都追求写好代码。然而有时候必须对研发说不,可信软件的构建是最重要的。

一段这样的代码可以躲过静态检查,IDE,code reivew,编译,直到进行生产环境,一路畅通。这样类似的问题有多少?最终会引起致命的问题?平时的研发非常忙碌难以顾忌这些问题,作为技术管理者有责任深入编程一线去了解这些潜在的问题。

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio

这种的根据自己的场景来实现一个 linter 就好了吧,很难么

cch123 回复

是的这种方法也可以,但是要维护自己的 linter 插件了,少维护一份代码多好。手段自然不是一种,感谢指出

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册