day 10 - 千万不要放过error

在Go的世界里面, 如果error没接好, 服务就会直接panic了。panic发生在k8s环境中, k8s会帮忙重启, 但如果是在机房的环境底下, 又没有重启的机制..., 你准备好接电话了吗?/images/emoticon/emoticon37.gif

每个服务都要处理error, 包含套件会发生的Scylla error, Redis error, 程序内的资料与逻辑验证error, 以及其他统称为Server error 的错误....等, 这些都需要被收集起来回覆给Client端或是留下纪录debug。

如果把error的处理各自散落在程序码各处一样会有维护性差的状况, 所以可以把Error收在一起, 既能统一输出格式也方便api 接收端排除问题和处理显示格式, 对照错误查找也快。

这边粗略写一下我们平常使用的error内容, 结构里的CodeMsg用来快速判断错误属於哪一类, 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

<<:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 4 - 利用工具录制脚本

>>:  Day18 - 轻前端 Vue - 复杂型别 object

用React刻自己的投资Dashboard Day10 - 用useCallback hook帮你记住函式

tags: 2021铁人赛 React 在Day9说明了useEffect的用法,不过其实当Card...

Dungeon Mizarka 020

魔法设定参考 游戏的设定会将主角设定成初阶的魔法师,故使用魔法是很基本的攻击方式。但这样的考量下其实...

Day12 - Google Kubernetes Engine 基础 - Pod 建置

前言 前一天我们建立好了 Kubernetes 的环境,今天就来实际使用看看,将应用程序透过 Pod...

Day 12 - Length of Last Word

大家好,我是毛毛。ヾ(´∀ ˋ)ノ 废话不多说开始今天的解题Day~ 58. Length of L...

Java学习之路03---标识符、关键字、变数概念

架构图 前言 Java程序是一系列对象的集合,而对象之间透过彼此之间调用方法来达到开发目的,因此在认...