golang错误处理

问题

错误处理,是非常重要的。在go语言中,错误处理被设计的十分简单。
如果做得好,会在排查问题等方面很有帮助;如果做得不好,就会比较麻烦。

请求接口拿到一个错误,却并不知道错在哪里。然后,还需要找人看,然后拿到足够的上下文。所以,错误处理做不好,就意味着效率低下(团队越大,损耗越大)。
开发环境,要返回足够能够排查问题的错误信息。

从1.0开始,go中定义错误为 error 接口

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
    Error() string
}

go语言中,错误处理的几种方式

  1. 通过判断值相等。像 io.EOF,go语言中,称为 sentinel error
  2. 通过断言( type assertion or type switch),判断err的类型或者是否实现了某个接口
  3. 利用包提供的方法。像 os.IsNotExist。go语言中,称为 ad-hoc check
  4. 当上面3中方式不可用时,通过搜索 err.Error() 是否包含特定字符串。(不被推荐)
  • 尽量不要使用 sentinel error
  • 尽量不要使用 ad-hoc check

错误值

go-grpc 中,定义错误值为:

message Status {
  // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].
  int32 code = 1;

  // A developer-facing error message, which should be in English. Any
  // user-facing error message should be localized and sent in the
  // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.
  string message = 2;

  // A list of messages that carry the error details.  There will be a
  // common set of message types for APIs to use.
  repeated google.protobuf.Any details = 3;
}

stack trace

在排查一个错误时,第一步通常是依据运行的流程,找到出错的地方,然后是看为什么出错。
这里,stack trace 对于查看问题有很大帮助。

而不是每个产生错误的地方都打印日志。先这样打日志比较费劲,查看问题的时候,也很难找到关键的信息。

凡是返回 error 的地方,返回一个合适的error。

关于性能
在业务开发中,并不需要可以关注这点。

message

在原有的错误 err.Error() 追加描述信息。这个一般是用来描述错误的原因。并产品级别提供给用户看的文案信息等同。这也意味着,在生产环境下,这个字段不应该对外展示(最好是,服务端不对外暴露)。

code

code 对应一种外部错误,和错误文案对应。(文案的多语言,在业务中间件中,统一依据 code + lang 获取文案信息)

details

如果在需要的情况下,增加details信息,暴露更多的错误细节。
details 尽量只用来排查信息,而不要用来逻辑判断。如果要用,采用 code

错误链

Unwarp

// Is reports whether err or any of the errors in its chain is equal to target.
func Is(err, target error) bool

// As checks whether err or any of the errors in its chain is a value of type E.
// If so, it returns the discovered value of type E, with ok set to true.
// If not, it returns the zero value of type E, with ok set to false.
func As(type E)(err error) (e E, ok bool)

上游,下游,本服务

正常流程正确,正常流程错误,异常流程,异常(nil)

  1. 正常流程正确,正常流程错误,异常流程错误

     正常流程正确,例如:签约接口,正常签约
     正常流程错误 同 外部错误,例如:验证手机验证码接口,用户输入错误验证码
     异常流程错误 同 内部错误,例如:解约接口,传递了一个不存在的签约号
    

错误值的序列化和反序列化 + format

type Resp struct {
    Code    int           `json:"code"`
    Message string        `json:"message"`
    NodeId  string        `json:"node_id"` // or cluster+ip 在分布式的情况下,比较容易处理错误
    TraceId string        `json:"trace_id"`
    Details []interface{} `json:"details,omitempty"` // 依据系统可选
    Data    interface{}   `json:"data,omitempty"`
}

所有地方都加,或者是外部包要Wrap一下

参考资料

https://go.googlesource.com/proposal/+/master/design/go2draft.md
https://godoc.org/golang.org/x/xerrors
https://github.com/pkg/errors 一个很好的包

备注

error consts.CaaError 同时存在,后者可以向前者赋值,可能出现混淆。所以,应该保持错误值的一致。

不要忽略错误。

  1. 错误处理

要显示,要能够从接口中表现出来。如果能够显示出具体有哪些错误,或者错误类型。throw 也要在函数,上做声明。error 显示返回

这个事实是很难的一件事情。代码分层不合理,基本是做不到的。相当于只应该在流程层(逻辑层或者service层)才能赋予错误码
保持一个service一个文件夹,一个接口一个文件

TODO

下游服务的接口,如果能够明确定义出,可能返回的code有哪些。上游服务,才好完整性的处理相应的(上游关心的)code

像余额不足

那么怎么才能比较好的,完整定义一个接口的所有code呢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • error code(错误代码)=0是操作成功完成。error code(错误代码)=1是功能错误。error c...
    Heikki_阅读 3,457评论 1 9
  • 为了方便分享,特地把blog抄到这里 = =实在是对代码中到处打印错误日志的现象难以忍受, 于是琢磨了一个优雅一些...
    mozhata_cf7b阅读 3,465评论 3 10
  • error code(错误代码)=2000是无效的像素格式。error code(错误代码)=2001是指定的驱动...
    Heikki_阅读 1,874评论 0 4
  • 环境搭建 Golang在Mac OS上的环境配置 使用Visual Studio Code辅助Go源码编写 VS ...
    陨石坠灭阅读 5,794评论 0 5
  • 一、数据类型转换 https://studygolang.com/articles/10838 package m...
    蓓蓓的万能男友阅读 1,106评论 0 1