如何优雅的处理错误

1、封装错误error,使其记录错误文件名称、文件路径、行数、操作、错误信息等相关信息。

//封装错误类型,MyError 类型记录了文件,行号,相关的错误信息
type MyError struct {
	File string
	Line int
	Msg  string
}

//PathError 除了底层错误外还提供了使用哪个文件,执行哪个操作等相关信息。
type PathError struct {
	Path string
	Op   string
	Msg  string
}

func (e *MyError) Error() string {
	return fmt.Sprintln("%s:%d: %s", e.File, e.Line, e.Msg)
}

func (e *PathError) Error() string {
	return fmt.Sprintln("%s:%s: %s", e.Path, e.Op, e.Msg)
}

2、错误处理:直接返回,不去判断具体错误。

func fn() error {
    x, err := bar.Foo()
    if err != nil {
        return err
    }
    // use x
}

3、断言错误,可以对不同错误类型进行一些简单的相对应的处理。

比如一些网络连接超时的错误,可以断言这种错误类型,然后代码控制重试操作。

4、将错误信息一层一层的往上打,而不是只简单返回错误error

反例:

func AuthenticateRequest(r *Request) error {
    err := authenticate(r.User)
    if err != nil {
        return err
    }
    return nil
}

错误信息打出去后,仍旧无法定位错误的地方。

正例:

func AuthenticateRequest(r *Request) error {
    err := authenticate(r.User)
    if err != nil {
        return fmt.Errorf("authenticate failed: %v", err)
    }
    return nil
}

5、给错误添加相关信息,使用包 github.com/pkg/errors,里面有两个主要函数,第一个函数是封装函数 Wrap ,输入一个错误和一个信息,生成一个新的错误返回。第二个函数是 Cause ,输入一个封装过的错误,解包之后得到原始的错误信息。

使用这两个函数,我们现在可以给任何错误添加相关信息,并且在我们需要查看底层错误类型的时候可以解包查看。下面的例子是读取文件内容到内存的函数。

func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, errors.Wrap(err, "open failed")
    }
    defer f.Close()

    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, errors.Wrap(err, "read failed")
    }
    return buf, nil
}

当发生错误时,输出结果是:

could not read config: open failed: open /Users/dfc/.settings.xml: no such file or directory

6、错误只处理一次,只在需要作出决定时才处理错误,其他时候忽略。

猜你喜欢

转载自blog.csdn.net/zuoyinlong5434/article/details/81088875