文章目录

  • 错误包需要具有哪些功能?
    • 1. 应该能支持错误堆栈
    • 2. 能够支持不同的打印格式
    • 3. 能支持 Wrap/Unwrap 功能,也就是在已有的错误上,追加一些新的信息
    • 4. 错误包应该有Is方法
    • 5. 错误包应该支持 As 函数
    • 1.1 常见调用方式
    • 1.2 自定义error方法
      • 1.2.1 函数调用error
    • 1.2.2 自定义Error模板1
    • 1.2 自定义Error模板2
  • 2 errors包
    • 2.1 获取error包
    • 2.2 errors.New()
  • 3 自定义error与errors.New()使用比较

错误包需要具有哪些功能?

1. 应该能支持错误堆栈

我们来看下面一段代码,假设保存在bad.go文件中:

package mainimport ("fmt""log"
)func main() {if err := funcA(); err != nil {log.Fatalf("call func got failed: %v", err)return}log.Println("call func success")
}func funcA() error {if err := funcB(); err != nil {return err}return fmt.Errorf("func called error")
}func funcB() error {return fmt.Errorf("func called error")
}

执行上面的代码:

$ go run bad.go
2021/07/02 08:06:55 call func got failed: func called error
exit status 1

这时我们想定位问题,但不知道具体是哪行代码报的错误,只能靠猜,还不一定能猜到。为了解决这个问题,我们可以加一些 Debug 信息,来协助我们定位问题。这样做在测试环境是没问题的,但是在线上环境,一方面修改、发布都比较麻烦,另一方面问题可能比较难重现。这时候我们会想,要是能打印错误的堆栈就好了。例如:


2021/07/02 14:17:03 call func got failed: func called error
main.funcB/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:27
main.funcA/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:19
main.main/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:10
runtime.main/home/colin/go/go1.16.2/src/runtime/proc.go:225
runtime.goexit/home/colin/go/go1.16.2/src/runtime/asm_amd64.s:1371
exit status 1

通过上面的错误输出,我们可以很容易地知道是哪行代码报的错,从而极大提高问题定位的效率,降低定位的难度。所以,在我看来,一个优秀的 errors 包,首先需要支持错误堆栈。

2. 能够支持不同的打印格式

例如%+v、%v、%s等格式,可以根据需要打印不同丰富度的错误信息。

3. 能支持 Wrap/Unwrap 功能,也就是在已有的错误上,追加一些新的信息

例如errors.Wrap(err, "open file failed") 。Wrap 通常用在调用函数中,调用函数可以基于被调函数报错时的错误 Wrap 一些自己的信息,丰富报错信息,方便后期的错误定位,例如:

func funcA() error {if err := funcB(); err != nil {return errors.Wrap(err, "call funcB failed")}return errors.New("func called error")
}func funcB() error {return errors.New("func called error")
}

这里要注意,不同的错误类型,Wrap 函数的逻辑也可以不同。另外,在调用 Wrap 时,也会生成一个错误堆栈节点。我们既然能够嵌套 error,那有时候还可能需要获取被嵌套的 error,这时就需要错误包提供Unwrap函数。

4. 错误包应该有Is方法

在实际开发中,我们经常需要判断某个 error 是否是指定的 error。在 Go 1.13 之前,也就是没有 wrapping error 的时候,我们要判断 error 是不是同一个,可以使用如下方法:

if err == os.ErrNotExist {// normal code
}

5. 错误包应该支持 As 函数

Go 1.13 之前,没有 wrapping error 的时候,我们要把 error 转为另外一个 error,一般都是使用 type assertion 或者 type switch,也就是类型断言。例如:

if perr, ok := err.(*os.PathError); ok {fmt.Println(perr.Path)
}

但是现在,返回的 err 可能是嵌套的 error,甚至好几层嵌套,这种方式就不能用了。所以,我们可以通过实现 As 函数来完成这种功能。现在我们把上面的例子,用 As 函数实现一下:

var perr *os.PathError
if errors.As(err, &perr) {fmt.Println(perr.Path)
}

这样就可以完全实现类型断言的功能,而且还更强大,因为它可以处理 wrapping error
最后,能够支持两种错误创建方式:非格式化创建和格式化创建。例如:

