golang标准库errors替代方案与错误处理增强插件库errors的使用

Golang标准库errors替代方案与错误处理增强插件库errors的使用

Emperror/errors是一个功能强大的Golang错误处理库,它结合了标准库errors和github.com/pkg/errors的特性,并添加了许多增强功能。

主要特性

标准库特性

  • New 创建带有堆栈跟踪的错误
  • Unwrap 支持Go 1.13包装器接口和pkg/errors的causer接口
  • 向后兼容的IsAs函数

github.com/pkg/errors特性

  • New, Errorf, WithMessage, WithMessagef, WithStack, Wrap, Wrapf函数行为与原库相同
  • Cause支持两种错误解包接口

额外特性

  • NewPlain 创建无上下文(如堆栈跟踪)的错误
  • Sentinel 创建常量错误的简写类型
  • WithStackDepth 允许自定义调用深度附加堆栈跟踪
  • 条件性附加堆栈跟踪的函数
  • 多错误聚合
  • 添加键值对到错误的函数
  • match包用于错误匹配

安装

go get emperror.dev/errors

使用示例

基本用法

package main

import "emperror.dev/errors"

// ErrSomethingWentWrong 是一个哨兵错误,适用于单个API层
const ErrSomethingWentWrong = errors.Sentinel("something went wrong")

// ErrMyError 是可以从公共API返回的错误
type ErrMyError struct {
	Msg string
}

func (e ErrMyError) Error() string {
	return e.Msg
}

func foo() error {
	// 为哨兵错误附加堆栈跟踪
	return errors.WithStack(ErrSomethingWentWrong)
}

func bar() error {
	return errors.Wrap(ErrMyError{"something went wrong"}, "error")
}

func main() {
	if err := foo(); err != nil {
		if errors.Cause(err) == ErrSomethingWentWrong { // 或者使用 errors.Is(ErrSomethingWentWrong)
			// 处理错误
		}
	}

	if err := bar(); err != nil {
		if errors.As(err, &ErrMyError{}) {
			// 处理错误
		}
	}
}

错误匹配

package main

import (
    "emperror.dev/errors"
    "emperror.dev/errors/match"
)

// ErrSomethingWentWrong 是一个哨兵错误
const ErrSomethingWentWrong = errors.Sentinel("something went wrong")

type clientError interface{
    ClientError() bool
}

func foo() error {
	// 为哨兵错误附加堆栈跟踪
	return errors.WithStack(ErrSomethingWentWrong)
}

func main() {
    var ce clientError
    matcher := match.Any{match.As(&ce), match.Is(ErrSomethingWentWrong)}

	if err := foo(); err != nil {
		if matcher.MatchError(err) {
			// 可以使用匹配器编写复杂的错误处理条件
		}
	}
}

开发

欢迎贡献代码!

  1. 克隆仓库
  2. 在新分支上修改
  3. 运行测试套件:
./pleasew build
./pleasew test
./pleasew gotest
./pleasew lint
  1. 提交、推送并创建PR

许可证

MIT许可证。详细信息请查看许可证文件。

部分代码受第三方库启发(或完全复制),它们的许可证可以在第三方许可证文件中找到。


更多关于golang标准库errors替代方案与错误处理增强插件库errors的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang标准库errors替代方案与错误处理增强插件库errors的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 错误处理增强方案与替代库

在 Go 语言中,标准库的 errors 包提供了基本的错误处理功能,但随着项目复杂度增加,开发者往往需要更强大的错误处理能力。以下是几种流行的替代方案和增强插件库。

1. pkg/errors 库

pkg/errors 是最流行的 errors 替代库之一,提供了错误堆栈跟踪和错误包装功能。

import (
	"github.com/pkg/errors"
	"fmt"
)

func main() {
	err := process()
	if err != nil {
		fmt.Printf("%+v\n", err) // 打印完整堆栈
	}
}

func process() error {
	err := readFile()
	if err != nil {
		return errors.Wrap(err, "process failed") // 包装错误并添加上下文
	}
	return nil
}

func readFile() error {
	return errors.New("file not found") // 创建新错误
}

主要特性:

  • Wrap() 包装错误并添加上下文信息
  • WithStack() 添加调用堆栈
  • Cause() 获取原始错误
  • %+v 格式化打印完整堆栈信息

2. hashicorp/go-multierror

当需要处理多个错误时,go-multierror 非常有用。

import (
	"github.com/hashicorp/go-multierror"
	"errors"
)

func main() {
	var result error
	
	// 模拟多个操作,每个都可能失败
	for i := 0; i < 3; i++ {
		if err := doTask(i); err != nil {
			result = multierror.Append(result, err)
		}
	}
	
	if result != nil {
		fmt.Println(result) // 打印所有错误
	}
}

func doTask(i int) error {
	return errors.New(fmt.Sprintf("task %d failed", i))
}

3. uber-go/multierr

Uber 的 multierr 是另一个处理多个错误的库,API 更现代化。

import (
	"go.uber.org/multierr"
	"errors"
)

func main() {
	var err error
	
	err = multierr.Append(err, errors.New("first error"))
	err = multierr.Append(err, errors.New("second error"))
	
	fmt.Println(err) // "first error; second error"
}

4. golang.org/x/xerrors

Go 官方实验性的增强错误包,提供了类似 pkg/errors 的功能。

import (
	"golang.org/x/xerrors"
	"fmt"
)

func main() {
	err := process()
	if err != nil {
		fmt.Printf("%+v\n", err)
	}
}

func process() error {
	err := readFile()
	if err != nil {
		return xerrors.Errorf("process failed: %w", err)
	}
	return nil
}

func readFile() error {
	return xerrors.New("file not found")
}

5. 自定义错误类型

对于更复杂的场景,可以定义自己的错误类型:

type BusinessError struct {
	Code    int
	Message string
	Cause   error
}

func (e *BusinessError) Error() string {
	if e.Cause != nil {
		return fmt.Sprintf("%s (code: %d): %v", e.Message, e.Code, e.Cause)
	}
	return fmt.Sprintf("%s (code: %d)", e.Message, e.Code)
}

func main() {
	err := &BusinessError{
		Code:    404,
		Message: "Resource not found",
		Cause:   errors.New("file missing"),
	}
	fmt.Println(err)
}

最佳实践建议

  1. 错误包装:使用 WrapWithStack 为错误添加上下文
  2. 错误检查:使用 errors.Iserrors.As (Go 1.13+) 进行错误类型检查
  3. 错误日志:记录完整堆栈信息便于调试
  4. 错误处理:在应用边界处理错误,不要在中间层忽略错误
  5. 错误分类:区分业务错误和系统错误
// Go 1.13+ 错误检查示例
if errors.Is(err, os.ErrNotExist) {
	// 处理文件不存在的错误
}

var pathError *os.PathError
if errors.As(err, &pathError) {
	// 处理 PathError 类型错误
}

选择哪种方案取决于项目需求:简单的项目可以使用标准库或 xerrors,需要堆栈跟踪的选择 pkg/errors,需要合并多个错误的选择 multierror 或 uber/multierr。

回到顶部