Go iota引起的線上事故

背景是這樣的,前端頁面有一個日志level的枚舉值傳遞給后端,server端會對該枚舉值的有效性進(jìn)行校驗(yàn),QA測試階段使用了默認(rèn)的level(也就是Info),并沒有測試出這個bug。但是線上業(yè)務(wù)需要配置一個Debug的level,出現(xiàn)了提交失敗的問題(后端校驗(yàn)為非法的枚舉值)。示例代碼如下:

枚舉的定義:

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
}

枚舉值校驗(yàn)的代碼:

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

你可以暫停30s,看看是否發(fā)現(xiàn)了問題...

我當(dāng)時看了好多遍,實(shí)在無法發(fā)現(xiàn)是哪個地方出現(xiàn)了問題,所以看似簡單的問題才是最迷惑的,不知道你有同感沒。
最后直接放大招,IDE debug搞起:前端傳遞Debug的level是使用的是接口文檔中約束的枚舉: 0,根據(jù)我的理解FormatVlogLevel函數(shù)應(yīng)該返回"Debug",然后直接返回了"Unknown";最后添加了萬能輸出代碼: fmt.Println(l) fmt.Println(VlogLevelDebug)。第一個輸出是0,第二個輸出是1,這。。。。

后來發(fā)現(xiàn)IDE給出了常量值的解析結(jié)果:

iota

后來猜測可能和前面的那個枚舉定義有關(guān),將VlogLevelUnknown = "Unknown"調(diào)整到此const(...)域以外,發(fā)現(xiàn)結(jié)果就符合預(yù)期了:
iota

總結(jié):
這種錯誤在編譯環(huán)節(jié)是不會報(bào)錯的,而且后期排查起來也非常困難。我嘗試google了一下這類錯誤,發(fā)現(xiàn)并沒有多少有價(jià)值的資料,是個不小心就可能引起大問題的坑。所以歸納幾條建議,供大家參考:

  • iota前不要定義任何常量值
  • 如果有必要請直接使用字面量直接初始化常量值
  • 編寫單元測試,確保枚舉值和預(yù)期嚴(yán)格一致

參考資料:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 開發(fā)環(huán)境 GOROOT: go的安裝目錄, go原生的工具在該目錄下GOPATH: 通常存放自己開發(fā)的代碼或第三方...
    Never_Yg閱讀 426評論 0 0
  • Go語言數(shù)據(jù)類型 Go語言本質(zhì)是用C語言編寫的一套高級開發(fā)語言, 所以Go語言中的數(shù)據(jù)類型大部分都是由C語言演變而...
    極客江南閱讀 992評論 0 6
  • 第4天iota關(guān)鍵字 package main import "fmt" func main() { /* i...
    3天時間閱讀 473評論 0 0
  • 一、常量概述 * 常量是一個固定值,在編譯器就確定結(jié)果.聲明時必須賦值且結(jié)果不可以改變. * 因?yàn)槌A吭诰幾g器就確...
    小陳工閱讀 396評論 0 1
  • 能力模式 選擇題 【初級】下面屬于關(guān)鍵字的是()A. funcB. defC. structD. class 參考...
    靈魂深靈閱讀 5,386評論 2 5

友情鏈接更多精彩內(nèi)容