go error实践

lilei · · 308 次点击 · · 开始浏览    

# go error实践 相关代码地址,[代码地址](https://github.com/luxun9527/go-lib/tree/master/utils/errors),如果觉得对你有帮助,欢迎给我的 GitHub 仓库点个 Star ⭐!你的支持是我持续改进和发布更多优质内容的动力。感谢你的关注和支持! ## 1、error相关api https://www.cnblogs.com/YLTFY1998/p/16741285.html ### is/as ``` func Is(err, target error) bool ``` 判断两个错误是否是一样的,如果错误有wrap,会递归unwrap,拿到原始错误再比较。如果错误有实现Is()接口,会调用来比较 ``` func As(err error, target interface{}) bool ``` errors.As 的用法,判断err能否转为指定类型。如果能则转换, 接受两种类型,error和interface类型,interface类型会被反射直接赋值,一定能转。error类型,会执行第一个参数自定义的as方法如果有的话。 ### wrap/unwrap ``` github.com/pkg/errors ``` errors.Wrap()/fmt.Errorf() 使用这两个方法,可以给err包装一层,给error提供更多的信息。使用Unwrap可以获取原始的错误。 **以下是示例代码** custom_error.go ```protobuf package error import ( "errors" "fmt" ) type CustomError struct { Code int32 Message string } // 自定义错误类型需要实现error接口 func (e CustomError) Error() string { return e.Message } func (e CustomError) String() string { return fmt.Sprintf(`{code:"%v",message:"%v"}`, e.Code, e.Message) } // errors.Is()会调用这个方法来判断是否是同一个错误类型 func (e CustomError) Is(err error) bool { var customError CustomError if ok := errors.As(err, &customError); !ok { return false } return e.Code == customError.Code } // 提取指定类型的错误,判断包装的 error 链中,某一个 error 的类型是否与 target 相同,并提取第一个符合目标类型的错误的值,将其赋值给 target。 func (e CustomError) As(target interface{}) bool { t, ok := target.(**E) if ok { *t = &E{} } return true } // 解析错误,获取原始错误 func (e CustomError) Unwrap() error { return e } func NewCustomError(code int32, message string) CustomError { return CustomError{ Code: code, Message: message, } } type E struct { } func (E) Error() string { return "E type error" } ``` ```protobuf package error import ( "github.com/pkg/errors" "log" "testing" ) func TestCustomError(t *testing.T) { err := NewCustomError(400, "Invalid request parameters") log.Printf("Error: %v", err) //Error: Invalid request parameters //参考https://www.cnblogs.com/YLTFY1998/p/16741285.html //=================================================演示errors.IS的用法比较错误是否相同============================================= err1 := NewCustomError(400, "Invalid request parameters") err2 := NewCustomError(400, "Invalid request parameters1") log.Printf("Error: %v", errors.Is(err2, err1)) //=====================================================演示errors.As错误装换================================================= //err3 := errors.New("invalid request parameters") err4 := NewCustomError(400, "Invalid request parameters") var err5 *E //errors.As 的用法,判断err能否转为指定类型。 //接受两种类型,error和interface类型,interface类型会被反射直接赋值,error类型,会执行第一个参数自定义的as方法如果有的话。 //将错误转为另一种错误类型。 r2 := errors.As(err4, &err5) log.Printf("as result r2: %v err: %v", r2, err5) //2024/05/18 23:06:55 as result r2: true err: E type error //演示将错误转换为interface类型,i会被反射赋值。 var i interface{} r3 := errors.As(err4, &i) log.Printf("as result r3: %v err: %v", r3, i) //2024/05/18 23:06:55 as result r3: true err: Invalid request parameters //==========================================wrap用法================================================== //wrap给错误包装了一层堆栈,errors.Unwrap 可以获取原始错误 err6 := NewCustomError(500, "internal error") //使用fmt.Errorf有同样的效果,errors.Is 和 errors.As都是会逐层unwrap的来判断原始错误。 err7 := errors.Wrap(err6, "wrap error") log.Printf("Error: err7 %v", err7) //2024/05/18 23:06:55 Error: err7 wrap error: internal error r4 := errors.Is(err7, err6) log.Printf("result r3 = %v", r4) //2024/05/18 23:16:11 result r3 = true } ``` ## 2、go error实践 **以下是我在个人在日常开发中觉得比较好的实践。** 1、预定义错误使用grpc的status来预定义,预定义错误,错误码按照模块区分。api和rpc的业务错误都使用预定的grpc status错误,这样api不用关心grpc的错误直接抛出就可以,在api统一处理。 2、后端的grpc返回业务错误的时候使用error来返回,而不是在message中定义code,message的形式 3、多语言场景,翻译在api层统一对错误处理。如果需要翻译支持动态修改,修改无需重启程序可以使用fsnotify,etcd,nacos来实现。 4、错误统一存放,统一格式,最好使用工具生成。如go:generate 加上stringer,如果不能满足自己的需求可以使用ast包来自己自定义。 ```go package error import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type Code codes.Code func (c Code) Translate(lang string) string { d, ok := _m[lang] if !ok { d = _m["zh-CN"] //默认中文 } result, ok := d[c] if !ok { return "服务器繁忙,请稍后再试" } return result } func (c Code) Error(msg string) error { return status.New(codes.Code(c), msg).Err() } var _m = map[string]map[Code]string{ "zh-CN": { UserNotFoundCode: "用户不存在", InternalCode: "服务器繁忙,请稍后再试", }, } //go:generate errgen -p common.go package error const ( CommonCodeInit Code = 100000 * (iota + 1) AccountCodeInit ) const ( InternalCode = CommonCodeInit + iota + 1 RedisErrCode ExecSqlFailedCode ParamValidateFailedCode RecordNotFoundErrCode DuplicateDataErrCode MongoErrCode KafkaErrCode EtcdErrCode DtmErrCode //未知错误 如果没有多语言的需求,且不出文件中加载错误文案, //可以使用ast解析注释的方式,将注释解析为文案。 PulsarErrCode ) var ( Internal = InternalCode.Error("") RedisErr = RedisErrCode.Error("") ExecSqlFailed = ExecSqlFailedCode.Error("") ParamValidateFailed = ParamValidateFailedCode.Error("") RecordNotFoundErr = RecordNotFoundErrCode.Error("") DuplicateDataErr = DuplicateDataErrCode.Error("") MongoErr = MongoErrCode.Error("") KafkaErr = KafkaErrCode.Error("") EtcdErr = EtcdErrCode.Error("") DtmErr = DtmErrCode.Error("") PulsarErr = PulsarErrCode.Error("未知错误") ) ```

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

308 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传