From 84fef8dea32dd5f7bde92aee9c126305c8dc1180 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 10 Dec 2020 23:33:24 +0800 Subject: [PATCH] add error code feature for package gerror --- .example/os/gmlock/locker4.go | 2 +- errors/gerror/gerror.go | 145 ++++++++++++++++++++------ errors/gerror/gerror_error.go | 23 +++- errors/gerror/gerror_stack.go | 6 +- errors/gerror/gerror_test.go | 44 +++++++- net/ghttp/ghttp.go | 6 ++ net/ghttp/ghttp_func.go | 2 +- net/ghttp/ghttp_request_middleware.go | 2 +- util/gconv/gconv_map.go | 4 +- util/gconv/gconv_struct.go | 2 +- util/gconv/gconv_structs.go | 2 +- 11 files changed, 188 insertions(+), 50 deletions(-) diff --git a/.example/os/gmlock/locker4.go b/.example/os/gmlock/locker4.go index a7d8df5f4..3f27939c0 100644 --- a/.example/os/gmlock/locker4.go +++ b/.example/os/gmlock/locker4.go @@ -13,7 +13,7 @@ func main() { key := "key" // 第一次锁带时间 - gmlock.Lock(key, 1000) + gmlock.Lock(key) glog.Println("lock1") // 这个时候上一次的计时解锁已失效 gmlock.Unlock(key) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 8212712ea..c989d7f32 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -14,73 +14,71 @@ import ( "fmt" ) -// ApiStack is the interface for Stack feature. -type ApiStack interface { +// apiCode is the interface for Code feature. +type apiCode interface { + Error() string // It should be an error. + Code() int +} + +// apiStack is the interface for Stack feature. +type apiStack interface { Error() string // It should be an error. Stack() string } -// ApiCause is the interface for Cause feature. -type ApiCause interface { +// apiCause is the interface for Cause feature. +type apiCause interface { Error() string // It should be an error. Cause() error } -// ApiCurrent is the interface for Current feature. -type ApiCurrent interface { +// apiCurrent is the interface for Current feature. +type apiCurrent interface { Error() string // It should be an error. Current() error } -// ApiNext is the interface for Next feature. -type ApiNext interface { +// apiNext is the interface for Next feature. +type apiNext interface { Error() string // It should be an error. Next() error } // New creates and returns an error which is formatted from given text. func New(text string) error { - if text == "" { - return nil - } return &Error{ stack: callers(), text: text, + code: -1, + } +} + +// Newf returns an error that formats as the given format and args. +func Newf(format string, args ...interface{}) error { + return &Error{ + stack: callers(), + text: fmt.Sprintf(format, args...), + code: -1, } } // NewSkip creates and returns an error which is formatted from given text. // The parameter specifies the stack callers skipped amount. func NewSkip(skip int, text string) error { - if text == "" { - return nil - } return &Error{ stack: callers(skip), text: text, + code: -1, } } -// Newf returns an error that formats as the given format and args. -func Newf(format string, args ...interface{}) error { - if format == "" { - return nil - } - return &Error{ - stack: callers(), - text: fmt.Sprintf(format, args...), - } -} - -// NewfSkip returns an error that formats as the given format and args. +// NewSkipf returns an error that formats as the given format and args. // The parameter specifies the stack callers skipped amount. -func NewfSkip(skip int, format string, args ...interface{}) error { - if format == "" { - return nil - } +func NewSkipf(skip int, format string, args ...interface{}) error { return &Error{ stack: callers(skip), text: fmt.Sprintf(format, args...), + code: -1, } } @@ -94,6 +92,7 @@ func Wrap(err error, text string) error { error: err, stack: callers(), text: text, + code: -1, } } @@ -108,13 +107,91 @@ func Wrapf(err error, format string, args ...interface{}) error { error: err, stack: callers(), text: fmt.Sprintf(format, args...), + code: -1, } } +// NewCode creates and returns an error that has error code and given text. +func NewCode(code int, text string) error { + return &Error{ + stack: callers(), + text: text, + code: code, + } +} + +// NewCodef returns an error that has error code and formats as the given format and args. +func NewCodef(code int, format string, args ...interface{}) error { + return &Error{ + stack: callers(), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// NewCodeSkip creates and returns an error which has error code and is formatted from given text. +// The parameter specifies the stack callers skipped amount. +func NewCodeSkip(code, skip int, text string) error { + return &Error{ + stack: callers(skip), + text: text, + code: code, + } +} + +// NewCodeSkipf returns an error that has error code and formats as the given format and args. +// The parameter specifies the stack callers skipped amount. +func NewCodeSkipf(code, skip int, format string, args ...interface{}) error { + return &Error{ + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// WrapCode wraps error with code and text. +// It returns nil if given err is nil. +func WrapCode(code int, err error, text string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: text, + code: code, + } +} + +// WrapCodef wraps error with code and format specifier. +// It returns nil if given is nil. +func WrapCodef(code int, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// Cause returns the error code of current error. +// It returns -1 if it has no error code or it does not implements interface Code. +func Code(err error) int { + if err != nil { + if e, ok := err.(apiCode); ok { + return e.Code() + } + } + return -1 +} + // Cause returns the root cause error of . func Cause(err error) error { if err != nil { - if e, ok := err.(ApiCause); ok { + if e, ok := err.(apiCause); ok { return e.Cause() } } @@ -127,7 +204,7 @@ func Stack(err error) string { if err == nil { return "" } - if e, ok := err.(ApiStack); ok { + if e, ok := err.(apiStack); ok { return e.Stack() } return "" @@ -139,7 +216,7 @@ func Current(err error) error { if err == nil { return nil } - if e, ok := err.(ApiCurrent); ok { + if e, ok := err.(apiCurrent); ok { return e.Current() } return err @@ -151,7 +228,7 @@ func Next(err error) error { if err == nil { return nil } - if e, ok := err.(ApiNext); ok { + if e, ok := err.(apiNext); ok { return e.Next() } return nil diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index c8b189eda..c60d9abab 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -20,10 +20,11 @@ type Error struct { error error // Wrapped error. stack stack // Stack array, which records the stack information when this error is created or wrapped. text string // Error text, which is created by New* functions. + code int // Error code if necessary. } const ( - gFILTER_KEY = "/errors/gerror/gerror" + stackFilterKey = "/errors/gerror/gerror" ) var ( @@ -49,7 +50,19 @@ func (err *Error) Error() string { } return err.text } - return err.error.Error() + if err.error != nil { + err.error.Error() + } + return "" +} + +// Code returns the error code. +// It returns -1 if it has no error code. +func (err *Error) Code() int { + if err == nil { + return -1 + } + return err.code } // Cause returns the root cause error. @@ -63,7 +76,7 @@ func (err *Error) Cause() error { if e, ok := loop.error.(*Error); ok { // Internal Error struct. loop = e - } else if e, ok := loop.error.(ApiCause); ok { + } else if e, ok := loop.error.(apiCause); ok { // Other Error that implements ApiCause interface. return e.Cause() } else { @@ -165,7 +178,7 @@ func formatSubStack(st stack, buffer *bytes.Buffer) { for _, p := range st { if fn := runtime.FuncForPC(p - 1); fn != nil { file, line := fn.FileLine(p - 1) - if strings.Contains(file, gFILTER_KEY) { + if strings.Contains(file, stackFilterKey) { continue } // Avoid stack string like "" diff --git a/errors/gerror/gerror_stack.go b/errors/gerror/gerror_stack.go index ce64255aa..bcf54cad3 100644 --- a/errors/gerror/gerror_stack.go +++ b/errors/gerror/gerror_stack.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -12,13 +12,13 @@ import "runtime" type stack []uintptr const ( - gMAX_STACK_DEPTH = 32 + maxStackDepth = 32 ) // callers returns the stack callers. func callers(skip ...int) stack { var ( - pcs [gMAX_STACK_DEPTH]uintptr + pcs [maxStackDepth]uintptr n = 3 ) if len(skip) > 0 { diff --git a/errors/gerror/gerror_test.go b/errors/gerror/gerror_test.go index e1f64a92e..9ab5d3df3 100644 --- a/errors/gerror/gerror_test.go +++ b/errors/gerror/gerror_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -155,3 +155,45 @@ func Test_Next(t *testing.T) { t.Assert(err, nil) }) } + +func Test_Code(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("123") + t.Assert(gerror.Code(err), -1) + t.Assert(err.Error(), "123") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCode(1, "123") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "123") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCodef(1, "%s", "123") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "123") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCodeSkip(1, 0, "123") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "123") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCodeSkipf(1, 0, "%s", "123") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "123") + }) + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.WrapCode(1, err, "3") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.WrapCodef(1, err, "%s", "3") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "3: 2: 1") + }) +} diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 553df586d..2b40d4ca9 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -92,6 +92,12 @@ type ( handler *handlerItem // Handler object. } + // errorStack is the interface for Stack feature. + errorStack interface { + Error() string + Stack() string + } + // Request handler function. HandlerFunc = func(r *Request) diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 5c69f3bec..78b984524 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -74,7 +74,7 @@ func niceCallFunc(f func()) { case gEXCEPTION_EXIT, gEXCEPTION_EXIT_ALL: return default: - if _, ok := e.(gerror.ApiStack); ok { + if _, ok := e.(errorStack); ok { // It's already an error that has stack info. panic(e) } else { diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 67f88dc40..3ce5f341a 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -121,7 +121,7 @@ func (m *Middleware) Next() { loop = false } }, func(exception error) { - if e, ok := exception.(gerror.ApiStack); ok { + if e, ok := exception.(errorStack); ok { // It's already an error that has stack info. m.request.error = e } else { diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index d3e62fda0..8688507b9 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -445,7 +445,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s defer func() { // Catch the panic, especially the reflect operation panics. if e := recover(); e != nil { - err = gerror.NewfSkip(1, "%v", e) + err = gerror.NewSkipf(1, "%v", e) } }() var ( @@ -545,7 +545,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] defer func() { // Catch the panic, especially the reflect operation panics. if e := recover(); e != nil { - err = gerror.NewfSkip(1, "%v", e) + err = gerror.NewSkipf(1, "%v", e) } }() var ( diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 5f618e4b9..a84226e33 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -54,7 +54,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str defer func() { // Catch the panic, especially the reflect operation panics. if e := recover(); e != nil { - err = gerror.NewfSkip(1, "%v", e) + err = gerror.NewSkipf(1, "%v", e) } }() diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 3f02b9311..0372bb86f 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -46,7 +46,7 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st defer func() { // Catch the panic, especially the reflect operation panics. if e := recover(); e != nil { - err = gerror.NewfSkip(1, "%v", e) + err = gerror.NewSkipf(1, "%v", e) } }() // If given is JSON, it then uses json.Unmarshal doing the converting.