golang增强错误处理与堆栈追踪插件库errorx的使用
Golang增强错误处理与堆栈追踪插件库errorx的使用
errorx是一个Go语言的错误处理库,提供了增强的错误实现和错误相关工具。
主要特性
- 堆栈追踪
- 错误组合
- 增强错误信息(添加堆栈追踪和消息)
- 强大的类型和特征检查
基本使用
创建错误
// 使用errorx创建带有堆栈追踪的错误
return errorx.IllegalState.New("unfortunate")
装饰错误
if err != nil {
// 为现有错误添加额外信息
return errorx.Decorate(err, "this could be so much better")
}
打印错误
// 使用%+v打印完整错误信息(包括堆栈追踪)
log.Printf("Error: %+v", err)
输出示例:
Error: this could be so much better, cause: common.illegal_state: unfortunate
at main.culprit()
main.go:21
at main.innocent()
main.go:16
at main.main()
main.go:11
错误检查
类型检查
// 首先定义错误类型
MyError = MyErrors.NewType("my_error")
// 然后检查错误类型
if errorx.IsOfType(err, MyError) {
// 处理特定错误类型
}
特征检查
// 创建带有特征(Trait)的错误类型
TimeoutElapsed = MyErrors.NewType("timeout", errorx.Timeout())
// 检查错误特征
if errorx.HasTrait(err, errorx.Timeout()) {
// 处理超时类错误
}
包装错误
// 包装错误,保留原始错误但隐藏其类型
return MyError.Wrap(err, "fail")
堆栈追踪
增强堆栈追踪
// 为从其他goroutine接收的错误添加堆栈追踪
return errorx.EnhanceStackTrace(<-errorChan, "task failed")
禁用堆栈追踪
// 创建不收集堆栈追踪的错误类型
ErrInvalidToken = AuthErrors.NewType("invalid_token").ApplyModifiers(errorx.TypeModifierOmitStackTrace)
完整示例
package main
import (
"fmt"
"log"
"github.com/joomcode/errorx"
)
// 定义错误命名空间
var (
AppErrors = errorx.NewNamespace("app")
DbErrors = AppErrors.NewSubNamespace("db")
)
// 定义具体错误类型
var (
ErrDbConnection = DbErrors.NewType("connection_failed", errorx.Timeout())
ErrInvalidInput = AppErrors.NewType("invalid_input")
)
func main() {
if err := process(); err != nil {
log.Printf("Error: %+v", err)
}
}
func process() error {
if err := dbOperation(); err != nil {
// 包装错误并添加上下文
return ErrInvalidInput.Wrap(err, "failed to process request")
}
return nil
}
func dbOperation() error {
// 模拟数据库操作失败
return ErrDbConnection.New("connection timeout after 5 seconds")
}
输出示例:
Error: app.invalid_input: failed to process request, cause: app.db.connection_failed: connection timeout after 5 seconds
at main.process()
/path/to/file.go:25
at main.main()
/path/to/file.go:15
----------------------------------
at main.dbOperation()
/path/to/file.go:31
性能考虑
- 在深度调用栈情况下,堆栈追踪捕获会带来约10倍的性能下降
- 格式化带有堆栈追踪的错误比创建它更昂贵(约10倍)
- 相比最朴素的堆栈追踪实现,errorx的错误创建要快100倍
- 对于高频调用或作为控制流机制的错误,应禁用堆栈追踪
errorx提供了强大的错误处理能力,同时保持了良好的性能特性,是传统Go错误处理的有力补充。
更多关于golang增强错误处理与堆栈追踪插件库errorx的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang增强错误处理与堆栈追踪插件库errorx的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 错误处理增强库 errorx 使用指南
errorx 是一个强大的 Golang 错误处理库,它提供了丰富的错误处理功能,包括堆栈追踪、错误包装、错误分类等特性,可以显著提升 Go 程序的错误处理能力。
安装 errorx
go get github.com/pkg/errors
注意:errorx 实际上是基于 pkg/errors 的增强版本,但概念和用法类似。
基本用法
1. 创建带堆栈的错误
package main
import (
"fmt"
"github.com/pkg/errors"
)
func main() {
err := errors.New("原始错误")
fmt.Printf("%+v\n", err)
}
输出会包含完整的堆栈信息:
原始错误
main.main
/path/to/file.go:8
runtime.main
...
2. 包装错误
func foo() error {
return errors.New("foo错误")
}
func bar() error {
err := foo()
if err != nil {
return errors.Wrap(err, "bar失败")
}
return nil
}
func main() {
err := bar()
if err != nil {
fmt.Printf("%+v\n", err)
}
}
输出会显示完整的错误链:
foo错误
main.foo
/path/to/file.go:10
main.bar
/path/to/file.go:14 - bar失败
main.main
/path/to/file.go:20
高级特性
1. 错误类型判断
var ErrNotFound = errors.New("未找到")
func findUser(id int) error {
return errors.Wrapf(ErrNotFound, "用户ID %d 未找到", id)
}
func main() {
err := findUser(123)
if errors.Is(err, ErrNotFound) {
fmt.Println("确实是未找到错误")
}
}
2. 错误解包
func main() {
err := findUser(123)
// 获取原始错误
cause := errors.Cause(err)
fmt.Println("原始错误:", cause)
// 获取包装消息
fmt.Println("包装消息:", err.Error())
}
3. 自定义错误类型
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("错误码 %d: %s", e.Code, e.Message)
}
func someOperation() error {
return &MyError{Code: 404, Message: "资源不存在"}
}
func main() {
err := someOperation()
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Printf("自定义错误: 代码=%d, 消息=%s\n", myErr.Code, myErr.Message)
}
}
实际应用示例
package main
import (
"database/sql"
"fmt"
"github.com/pkg/errors"
)
var (
ErrUserNotFound = errors.New("用户不存在")
ErrDBFailure = errors.New("数据库错误")
)
type User struct {
ID int
Name string
}
func getUserFromDB(db *sql.DB, id int) (*User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.Wrapf(ErrUserNotFound, "用户ID %d", id)
}
return nil, errors.Wrap(ErrDBFailure, err.Error())
}
return &user, nil
}
func GetUserProfile(db *sql.DB, id int) (*User, error) {
user, err := getUserFromDB(db, id)
if err != nil {
return nil, errors.Wrap(err, "获取用户资料失败")
}
return user, nil
}
func main() {
db, _ := sql.Open("mysql", "user:password@/dbname")
user, err := GetUserProfile(db, 123)
switch {
case errors.Is(err, ErrUserNotFound):
fmt.Println("处理用户不存在情况")
case errors.Is(err, ErrDBFailure):
fmt.Println("处理数据库错误")
case err != nil:
fmt.Printf("未知错误: %+v\n", err)
default:
fmt.Printf("用户信息: %v\n", user)
}
}
最佳实践
- 在应用边界处(如HTTP handler)打印完整错误日志:
log.Printf("%+v", err)
- 在内部函数间传递错误时使用
Wrap
添加上下文 - 定义清晰的错误类型和错误变量
- 使用
errors.Is
和errors.As
进行错误类型判断 - 避免过度包装错误,保持错误链简洁
errorx/pkgerrors 的错误处理方式已经成为 Go 社区的事实标准,它弥补了标准库errors
包的不足,特别是在错误追踪和上下文传递方面。