mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
improve package gerror; add support for gerror formating output in glog
This commit is contained in:
@ -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 <err> does not support stacks.
|
||||
// It returns an empty string id the <err> 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 ""
|
||||
}
|
||||
|
||||
122
g/errors/gerror/gerror_error.go
Normal file
122
g/errors/gerror/gerror_error.go
Normal file
@ -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 <err> 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++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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' {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@ -1,3 +1 @@
|
||||
module github.com/gogf/gf
|
||||
|
||||
require github.com/pkg/errors v0.8.1
|
||||
module github.com/gogf/gf
|
||||
Reference in New Issue
Block a user