errors.New("file not found")
errors.Errorf("file %s not found", "iam-apiserver")

上面,我们介绍了一个优秀的错误包应该具备的功能。一个好消息是,Github 上有不少实现了这些功能的错误包,其中github.com/pkg/errors包最受欢迎。所以,我基于github.com/pkg/errors包进行了二次封装。

1.1 常见调用方式

模板

n, err := Foo(0)  if err != nil { //  错误处理
} else { //  使用返回值 n
}

练习1

package mainimport (  "fmt""os"
)func main() {  f, err := os.Open("/test.txt")if err != nil {fmt.Println(err)return}fmt.Println(f.Name(), "opened successfully")
}
[root@localhost error]# go run err3.go
open /test.txt: no such file or directory

1.2 自定义error方法

1.2.1 函数调用error

func Foo(param int)(n int, err error) { // ...
}

1.2.2 自定义Error模板1

type fileError struct {}func (fe *fileError) Error() string {return "文件错误"
}

练习1
模拟一个错误

package mainimport "fmt"type fileError struct {}func (fe *fileError) Error() string {   //自定义会覆盖原来的Error接口return "文件错误"
}//只是模拟一个错误
func openFile() ([]byte, error) {return nil, &fileError{}
}func main() {conent, err := openFile()if err != nil {fmt.Println(err)} else {fmt.Println(string(conent))}
}
[root@localhost error]# go run err1.go
文件错误

1.2 自定义Error模板2

自定义中添加一个字符串

type fileError struct {s string
}func (fe *fileError) Error() string {return fe.s
}

练习2
声明fileError的时候,设置好要提示的错误文字

package mainimport "fmt"type fileError struct {s string
}func (fe *fileError) Error() string {return fe.s
}//只是模拟一个错误
func openFile() ([]byte, error) {return nil, &fileError{"文件错误,自定义"}
}func main() {conent, err := openFile()if err != nil {fmt.Println(err)} else {fmt.Println(string(conent))}
}
[root@localhost error]# go run err2.go
文件错误,自定义

练习3
添加一个时间刻度

package mainimport ("fmt""time"
)type MyError struct {When time.TimeWhat string
}func (e MyError) Error() string {return fmt.Sprintf("%v: %v", e.When, e.What)
}func oops() error {return MyError{time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),"the file system has gone away",}
}func main() { if err := oops(); err != nil { fmt.Println(err) }
}
[root@localhost error]# go run err4.go
1989-03-15 22:30:00 +0000 UTC: the file system has gone away

练习三
没有打开文件报错

package mainimport ("fmt""os"
)type PathError struct {Op   stringPath stringErr  error
}func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error()
} func main() {f, err := os.Open("/test.txt")if errObject, ok := err.(*os.PathError); ok {fmt.Println("错误输出:",err, "文件路径:", errObject.Path)return}fmt.Println(f.Name(), "opened successfully")
}
[root@localhost error]# go run err5.go
错误输出: open /test.txt: no such file or directory 文件路径: /test.txt

2 errors包

2.1 获取error包

go get github.com/pkg/errors

2.2 errors.New()

errors.New()接收合适的错误信息来创建
先声明再使用
l练习1

package mainimport ("errors""fmt"
)var errNotFound error = errors.New("Not found error")func main() {fmt.Printf("error: %v", errNotFound)
}
[root@localhost error]# go run errs1.go
error: Not found error

练习2
函数如何调用err
直接使用

package mainimport ("errors""fmt"
)func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math - square root of negative number")}else {return 1, errors.New("math - square root of 10")}
}func main() {if _, err := Sqrt(-1); err != nil {fmt.Printf("Error: %s\n", err)}
}
[root@localhost error]# go run errs2.go
Error: math - square root of negative number

3 自定义error与errors.New()使用比较

比较自定义error与errors.New()函数根据需求其实各有优点。

