Golang中错误包装的最佳实践探讨

Golang中错误包装的最佳实践探讨 在什么情况下为错误添加额外信息包装是有意义的?

例如,GetObjectX() 进行 REST 调用或数据库调用,该调用返回一个对象和错误。如果该错误不为 nil,您是直接返回它,还是用以下方式包装它:

errors.Wrap(err, "Could not get an answer from API")

请提供建议。

2 回复

JOhn_Stuart:

用额外信息包装一个错误有意义吗?

例如,GetObjectX() 进行了一个 REST 调用或数据库调用,该调用返回一个对象和错误。如果该错误不为 nil,你是直接返回它,还是用以下方式包装它:

嘿!

在我们工作的地方,我们基本上会用额外的信息包装所有的错误。这在查看错误时非常有用,你可以看到错误产生的完整路径。话虽如此,如果你的函数只有一个地方会返回错误,那么包装最“底层”的错误有时似乎没有意义。

更多关于Golang中错误包装的最佳实践探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中,错误包装的最佳实践取决于是否需要为错误添加上下文信息,以便于调试和错误追踪。以下是几种常见场景及示例代码:

1. 在多层调用中添加上下文信息

当错误从底层传递到上层时,如果缺乏上下文,调试会变得困难。此时包装错误是有意义的。

import "github.com/pkg/errors"

func GetObjectX() (*Object, error) {
    data, err := fetchFromAPI()
    if err != nil {
        return nil, errors.Wrap(err, "Could not get an answer from API")
    }
    return parseObject(data)
}

func fetchFromAPI() ([]byte, error) {
    // 模拟API调用失败
    return nil, errors.New("connection timeout")
}

2. 保留原始错误类型和堆栈信息

使用errors.Wrap可以保留原始错误的类型和堆栈信息,便于后续的错误类型判断或日志记录。

func ProcessRequest() error {
    if err := validateInput(); err != nil {
        return errors.Wrap(err, "invalid input")
    }
    return nil
}

func validateInput() error {
    return &ValidationError{Field: "username", Reason: "too short"}
}

3. 需要错误链检索的场景

当需要检查错误链中是否存在特定错误类型时,包装错误提供了灵活性。

import "github.com/pkg/errors"

func HandleError(err error) {
    if errors.Is(err, &ValidationError{}) {
        // 处理验证错误
    }
    if errors.Is(err, sql.ErrNoRows) {
        // 处理数据库无记录错误
    }
}

4. 直接返回原始错误的场景

如果错误已经包含足够的信息,或者上层不需要额外上下文,可以直接返回。

func ReadConfig() (*Config, error) {
    data, err := os.ReadFile("config.yaml")
    if err != nil {
        // 文件读取错误本身已包含文件路径信息
        return nil, err
    }
    return parseConfig(data)
}

5. 使用标准库的fmt.Errorf进行简单包装

Go 1.13+ 支持使用%w动词包装错误,适用于不需要第三方库的场景。

func GetUser(id string) (*User, error) {
    user, err := db.FindUser(id)
    if err != nil {
        return nil, fmt.Errorf("find user %s: %w", id, err)
    }
    return user, nil
}

总结:当错误需要添加上下文信息以帮助调试、需要保留错误链供后续检查,或在多层调用中传递时,包装错误是有意义的。反之,如果错误信息已经足够清晰,或不需要额外上下文,可以直接返回原始错误。

回到顶部