# gerror **Repository Path**: gousing/gerror ## Basic Information - **Project Name**: gerror - **Description**: Golang Error错误工具包,兼容Golang Error/Join/Unwrap,支持附加Stack堆栈信息, 支持附加Code错误码, 支持预定义错误,支持序列化…… - **Primary Language**: Go - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-03-21 - **Last Updated**: 2025-10-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Gousing Gerror ## 介绍 Golang Error错误工具包,兼容Golang Error/Join/Unwrap,支持附加Stack堆栈信息, 支持附加Code错误码, 支持预定义错误, 支持slog.Logger记录器, 支持序列化…… ### 安装及使用 Golang Error错误工具包 requires Go version 1.20 or above. ```golang go get gitee.com/gousing/gerror import "gitee.com/gousing/gerror" ``` ### 全局配置 ```golang // 设置默认错误状态码, 默认为0 // DefaultErrCode(code int) // 设置全局默认错误状态码为200 gerror.DefaultErrCode(200) // 设置默认是否记录Stack的选项, 默认为False // DefaultStackEnabled(enabled bool) // 设置全局默认是否记录Stack gerror.DefaultStackEnabled(true) ``` ## 用例 ```golang // 创建一个错误对象 err1 := gerror.New("error message") err2 := gerror.WrapCode(100001,err1,"wrap error") // WithStack // 如果全局未启用DefaultStackEnabled(默认不启用),某一个Error 错误对象创建时,需要记录堆栈信息,可以使用 WithStack()方法 err2.WithStack(true) ``` ### Gerror 接口 #### New Error #### 默认错误码Code > 默认错误码Code为:1, 可以通过 gerror.DefaultErrCode(code int) 进行设置 #### 是否自动记录Stack > 默认为否, 可以通过 gerror.DefaultStackEnabled(enabled bool) 进行设置 #### 注意 > 注意Wrap方式新建 Error 错误对象时,如果待包裹err为nil,则返回nil ```golang // New 创建一个 Error 错误 New(msg string) IError // NewF 创建一个格式化消息的 Error 错误 NewF(format string, a ...any) IError // Errorf 创建一个 Error 错误, like fmt.Errorf Errorf(format string, a ...any) IError // 使用Code创建Error // NewCode 创建一个含有Code的 Error 错误 NewCode(code int, msg string) IError // NewCodeF 创建一个含有Code的格式化消息的 Error 错误 NewCodeF(code int, format string, a ...any) IError // WrapXXX 接口 // - WrapXXX 接口 err 为 nil 时, 不进行任何操作, 返回 nil // Wrap 包裹err创建一个 Error 错误 Wrap(err error, msg string) IError // WrapF 包裹err创建一个格式化消息的 Error 错误 WrapF(err error, format string, a ...any) IError // WrapCode 包裹err创建一个含有Code的 Error 错误 WrapCode(code int, err error, msg string) IError // WrapCodeF 包裹err创建一个含有Code的格式化消息的 Error 错误 WrapCodeF(code int, err error, format string, a ...any) IError // WrapOrXXX 接口 // - WrapOrXXX 接口 err 为 nil 时,创建一个 Error 错误 // - WrapOrXXX 接口 err 不为 nil 时,包裹err创建一个 Error 错误 WrapOrNew(err error, format string, a ...any) IError ``` #### WithLogger > WithLogger 手动将错误信息登记到指定的slog日志记录器 ```golang // WithLogger(logs ...*slog.Logger) textLog := slog.New(slog.NewTextHandler(os.Stdout, nil)) jsonLog := slog.New(slog.NewJSONHandler(os.Stdout, nil)) err0 := gerror.New("simple msg string") gerror.WrapCode(100, err0, "wrap one stack msg string").WithLogger(textLog) err1 := fmt.Errorf("wrap two simple msg string: %w", err0) gerror.WrapCode(200, err1, "wrap three stack msg string").WithStack(true).WithLogger(textLog, jsonLog) ``` #### WithStack > WithStack 关闭或启用(更新)堆栈信息 > - enabled 为 false 时,强制关闭堆栈信息 > - enabled 为 true 时,启用(更新)堆栈信息 > - - !isDefined 非预定义错误对象,如不存在堆栈信息则开启堆栈,否则不做任何操作 > - - isDefined 预定义错误对象,创建一个错误对象的副本并启用堆栈信息 ```golang //WithStack(enabled bool) IError err := gerror.New("simple msg string") err.WithStack(true) err.WithStack(false) ``` #### Defined Error 全局开启Stack后, 对于预定义的错误, 堆栈信息将被固定,这显然不符合使用需求。 此时可以通过 Defined 预定义方式创建Error, 如果全局不开启Stack, 可以忽略这个限制。 > 预定义错误没有堆栈信息,如需堆栈信息,程序返回预定义错误时请调用 .WithStack(true) ```golang // Defined 预定义 Error 错误 Defined(msg string) IError DefinedCode(code int, msg string) IError DefinedF(format string, a ...any) IError DefinedCodeF(code int, format string, a ...any) IError /* * Defined 预定义方法 可以在项目中预定义一组常用的错误信息 * ------------------------------- * 错误码通常设计为6位,前3位为模块标识或HTTP状态码,后3位为自定义错误码 * 自定义请求/响应类错误码: * ------------------------------- * 请求类错误 400XXX => HTTP 400 (自定义范围: 400001 ~ 400999) * 认证类错误 401XXX => HTTP 401 (自定义范围: 401001 ~ 401999) * 拒绝类错误 403XXX => HTTP 403、404 (自定义范围: 403001 ~ 404999) * 服务类错误 500XXX => HTTP 500 (自定义范围: 500001 ~ 500999) * ------------------------------- * 自定义业务类错误码: * 前3位为模块标识,后3位为自定义错误码 * 自定义错误 600XXX => HTTP 400/500 (自定义范围: 600001 ~ 999999) */ // 示例 // file app/g/error.go AccessDenied = DefinedCode(401000, "access denied") // 访问被拒绝 AccessDeniedNotLogin = DefinedCode(403001, "access denied: not logged in") // 用户未登录 AccessDeniedExpired = DefinedCode(403002, "access denied: session has expired") // 用户会话过期 AccessDeniedNoPermission = DefinedCode(403003, "access denied: no permission") // 用户没有权限 AccessDeniedIP = DefinedCode(403004, "access denied: for IP") // IP Block AccessDeniedWAF = DefinedCode(403005, "access denied: for WAF") // WAF Block // file app/service/user.go func (e *User) IsLogin(c *gin.Context) { // do something // 为预定义错误手动开启堆栈信息 return g.AccessDeniedNotLogin.WithStack(true) } // file app/api/handler/auth.go func Auth(c *gin.Context) { if err :=service.User().IsLogin(c);err!=nil { // err.String() // err.Source() // err.Stacks() // logger.Error(err.String()) // err.WithLogger(slog.Default()) return err } } ``` ### Error 错误对象 ```golang type Error struct { code int msg string cause error isDefined bool caller *Caller } ``` ### IError 接口 ```golang // IError 接口 type IError interface { // Code 获取错误码 Code() int // Error 获取错误信息(不包含堆栈信息) Error() string // String 获取详细的错误信息(如存在堆栈信息则包含) String() string // Unwrap 获取被包装的错误, 如果没有被包装,则返回 error Unwrap() error // WithStack 手动关闭或启用(更新)堆栈信息 // - enabled 为 false 时,关闭堆栈信息 // - enabled 为 true 时,启用(更新)堆栈信息 // - - !isDefined 非预定义错误对象,如不存在堆栈信息则开启堆栈,否则不做任何操作 // - - isDefined 预定义错误对象,创建一个错误对象的副本并启用堆栈信息 WithStack(enabled bool) IError // WithLogger 手动将错误信息登记到指定的slog日志记录器 WithLogger(logs ...*slog.Logger) // Source 获取Caller堆栈Source信息(第一条堆栈信息) // - source 返回值: 获取成功时返回Source对象 // - exists 返回值: 未开启堆栈或获取失败时返回 false Source() (source Source, exists bool) // Stacks 获取Caller堆栈的[]Source信息切片 // - 返回值: 未开启堆栈或获取失败时返回 nil Stacks() []Source // Format FMT格式化输出接口 Format(s fmt.State, verb rune) // MarshalJSON 序列化接口 encoding/json.Marshaler MarshalJSON() ([]byte, error) // UnmarshalJSON 反序列化接口 encoding/json.Unmarshaler UnmarshalJSON(data []byte) error } ``` ## Gousing 通用选项 ### 自定义 Jsoner ```golang // gerror 序列化默认使用兼容标准库的json-iterator // jsoniter.ConfigCompatibleWithStandardLibrary type JsonerAPI interface { // Marshal adapts to json/encoding Marshal API // Refer to https://godoc.org/encoding/json#Marshal for more information Marshal(v any) ([]byte, error) // Unmarshal adapts to json/encoding Unmarshal API // Refer to https://godoc.org/encoding/json#Unmarshal for more information Unmarshal(data []byte, v any) error } ``` ```golang // 使用Golang标准库 type myJsoner struct{} func (m myJsoner) Marshal(v any) ([]byte, error) { return json.Marshal(v) } func (m myJsoner) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) } gerror.SetJsoner(myJsoner{}) ``` ## Benchmark ```shell go.exe test -benchmem -benchtime=100000x -run=^$ -bench ^Benchmark_Error gitee.com/gousing/gerror goos: windows goarch: amd64 pkg: gitee.com/gousing/gerror cpu: 12th Gen Intel(R) Core(TM) i5-12400F ``` | Benchmark_Error | - | - | - | - | | :------------------------------------------ | :----- | :---------- | :------- | :---------- | |Benchmark_Error/StackDisabled-12 | 100000 | 29.64 ns/op | 64 B/op | 1 allocs/op| |Benchmark_Error/StackEnabled-12 | 100000 | 288.1 ns/op | 224 B/op | 3 allocs/op| |Benchmark_Error/WithStack-12 | 100000 | 314.0 ns/op | 224 B/op | 3 allocs/op| |Benchmark_Error/WrapAndStackDisabled-12 | 100000 | 52.96 ns/op | 128 B/op | 2 allocs/op| |Benchmark_Error/WrapAndStackEnabled-12 | 100000 | 318.0 ns/op | 288 B/op |4 allocs/op|