From 6302789c41e02a234f9e5a85254a9ceaeb4337af Mon Sep 17 00:00:00 2001 From: john Date: Sun, 30 Jun 2019 22:21:08 +0800 Subject: [PATCH] improve package gerror; add support for gerror formating output in glog --- g/errors/gerror/gerror.go | 139 +++++--------------------------- g/errors/gerror/gerror_error.go | 122 ++++++++++++++++++++++++++++ g/errors/gerror/gerror_stack.go | 18 +++-- g/errors/gerror/gerror_test.go | 76 ++++++++++++++++- g/os/glog/glog_logger.go | 7 +- geg/errors/gerror/gerror1.go | 30 +++---- geg/errors/gerror/gerror2.go | 8 +- go.mod | 4 +- go.sum | 2 - 9 files changed, 251 insertions(+), 155 deletions(-) create mode 100644 g/errors/gerror/gerror_error.go delete mode 100644 go.sum diff --git a/g/errors/gerror/gerror.go b/g/errors/gerror/gerror.go index 15db337bb..29605bb1e 100644 --- a/g/errors/gerror/gerror.go +++ b/g/errors/gerror/gerror.go @@ -8,47 +8,10 @@ package gerror import ( - "bytes" "fmt" - "io" - "runtime" - "strings" - "github.com/gogf/gf/g/util/gconv" - "github.com/pkg/errors" ) -// stacker is an interface for errors.StackTrace. -type stacker interface { - StackTrace() errors.StackTrace -} - -// stacker is an interface for errors.Cause. -type causer interface { - Cause() error -} - -// stackError is custom error for additional features. -type stackError struct { - error - *stack -} - -const ( - gFILTER_KEY = "/g/errors/gerror/gerror.go" -) - -var ( - // goRootForFilter is used for stack filtering purpose. - goRootForFilter = runtime.GOROOT() -) - -func init() { - if goRootForFilter != "" { - goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1) - } -} - // New returns an error that formats as the given value. func New(value interface{}) error { if value == nil { @@ -62,107 +25,47 @@ func NewText(text string) error { if text == "" { return nil } - return &stackError{ - err, - callers(), + return &Error{ + stack: callers(), + text: text, } } // Wrap wraps error with text. +// It returns nil if given err is nil. func Wrap(err error, text string) error { if err == nil { return nil } - return &stackError{errors.Wrap(err, text)} + return &Error{ + error: err, + stack: callers(), + text: text, + } } // Wrapf returns an error annotating err with a stack trace // at the point Wrapf is called, and the format specifier. -// If err is nil, Wrapf returns nil. +// It returns nil if given err is nil. func Wrapf(err error, format string, args ...interface{}) error { - return &stackError{errors.Wrapf(err, format, args...)} -} - -// Cause returns the underlying cause of the error, if possible. -// An error value has a cause if it implements the following -// interface: -// -// type causer interface { -// Cause() error -// } -// -// If the error does not implement Cause, the original error will -// be returned. If the error is nil, nil will be returned without further -// investigation. -func Cause(err error) error { - return &stackError{errors.Cause(err)} -} - -// Format formats the frame according to the fmt.Formatter interface. -// -// %v, %s : Print the error string; -// %+v, %+s : Print the error stack list; -func (err *stackError) Format(s fmt.State, verb rune) { - switch verb { - case 's', 'v': - switch { - case s.Flag('+'): - io.WriteString(s, Stack(err.error)) - default: - io.WriteString(s, err.Error()) - } + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: fmt.Sprintf(format, args...), } } -func (err *stackError) Cause() error { - return err.error -} - // Stack returns the stack callers as string. -// It returns am empty string id the does not support stacks. +// It returns an empty string id the does not support stacks. func Stack(err error) string { if err == nil { return "" } - if _, ok := err.(causer); !ok { - return "" - } - index := 1 - buffer := bytes.NewBuffer(nil) - for err != nil { - cause, ok := err.(causer) - if !ok { - if err, ok := err.(stacker); ok { - buffer.WriteString(fmt.Sprintf("%d.\t%v\n", index, err)) - index++ - formatSubStack(err, buffer) - } - break - } - if err, ok := err.(stacker); ok { - buffer.WriteString(fmt.Sprintf("%d.\t%v\n", index, err)) - index++ - formatSubStack(err, buffer) - } - err = cause.Cause() - } - return buffer.String() -} - -// formatSubStack formats the stack for error. -func formatSubStack(err stacker, buffer *bytes.Buffer) { - index := 1 - for _, f := range err.StackTrace() { - if fn := runtime.FuncForPC(uintptr(f) - 1); fn != nil { - file, line := fn.FileLine(uintptr(f) - 1) - if strings.Contains(file, gFILTER_KEY) { - continue - } - if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { - continue - } - buffer.WriteString(fmt.Sprintf("\t%d).\t%s\n\t\t%s:%d\n", index, fn.Name(), file, line)) - index++ - } + if e, ok := err.(*Error); !ok { + return e.Stack() } + return "" } diff --git a/g/errors/gerror/gerror_error.go b/g/errors/gerror/gerror_error.go new file mode 100644 index 000000000..c92158159 --- /dev/null +++ b/g/errors/gerror/gerror_error.go @@ -0,0 +1,122 @@ +// Copyright 2019 gf 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, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +import ( + "bytes" + "fmt" + "io" + "runtime" + "strings" +) + +// Error is custom error for additional features. +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. +} + +const ( + gFILTER_KEY = "/g/errors/gerror/gerror" +) + +var ( + // goRootForFilter is used for stack filtering purpose. + goRootForFilter = runtime.GOROOT() +) + +func init() { + if goRootForFilter != "" { + goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1) + } +} + +// Error implements the interface of Error, it returns the error as string. +func (err *Error) Error() string { + if err.text != "" { + if err.error != nil { + return err.text + ": " + err.error.Error() + } + return err.text + } + return err.error.Error() +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %v, %s : Print the error string; +// %-v, %-s : Print current error string; +// %+s : Print full stack error list; +// %+v : Print the error string and full stack error list; +func (err *Error) Format(s fmt.State, verb rune) { + switch verb { + case 's', 'v': + switch { + case s.Flag('-'): + if err.text != "" { + io.WriteString(s, err.text) + } else { + io.WriteString(s, err.Error()) + } + case s.Flag('+'): + if verb == 's' { + io.WriteString(s, err.Stack()) + } else { + io.WriteString(s, err.Error()+"\n"+err.Stack()) + } + default: + io.WriteString(s, err.Error()) + } + } +} + +// Stack returns the stack callers as string. +// It returns an empty string id the does not support stacks. +func (err *Error) Stack() string { + if err == nil { + return "" + } + loop := err + index := 1 + buffer := bytes.NewBuffer(nil) + for loop != nil { + buffer.WriteString(fmt.Sprintf("%d.\t%-v\n", index, loop)) + index++ + formatSubStack(loop.stack, buffer) + if loop.error != nil { + if e, ok := loop.error.(*Error); ok { + loop = e + } else { + buffer.WriteString(fmt.Sprintf("%d.\t%s\n", index, loop.error.Error())) + index++ + break + } + } else { + break + } + } + return buffer.String() +} + +// formatSubStack formats the stack for error. +func formatSubStack(st stack, buffer *bytes.Buffer) { + index := 1 + for _, p := range st { + if fn := runtime.FuncForPC(p - 1); fn != nil { + file, line := fn.FileLine(p - 1) + if strings.Contains(file, gFILTER_KEY) { + continue + } + if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { + continue + } + buffer.WriteString(fmt.Sprintf("\t%d).\t%s\n\t\t%s:%d\n", index, fn.Name(), file, line)) + index++ + } + } +} diff --git a/g/errors/gerror/gerror_stack.go b/g/errors/gerror/gerror_stack.go index 53a7b22cc..5a0d5239b 100644 --- a/g/errors/gerror/gerror_stack.go +++ b/g/errors/gerror/gerror_stack.go @@ -1,3 +1,9 @@ +// Copyright 2019 gf 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, +// You can obtain one at https://github.com/gogf/gf. + package gerror import "runtime" @@ -5,10 +11,12 @@ import "runtime" // stack represents a stack of program counters. type stack []uintptr -func callers() *stack { - const depth = 32 - var pcs [depth]uintptr +const ( + gMAX_STACK_DEPTH = 32 +) + +func callers() stack { + var pcs [gMAX_STACK_DEPTH]uintptr n := runtime.Callers(3, pcs[:]) - var st stack = pcs[0:n] - return &st + return pcs[0:n] } diff --git a/g/errors/gerror/gerror_test.go b/g/errors/gerror/gerror_test.go index 71aaa7d6c..b14053ea9 100644 --- a/g/errors/gerror/gerror_test.go +++ b/g/errors/gerror/gerror_test.go @@ -7,6 +7,8 @@ package gerror_test import ( + "errors" + "fmt" "testing" "github.com/gogf/gf/g/errors/gerror" @@ -30,10 +32,76 @@ func Test_Nil(t *testing.T) { func Test_Wrap(t *testing.T) { gtest.Case(t, func() { - err := gerror.New("1") - err = gerror.Wrap(err, "func2 error") - err = gerror.Wrap(err, "func3 error") + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") gtest.AssertNE(err, nil) - gtest.Assert(err.Error(), "func3 error: func2 error: 1") + gtest.Assert(err.Error(), "3: 2: 1") + }) + + gtest.Case(t, func() { + err := gerror.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + gtest.Assert(err.Error(), "3: 2: 1") + }) +} + +func Test_Format(t *testing.T) { + gtest.Case(t, func() { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1") + gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1") + }) + + gtest.Case(t, func() { + err := gerror.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1") + gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1") + }) + + gtest.Case(t, func() { + err := gerror.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + gtest.Assert(fmt.Sprintf("%-s", err), "3") + gtest.Assert(fmt.Sprintf("%-v", err), "3") + }) +} + +func Test_Stack(t *testing.T) { + gtest.Case(t, func() { + err := errors.New("1") + gtest.Assert(fmt.Sprintf("%+v", err), "1") + }) + + gtest.Case(t, func() { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + //fmt.Printf("%+v", err) + }) + + gtest.Case(t, func() { + err := gerror.New("1") + gtest.AssertNE(fmt.Sprintf("%+v", err), "1") + //fmt.Printf("%+v", err) + }) + + gtest.Case(t, func() { + err := gerror.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + gtest.AssertNE(err, nil) + //fmt.Printf("%+v", err) }) } diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index a9108120f..a8ea66ee4 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -257,10 +257,15 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) { buffer.WriteString(l.prefix + " ") } } + // Convert value to string. tempStr := "" valueStr := "" for _, v := range value { - tempStr = gconv.String(v) + if err, ok := v.(error); ok { + tempStr = fmt.Sprintf("%+v", err) + } else { + tempStr = gconv.String(v) + } if len(valueStr) > 0 { if valueStr[len(valueStr)-1] == '\n' { if tempStr[0] == '\n' { diff --git a/geg/errors/gerror/gerror1.go b/geg/errors/gerror/gerror1.go index 4c12dc29e..809fc81f6 100644 --- a/geg/errors/gerror/gerror1.go +++ b/geg/errors/gerror/gerror1.go @@ -1,31 +1,25 @@ package main import ( + "errors" "fmt" - "github.com/pkg/errors" - "github.com/gogf/gf/g/errors/gerror" ) -func Test1() error { +func Error1() error { + return errors.New("test") +} + +func Error2() error { return gerror.New("test") } -func Test2() error { - return gerror.Wrap(Test1(), "error test1") -} - -type stackTracer interface { - StackTrace() errors.StackTrace -} - func main() { - err := Test2() - fmt.Printf("%s\n", err) - fmt.Printf("%v\n", err) - fmt.Printf("%+v\n", err) - fmt.Println(gerror.Stack(err)) - return - + err1 := Error1() + err2 := Error2() + fmt.Printf("%s, %-s, %+s\n", err1, err1, err1) + fmt.Printf("%v, %-v, %+v\n", err1, err1, err1) + fmt.Printf("%s, %-s, %+s\n", err2, err2, err2) + fmt.Printf("%v, %-v, %+v\n", err2, err2, err2) } diff --git a/geg/errors/gerror/gerror2.go b/geg/errors/gerror/gerror2.go index 8d0d44f8a..a210d5fef 100644 --- a/geg/errors/gerror/gerror2.go +++ b/geg/errors/gerror/gerror2.go @@ -19,8 +19,8 @@ func ReadConfig() error { } func main() { - //glog.Println(OpenConfig()) - glog.Printf("unexpected error: %+s", ReadConfig()) - //glog.Errorf("unexpected error: %+s", OpenConfig()) - + err := ReadConfig() + glog.Printf("%s\n%+s", err, err) + glog.Printf("%+v", err) + glog.Error(err) } diff --git a/go.mod b/go.mod index a283f6e2b..fd3232e72 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1 @@ -module github.com/gogf/gf - -require github.com/pkg/errors v0.8.1 +module github.com/gogf/gf \ No newline at end of file diff --git a/go.sum b/go.sum deleted file mode 100644 index f29ab350a..000000000 --- a/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=