Golang进阶

Go iota引起的线上事故

2023-07-19  本文已影响0人  qishuai

背景是这样的,前端页面有一个日志level的枚举值传递给后端,server端会对该枚举值的有效性进行校验,QA测试阶段使用了默认的level(也就是Info),并没有测试出这个bug。但是线上业务需要配置一个Debug的level,出现了提交失败的问题(后端校验为非法的枚举值)。示例代码如下:

枚举的定义:

type vlogLevel int

const (
    VlogLevelUnknown = "Unknown"

    VlogLevelDebug vlogLevel = iota
    VlogLevelInfo
    VlogLevelWarn
    VlogLevelError
    VlogLevelFatal
)

// FormatVlogLevel 返回vlog level的语义
func FormatVlogLevel(l int) string {
    switch vlogLevel(l) {
    case VlogLevelDebug:
        return "Debug"
    case VlogLevelInfo:
        return "Info"
    case VlogLevelWarn:
        return "Warn"
    case VlogLevelError:
        return "Error"
    case VlogLevelFatal:
        return "Fatal"
    }

    return VlogLevelUnknown
}

枚举值校验的代码:

if data.FormatVlogLevel(req.LogLevel) == data.VlogLevelUnknown {
    return nil, errors.New("invalid vlog_level value")
}

你可以暂停30s,看看是否发现了问题...

我当时看了好多遍,实在无法发现是哪个地方出现了问题,所以看似简单的问题才是最迷惑的,不知道你有同感没。
最后直接放大招,IDE debug搞起:前端传递Debug的level是使用的是接口文档中约束的枚举: 0,根据我的理解FormatVlogLevel函数应该返回"Debug",然后直接返回了"Unknown";最后添加了万能输出代码: fmt.Println(l) fmt.Println(VlogLevelDebug)。第一个输出是0,第二个输出是1,这。。。。

后来发现IDE给出了常量值的解析结果:

iota
后来猜测可能和前面的那个枚举定义有关,将VlogLevelUnknown = "Unknown"调整到此const(...)域以外,发现结果就符合预期了:
iota

总结:
这种错误在编译环节是不会报错的,而且后期排查起来也非常困难。我尝试google了一下这类错误,发现并没有多少有价值的资料,是个不小心就可能引起大问题的坑。所以归纳几条建议,供大家参考:

参考资料:

上一篇 下一篇

猜你喜欢

热点阅读