问题
错误处理,是非常重要的。在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语言中,错误处理的几种方式
- 通过判断值相等。像
io.EOF
,go语言中,称为sentinel error
- 通过断言( type assertion or type switch),判断err的类型或者是否实现了某个接口
- 利用包提供的方法。像
os.IsNotExist
。go语言中,称为ad-hoc check
- 当上面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)
-
正常流程正确,正常流程错误,异常流程错误
正常流程正确,例如:签约接口,正常签约 正常流程错误 同 外部错误,例如:验证手机验证码接口,用户输入错误验证码 异常流程错误 同 内部错误,例如:解约接口,传递了一个不存在的签约号
错误值的序列化和反序列化 + format
所有地方都加,或者是外部包要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 同时存在,后者可以向前者赋值,可能出现混淆。所以,应该保持错误值的一致。
不要忽略错误。
- 错误处理
要显示,要能够从接口中表现出来。如果能够显示出具体有哪些错误,或者错误类型。throw 也要在函数,上做声明。error 显示返回
这个事实是很难的一件事情。代码分层不合理,基本是做不到的。相当于只应该在流程层(逻辑层或者service层)才能赋予错误码
保持一个service一个文件夹,一个接口一个文件
有疑问加站长微信联系(非本文作者)