在Go的世界裡面, 如果error沒接好, 服務就會直接panic了。panic發生在k8s環境中, k8s會幫忙重啟, 但如果是在機房的環境底下, 又沒有重啟的機制..., 你準備好接電話了嗎?
每個服務都要處理error, 包含套件會發生的Scylla error, Redis error, 程式內的資料與邏輯驗證error, 以及其他統稱為Server error 的錯誤....等, 這些都需要被收集起來回覆給Client端或是留下紀錄debug。
如果把error的處理各自散落在程式碼各處一樣會有維護性差的狀況, 所以可以把Error收在一起, 既能統一輸出格式也方便api 接收端排除問題和處理顯示格式, 對照錯誤查找也快。
這邊粗略寫一下我們平常使用的error內容, 結構裡的Code
跟Msg
用來快速判斷錯誤屬於哪一類, ExtraInfo
則是用來說明錯誤發生當下使用的相關資訊, Time
負責記錄當下時間, Service
用來區分是哪一個服務送出來的錯誤, OriginError
是紀錄原始的錯誤訊息。
lib/error
├── error.go
└── error_test.go
package coconutError
import (
"fmt"
"time"
coconutLog "github.com/evelynocean/coconut/lib/log"
)
type Error struct {
Msg string `json:"msg"`
Code int `json:"code"`
ExtraInfo map[string]interface{} `json:"extrainfo"`
Time int64 `json:"time"`
Service string `json:"service"`
OriginError string `json:"origin_error"`
}
var (
Logger *coconutLog.Logger
// ErrServer 系統錯誤
ErrServer = &Error{Code: 9999, Msg: "ERROR_SERVER", ExtraInfo: make(map[string]interface{})}
// ErrRedis redis 相關
ErrRedis = &Error{Code: 9998, Msg: "ERROR_REDIS", ExtraInfo: make(map[string]interface{})}
)
func init() {
Logger = coconutLog.New()
}
// ParseError error 輸出前加工
func ParseError(e *Error, err error) *Error {
e.Service = "Coconut"
e.Time = time.Now().Unix()
e.OriginError = err.Error()
logMsg := map[string]interface{}{
"service": "Coconut",
"time": time.Now().Unix(),
"origin_error": err.Error(),
"code": e.Code,
"msg": e.Msg,
"extra_info": e.ExtraInfo,
}
Logger.WithFields(logMsg).Errorf(e.Msg)
return e
}
func (t Error) Error() string {
return fmt.Sprintf("[Error %d] %s", t.Code, t.Msg)
}
針對error 的部分寫 go test驗證是否符合預期
package coconutError
import (
"encoding/json"
"errors"
"fmt"
"testing"
)
func TestError(t *testing.T) {
e := ErrServer
e.ExtraInfo = map[string]interface{}{
"aa": 1,
}
testError := ParseError(e, errors.New("測試錯誤格式"))
str, err := json.Marshal(testError)
if err != nil {
t.Errorf("json.Marshal error: %s", err)
}
fmt.Println(string(str))
}
go test 執行狀況, error log輸出時有轉成JSON
的格式, 方便之後的Log資料搜集
-> % go test
{"code":9999,"extra_info":{"aa":1},"fields.msg":"ERROR_SERVER","fields.time":1631846776,"level":"error","msg":"ERROR_SERVER","origin_error":"測試錯誤格式","service":"Coconut","time":"2021-09-17T10:46:16+08:00"}
{"msg":"ERROR_SERVER","code":9999,"extrainfo":{"aa":1},"time":1631846776,"service":"Coconut","origin_error":"測試錯誤格式"}
PASS
ok github.com/evelynocean/coconut/lib/error 0.576s