github.com/qiniu/x@v1.11.9/errors/errors.go (about) 1 /* 2 Copyright 2022 Qiniu Limited (qiniu.com) 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package errors provides errors stack tracking utilities. 18 package errors 19 20 import ( 21 "errors" 22 "fmt" 23 "io" 24 "reflect" 25 "runtime" 26 "strconv" 27 "strings" 28 ) 29 30 // -------------------------------------------------------------------- 31 32 // New returns an error that formats as the given text. 33 // Each call to New returns a distinct error value even if the text is identical. 34 func New(msg string) error { 35 return errors.New(msg) 36 } 37 38 // Err returns the cause error. 39 func Err(err error) error { 40 if e, ok := err.(*Frame); ok { 41 return Err(e.Err) 42 } 43 return err 44 } 45 46 // Summary returns summary of specified error. 47 func Summary(err error) string { 48 e, ok := err.(interface { 49 Summary() string 50 }) 51 if !ok { 52 return err.Error() 53 } 54 return e.Summary() 55 } 56 57 // -------------------------------------------------------------------- 58 59 // NotFound is a generic NotFound error. 60 type NotFound struct { 61 Category string 62 } 63 64 func (p *NotFound) Error() string { 65 return fmt.Sprint(p.Category, " not found") 66 } 67 68 // IsNotFound unwraps err and checks it is a *NotFound object or not. 69 func IsNotFound(err error) bool { 70 for { 71 if e, ok := err.(interface{ Unwrap() error }); ok { 72 err = e.Unwrap() 73 } else { 74 _, ok = err.(*NotFound) 75 return ok 76 } 77 } 78 } 79 80 // -------------------------------------------------------------------- 81 82 type List []error 83 84 func (p *List) Add(err error) { 85 *p = append(*p, err) 86 } 87 88 func (p List) Error() string { 89 n := len(p) 90 if n >= 2 { 91 s := make([]string, n) 92 for i, v := range p { 93 s[i] = v.Error() 94 } 95 return strings.Join(s, "\n") 96 } 97 if n == 1 { 98 return p[0].Error() 99 } 100 return "" 101 } 102 103 func (p List) Summary() string { 104 n := len(p) 105 if n >= 2 { 106 s := make([]string, n) 107 for i, v := range p { 108 s[i] = Summary(v) 109 } 110 return strings.Join(s, "\n") 111 } 112 if n == 1 { 113 return Summary(p[0]) 114 } 115 return "" 116 } 117 118 func (p List) ToError() error { 119 switch len(p) { 120 case 1: 121 return p[0] 122 case 0: 123 return nil 124 } 125 return p 126 } 127 128 // Format is required by fmt.Formatter 129 func (p List) Format(s fmt.State, verb rune) { 130 switch verb { 131 case 'v': 132 io.WriteString(s, p.Error()) 133 case 's': 134 io.WriteString(s, p.Summary()) 135 case 'q': 136 fmt.Fprintf(s, "%q", p.Error()) 137 } 138 } 139 140 // -------------------------------------------------------------------- 141 142 // Frame represents an error frame. 143 type Frame struct { 144 Err error 145 Func string 146 Args []interface{} 147 Code string 148 File string 149 Line int 150 } 151 152 // NewWith creates a new error frame. 153 func NewWith(err error, code string, n int, fn string, args ...interface{}) *Frame { 154 file, line := fileLine() 155 return &Frame{Err: err, Func: fn, Args: args, Code: code, File: file, Line: line + n} 156 } 157 158 func fileLine() (file string, line int) { 159 _, file, line, _ = runtime.Caller(2) 160 return 161 } 162 163 // NewFrame creates a new error frame. 164 func NewFrame(err error, code, file string, line int, fn string, args ...interface{}) *Frame { 165 return &Frame{Err: err, Func: fn, Args: args, Code: code, File: file, Line: line} 166 } 167 168 func (p *Frame) Error() string { 169 return string(errorDetail(make([]byte, 0, 32), p)) 170 } 171 172 func (p *Frame) Summary() string { 173 return Summary(p.Err) 174 } 175 176 func errorDetail(b []byte, p *Frame) []byte { 177 if f, ok := p.Err.(*Frame); ok { 178 b = errorDetail(b, f) 179 } else { 180 b = append(b, p.Err.Error()...) 181 b = append(b, "\n\n===> errors stack:\n"...) 182 } 183 b = append(b, p.Func...) 184 b = append(b, '(') 185 b = argsDetail(b, p.Args) 186 b = append(b, ")\n\t"...) 187 b = append(b, p.File...) 188 b = append(b, ':') 189 b = strconv.AppendInt(b, int64(p.Line), 10) 190 b = append(b, ' ') 191 b = append(b, p.Code...) 192 b = append(b, '\n') 193 return b 194 } 195 196 func argsDetail(b []byte, args []interface{}) []byte { 197 nlast := len(args) - 1 198 for i, arg := range args { 199 b = appendValue(b, arg) 200 if i != nlast { 201 b = append(b, ',', ' ') 202 } 203 } 204 return b 205 } 206 207 func appendValue(b []byte, arg interface{}) []byte { 208 if arg == nil { 209 return append(b, "nil"...) 210 } 211 v := reflect.ValueOf(arg) 212 kind := v.Kind() 213 if kind >= reflect.Bool && kind <= reflect.Complex128 { 214 return append(b, fmt.Sprint(arg)...) 215 } 216 if kind == reflect.String { 217 val := arg.(string) 218 if len(val) > 32 { 219 val = val[:16] + "..." + val[len(val)-16:] 220 } 221 return strconv.AppendQuote(b, val) 222 } 223 if kind == reflect.Array { 224 return append(b, "Array"...) 225 } 226 if kind == reflect.Struct { 227 return append(b, "Struct"...) 228 } 229 val := v.Pointer() 230 b = append(b, '0', 'x') 231 return strconv.AppendInt(b, int64(val), 16) 232 } 233 234 // Unwrap provides compatibility for Go 1.13 error chains. 235 func (p *Frame) Unwrap() error { 236 return p.Err 237 } 238 239 // Format is required by fmt.Formatter 240 func (p *Frame) Format(s fmt.State, verb rune) { 241 switch verb { 242 case 'v': 243 io.WriteString(s, p.Error()) 244 case 's': 245 io.WriteString(s, p.Summary()) 246 case 'q': 247 fmt.Fprintf(s, "%q", p.Error()) 248 } 249 } 250 251 // -------------------------------------------------------------------- 252 253 // CallDetail print a function call shortly. 254 func CallDetail(msg []byte, fn interface{}, args ...interface{}) []byte { 255 f := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()) 256 if f != nil { 257 msg = append(msg, f.Name()...) 258 msg = append(msg, '(') 259 msg = argsDetail(msg, args) 260 msg = append(msg, ')') 261 } 262 return msg 263 } 264 265 // -------------------------------------------------------------------- 266 267 // ErrorInfo is provided for backward compatibility 268 // 269 // Deprecated: Use Frame instead. 270 type ErrorInfo = Frame 271 272 // Detail is provided for backward compatibility 273 func (p *ErrorInfo) Detail(err error) *ErrorInfo { 274 p.Code = err.Error() 275 return p 276 } 277 278 // NestedObject is provided for backward compatibility 279 func (p *ErrorInfo) NestedObject() interface{} { 280 return p.Err 281 } 282 283 // ErrorDetail is provided for backward compatibility 284 func (p *ErrorInfo) ErrorDetail() string { 285 return p.Error() 286 } 287 288 // AppendErrorDetail is provided for backward compatibility 289 func (p *ErrorInfo) AppendErrorDetail(b []byte) []byte { 290 return errorDetail(b, p) 291 } 292 293 // SummaryErr is provided for backward compatibility 294 func (p *ErrorInfo) SummaryErr() error { 295 return p.Err 296 } 297 298 // Info is provided for backward compatibility 299 // 300 // Deprecated: Use NewWith instead. 301 func Info(err error, cmd ...interface{}) *ErrorInfo { 302 return &Frame{Err: err, Args: cmd} 303 } 304 305 // InfoEx is provided for backward compatibility 306 // 307 // Deprecated: Use NewWith instead. 308 func InfoEx(calldepth int, err error, cmd ...interface{}) *ErrorInfo { 309 return &Frame{Err: err, Args: cmd} 310 } 311 312 // Detail is provided for backward compatibility 313 // 314 // Deprecated: Use err.Error() instead. 315 func Detail(err error) string { 316 return err.Error() 317 } 318 319 // --------------------------------------------------------------------