github.com/zhongdalu/gf@v1.0.0/g/errors/gerror/gerror_error.go (about) 1 // Copyright 2019 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 package gerror 8 9 import ( 10 "bytes" 11 "fmt" 12 "io" 13 "runtime" 14 "strings" 15 ) 16 17 // Error is custom error for additional features. 18 type Error struct { 19 error error // Wrapped error. 20 stack stack // Stack array, which records the stack information when this error is created or wrapped. 21 text string // Error text, which is created by New* functions. 22 } 23 24 const ( 25 gFILTER_KEY = "/g/errors/gerror/gerror" 26 ) 27 28 var ( 29 // goRootForFilter is used for stack filtering purpose. 30 goRootForFilter = runtime.GOROOT() 31 ) 32 33 func init() { 34 if goRootForFilter != "" { 35 goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1) 36 } 37 } 38 39 // Error implements the interface of Error, it returns the error as string. 40 func (err *Error) Error() string { 41 if err.text != "" { 42 if err.error != nil { 43 return err.text + ": " + err.error.Error() 44 } 45 return err.text 46 } 47 return err.error.Error() 48 } 49 50 // Cause returns the root cause error. 51 func (err *Error) Cause() error { 52 loop := err 53 for loop != nil { 54 if loop.error != nil { 55 if e, ok := loop.error.(*Error); ok { 56 loop = e 57 } else { 58 return loop.error 59 } 60 } else { 61 return loop 62 } 63 } 64 return nil 65 } 66 67 // Format formats the frame according to the fmt.Formatter interface. 68 // 69 // %v, %s : Print the error string; 70 // %-v, %-s : Print current error string; 71 // %+s : Print full stack error list; 72 // %+v : Print the error string and full stack error list; 73 func (err *Error) Format(s fmt.State, verb rune) { 74 switch verb { 75 case 's', 'v': 76 switch { 77 case s.Flag('-'): 78 if err.text != "" { 79 io.WriteString(s, err.text) 80 } else { 81 io.WriteString(s, err.Error()) 82 } 83 case s.Flag('+'): 84 if verb == 's' { 85 io.WriteString(s, err.Stack()) 86 } else { 87 io.WriteString(s, err.Error()+"\n"+err.Stack()) 88 } 89 default: 90 io.WriteString(s, err.Error()) 91 } 92 } 93 } 94 95 // Stack returns the stack callers as string. 96 // It returns an empty string id the <err> does not support stacks. 97 func (err *Error) Stack() string { 98 if err == nil { 99 return "" 100 } 101 loop := err 102 index := 1 103 buffer := bytes.NewBuffer(nil) 104 for loop != nil { 105 buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop)) 106 index++ 107 formatSubStack(loop.stack, buffer) 108 if loop.error != nil { 109 if e, ok := loop.error.(*Error); ok { 110 loop = e 111 } else { 112 buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error())) 113 index++ 114 break 115 } 116 } else { 117 break 118 } 119 } 120 return buffer.String() 121 } 122 123 // formatSubStack formats the stack for error. 124 func formatSubStack(st stack, buffer *bytes.Buffer) { 125 index := 1 126 space := " " 127 for _, p := range st { 128 if fn := runtime.FuncForPC(p - 1); fn != nil { 129 file, line := fn.FileLine(p - 1) 130 if strings.Contains(file, gFILTER_KEY) { 131 continue 132 } 133 if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { 134 continue 135 } 136 if index > 9 { 137 space = " " 138 } 139 buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line)) 140 index++ 141 } 142 } 143 }