github.com/gogf/gf@v1.16.9/errors/gerror/gerror_error.go (about) 1 // Copyright GoFrame Author(https://goframe.org). 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/gogf/gf. 6 7 package gerror 8 9 import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "github.com/gogf/gf/errors/gcode" 14 "github.com/gogf/gf/internal/utils" 15 "io" 16 "runtime" 17 "strings" 18 ) 19 20 // Error is custom error for additional features. 21 type Error struct { 22 error error // Wrapped error. 23 stack stack // Stack array, which records the stack information when this error is created or wrapped. 24 text string // Error text, which is created by New* functions. 25 code gcode.Code // Error code if necessary. 26 } 27 28 const ( 29 // Filtering key for current error module paths. 30 stackFilterKeyLocal = "/errors/gerror/gerror" 31 ) 32 33 var ( 34 // goRootForFilter is used for stack filtering purpose. 35 // Mainly for development environment. 36 goRootForFilter = runtime.GOROOT() 37 ) 38 39 func init() { 40 if goRootForFilter != "" { 41 goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1) 42 } 43 } 44 45 // Error implements the interface of Error, it returns all the error as string. 46 func (err *Error) Error() string { 47 if err == nil { 48 return "" 49 } 50 errStr := err.text 51 if errStr == "" && err.code != nil { 52 errStr = err.code.Message() 53 } 54 if err.error != nil { 55 if errStr != "" { 56 errStr += ": " 57 } 58 errStr += err.error.Error() 59 } 60 return errStr 61 } 62 63 // Code returns the error code. 64 // It returns CodeNil if it has no error code. 65 func (err *Error) Code() gcode.Code { 66 if err == nil { 67 return gcode.CodeNil 68 } 69 return err.code 70 } 71 72 // Cause returns the root cause error. 73 func (err *Error) Cause() error { 74 if err == nil { 75 return nil 76 } 77 loop := err 78 for loop != nil { 79 if loop.error != nil { 80 if e, ok := loop.error.(*Error); ok { 81 // Internal Error struct. 82 loop = e 83 } else if e, ok := loop.error.(apiCause); ok { 84 // Other Error that implements ApiCause interface. 85 return e.Cause() 86 } else { 87 return loop.error 88 } 89 } else { 90 // return loop 91 // To be compatible with Case of https://github.com/pkg/errors. 92 return errors.New(loop.text) 93 } 94 } 95 return nil 96 } 97 98 // Format formats the frame according to the fmt.Formatter interface. 99 // 100 // %v, %s : Print all the error string; 101 // %-v, %-s : Print current level error string; 102 // %+s : Print full stack error list; 103 // %+v : Print the error string and full stack error list; 104 func (err *Error) Format(s fmt.State, verb rune) { 105 switch verb { 106 case 's', 'v': 107 switch { 108 case s.Flag('-'): 109 if err.text != "" { 110 io.WriteString(s, err.text) 111 } else { 112 io.WriteString(s, err.Error()) 113 } 114 case s.Flag('+'): 115 if verb == 's' { 116 io.WriteString(s, err.Stack()) 117 } else { 118 io.WriteString(s, err.Error()+"\n"+err.Stack()) 119 } 120 default: 121 io.WriteString(s, err.Error()) 122 } 123 } 124 } 125 126 // Stack returns the stack callers as string. 127 // It returns an empty string if the <err> does not support stacks. 128 func (err *Error) Stack() string { 129 if err == nil { 130 return "" 131 } 132 var ( 133 loop = err 134 index = 1 135 buffer = bytes.NewBuffer(nil) 136 ) 137 for loop != nil { 138 buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop)) 139 index++ 140 formatSubStack(loop.stack, buffer) 141 if loop.error != nil { 142 if e, ok := loop.error.(*Error); ok { 143 loop = e 144 } else { 145 buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error())) 146 index++ 147 break 148 } 149 } else { 150 break 151 } 152 } 153 return buffer.String() 154 } 155 156 // Current creates and returns the current level error. 157 // It returns nil if current level error is nil. 158 func (err *Error) Current() error { 159 if err == nil { 160 return nil 161 } 162 return &Error{ 163 error: nil, 164 stack: err.stack, 165 text: err.text, 166 code: err.code, 167 } 168 } 169 170 // Next returns the next level error. 171 // It returns nil if current level error or the next level error is nil. 172 func (err *Error) Next() error { 173 if err == nil { 174 return nil 175 } 176 return err.error 177 } 178 179 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 180 // Note that do not use pointer as its receiver here. 181 func (err *Error) MarshalJSON() ([]byte, error) { 182 return []byte(`"` + err.Error() + `"`), nil 183 } 184 185 // formatSubStack formats the stack for error. 186 func formatSubStack(st stack, buffer *bytes.Buffer) { 187 if st == nil { 188 return 189 } 190 index := 1 191 space := " " 192 for _, p := range st { 193 if fn := runtime.FuncForPC(p - 1); fn != nil { 194 file, line := fn.FileLine(p - 1) 195 // Custom filtering. 196 if !utils.IsDebugEnabled() { 197 if strings.Contains(file, utils.StackFilterKeyForGoFrame) { 198 continue 199 } 200 } else { 201 if strings.Contains(file, stackFilterKeyLocal) { 202 continue 203 } 204 } 205 // Avoid stack string like "<autogenerated>" 206 if strings.Contains(file, "<") { 207 continue 208 } 209 // Ignore GO ROOT paths. 210 if goRootForFilter != "" && 211 len(file) >= len(goRootForFilter) && 212 file[0:len(goRootForFilter)] == goRootForFilter { 213 continue 214 } 215 // Graceful indent. 216 if index > 9 { 217 space = " " 218 } 219 buffer.WriteString(fmt.Sprintf( 220 " %d).%s%s\n \t%s:%d\n", 221 index, space, fn.Name(), file, line, 222 )) 223 index++ 224 } 225 } 226 }