Golang在错误处理上,没有形成良好的规范,导致真正用好的人非常少,大部分golang开发人员(哪怕是3年+)在错误处理上,依旧无法避免以下问题:
err.Elem("用户模块").Text("用户查询信息异常").Stack(debug.Stack()).Attach(map[string]interface{
}{
"url": c.FullPath(),
"param": param,
})
control/login.go
func Login(c *gin.Context) error {
if e:=service.Login(userId);e!=nil {
logging.Println(e)
return
}
}
service/login.go
func Login(userId int) error {
if e:= dao.Login(userId);e!=nil {
logging.Println(e)
return e
}
return nil
}
runtime/debug.Stack(0x7deb80, 0xc000006018, 0xc000063f58)
E:/go1.12/src/runtime/debug/stack.go:24 +0xa4
github.com/fwhezfwhez/errorx.TestSe(0xc0000ca100)
G:/go_workspace/GOPATH/src/errorX/errorx_test.go:334 +0x33b
testing.tRunner(0xc0000ca100, 0x78aad8)
E:/go1.12/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
E:/go1.12/src/testing/testing.go:916 +0x361
testing.tRunner(0xc0000ca100, 0x78aad8)
E:/go1.12/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
E:/go1.12/src/testing/testing.go:916 +0x361
testing.tRunner(0xc0000ca100, 0x78aad8)
E:/go1.12/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
E:/go1.12/src/testing/testing.go:916 +0x361
testing.tRunner(0xc0000ca100, 0x78aad8)
E:/go1.12/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
E:/go1.12/src/testing/testing.go:916 +0x361
testing.tRunner(0xc0000ca100, 0x78aad8)
E:/go1.12/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
if e !=nil {
if e == loginService.LoginPasswordWrongErr {
c.JSON(200, gin.H{
"errmsg":e.Error(), "errcode":1})
return
}
if e == loginService.LoginInvalidUsernameErr {
c.JSON(200, gin.H{
"errmsg":e.Error(), "errcode":2})
return
}
if e == loginService.LoginFrequencyErr {
c.JSON(200, gin.H{
"errmsg":e.Error(), "errcode":3})
return
}
...
c.JSON(200, gin.H{
"errmsg":"系统错误", "errcode":10005})
return
}
func PlayGame() {
e := handle1()
if e!=nil {
err.SaveErr(e, map[string]interface{
}{
"label":"xyx:game",
"elem": "game"
})
e = handle2()
if e!=nil {
err.SaveErr(e, map[string]interface{
}{
"label":"xyx:game",
"elem": "game"
})
e = handle3()
if e!=nil {
err.SaveErr(e, map[string]interface{
}{
"label":"xyx:game",
"elem": "game"
})
e = handle4()
if e!=nil {
err.SaveErr(e, map[string]interface{
}{
"label":"xyx:game",
"elem": "game"
})
e = handle5()
if e!=nil {
err.SaveErr(e, map[string]interface{
}{
"label":"xyx:game",
"elem": "game"
})
}
除此之外呢,还有一些:
本次,将对上述的1-5问题,提供有效的解决方案。对6-8问题,提供技术方向。
在使用时,会接入项目里的错误报警机制,每一个需求/模块,会通过【代码生成】提供附加链路的方法包。
---login
| -- loginModel
| -- loginService
| -- loginControl
| -- loginRouter
| -- loginUtil
| |--error.go
error.go
func SaveError(e error, ctx ...map[string]interface{
}) {
if len(ctx) == 0 {
ctx = []map[string]interface{
}{
{
"label": "xyx:login",
"elem": "xyx:game",
},
}
} else {
ctx[0]["label"] = "xyx:login"
ctx[0]["elem"] = "xyx:game"
}
errs.SaveError(errorx.Wrap(e), ctx...)
}
每个错误处理的顶层,只需要调用
xxxUtil.SaveError(errorx.Wrap(e))
if e:= a.handle();e!=nil {
return errorx.Wrap(e)
}
func loginwrap() error {
e := fmt.Errorf("time out")
return errorx.Wrap(e)
}
func main() {
if e:= loginwrap() {
fmt.Println(errorx.Wrap(e).Error())
return
}
}
输出:
/x/x/x/x/main:15 | time out
/x/x/x/x/main:9 | time out
func Login() error {
return errorx.NewServiceError("登录密码错误",1)
// return errorx.NewServiceError("账户重复",2)
// return errorx.NewServiceError("登录频繁",3)
}
func main() {
e := Login()
if se,ok := errorx.IsServiceError(e); ok {
fmt.Println(se.Errmsg, se.Errcode)
}
}
和第一点类似,通过【自动生成】的错误处理包来自动打上需求和模块标签,业务中只需要顶层处理就好了
err.SaveError(errorx.Wrap(e))
需要对错误提供上报机制,应用组统一上报到同一个数据库(通过上报方限频,mq异步限制消费速率,数据库hash代理,来保证数据库稳定)。
对错误信息提供后台接口查询,避免上服务器查询。
同6
需要对每个标签的每条任务都做好存储,期限最好保留7天以上,并且,对订制标签需要做到报警机制。