package mainimport ("errors""fmt"
)type MsgError struct {Code intMsg  string
}
func (msg *MsgError) Error() string {return fmt.Sprintf("%s", msg.Msg)
}func f1(code int) (int, error) {if code == 1 {return -1, errors.New("msg test error")}return code, nil
}func f2(code int) (int, error) {if code == 1 {return -1, &MsgError{code, "struct msg test error"}}return code, nil
}func main() {for _, v := range []int{1, 2, 3, 4, 5, 6} {if code, err := f1(v); err != nil {fmt.Println(err)} else {fmt.Println("success:", code)}}for _, i := range []int{1, 2, 3} {if code, err := f2(i); err != nil {fmt.Println(err)} else {fmt.Println("success:", code)}}
}
[root@localhost error]# go run errs3.go
msg test error
success: 2
success: 3
success: 4
success: 5
success: 6
struct msg test error
success: 2
success: 3

go error接口与errors包详解相关推荐

  1. 【spring】jar包详解与模块依赖关系

    以spring3.X为例 jar包详解 1. spring-core.jar:包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心: 2. spri ...

  2. spring-jar包详解整理

    Spring各jar包详解 spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar,aspects.jar, spring-portlet.jar, and sprin ...

  3. springframework-jar包详解

    springframework-jar包详解 org.springframework.aop --Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework. ...

  4. android jar 包 意见反馈功能,android重点jar包详解.docx

    android重点jar包详解 深入理解View(一):从setContentView谈起 我们都知道?MVC,在Android中,这个?V?即指View,那我们今天就来探探View的究竟.在onCr ...

  5. java axis2 jar_Java axis2.jar包详解及缺少jar包错误分析

    Java  axis2.jar包详解及缺少jar包错误分析 一.最小开发jar集 axis2 开发最小jar包集: activation-1.1.jar axiom-api-1.2.13.jar ax ...

  6. STM32接口FSMC/FMC难点详解

    STM32接口FSMC/FMC难点详解 转载   http://blog.sina.com.cn/s/blog_808bca130102x94k.html STM32F767的FMC将外部存储器划分为 ...

  7. spring2.0和spring2.5及以上版本的jar包区别 spring jar 包详解

    spring jar 包详解 spring.jar是包含有完整发布的单个jar包,spring.jar中包含除了 spring-mock.jar里所包含的内容外其它所有jar包的内容,因为只有在开发环 ...

  8. java axis2 jar_Java axis2.jar包详解及缺少jar包错误分析

    Java  axis2.jar包详解及缺少jar包错误分析 一.最小开发jar集 axis2 开发最小jar包集: activation-1.1.jar axiom-api-1.2.13.jar ax ...

  9. Spring Boot的每个模块包详解

    Spring Boot的每个模块包详解,具体如下: 1.spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2.spring-boot-s ...

最新文章

  1. uplay服务器未响应,《看门狗:军团》存在崩溃丢失存档BUG 育碧正在修复
  2. 数据挖掘分析的必要性
  3. vs2017 不能加载.vdproj
  4. ESP8266编译脚本
  5. 网络安全比赛理论答题(一)
  6. 覆盖所有类型的javascript深度克隆
  7. 星益云聚合收银台v1.45开源完整版【最终版本】
  8. layui 日期插件onchange事件失效的方法
  9. shell脚本实现Fibonacci数列
  10. 【令人头秃的线段树】线段树入门题目详解(代码逐句分析)
  11. redis设计秒杀活动图解
  12. Tegra environment
  13. building sasl.wrapper extention
  14. OpenCV/kornia/Pillow/Halcon/NI Vision/MIL/*计算机视觉资料汇总
  15. 计算机c盘能分区吗,电脑C盘还可以分盘吗?
  16. 自问自答学ArrayList,看这篇就够了,详解问答
  17. mac下sourcetree设置代理
  18. DirectShow 下载
  19. 厦门故事(三):枫叶随风飘落,重重地摔在了地面上
  20. 网站备案:阿里云-信息系统安全等级保护备案证明及网站服务合同下载

热门文章

  1. 游戏《反恐精英:全球攻势》和《军团要塞2》源代码遭泄露 玩家纷纷卸载
  2. 黄页搜索软件测试,O2O必争之地 国产安卓黄页服务哪家强?
  3. CentOS7.5-1804系统内核升级
  4. 升级Java17问题记录
  5. 红帽linux5.5序列号,Redhat5 Server下载、序列号
  6. [圆方树][树链剖分][set]JZOJ 5909 跑商
  7. 机器学习职业进阶路线
  8. ubuntu重启和关机命令
  9. Java集合类的基本接口
  10. Hive 快速入门(全面)