github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/infra/err_stack.go (about) 1 package infra 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "path" 8 "runtime" 9 "strconv" 10 "strings" 11 12 "go.uber.org/multierr" 13 "go.uber.org/zap/zapcore" 14 ) 15 16 // References: 17 // https://github.com/pkg/errors/blob/master/stack.go 18 19 type frame uintptr 20 21 func (fr frame) pc() uintptr { 22 return uintptr(fr) - 1 23 } 24 25 func (fr frame) file() string { 26 pc := fr.pc() 27 fn := runtime.FuncForPC(pc) 28 if fn == nil { 29 return "unknownFile" 30 } 31 f, _ := fn.FileLine(pc) 32 return f 33 } 34 35 func (fr frame) line() int { 36 pc := fr.pc() 37 fn := runtime.FuncForPC(pc) 38 if fn == nil { 39 return 0 40 } 41 _, l := fn.FileLine(pc) 42 return l 43 } 44 45 func (fr frame) name() string { 46 pc := fr.pc() 47 fn := runtime.FuncForPC(pc) 48 if fn == nil { 49 return "unknownFunc" 50 } 51 return fn.Name() 52 } 53 54 // Format characters: 55 // %s - source file 56 // %d - source line 57 // %n - function name 58 // %v - verbose, equivalent to %s:%d 59 // %+s - full path, the root path is relative to the compile time GOPATH 60 // separated by \n\t (<function-name>\n\t<path>) 61 // %+v - equivalent to %+s:%d 62 func (fr frame) Format(state fmt.State, verb rune) { 63 switch verb { 64 case 's': 65 if state.Flag('+') { 66 _, _ = io.WriteString(state, fr.name()) 67 _, _ = io.WriteString(state, "\n\t") 68 _, _ = io.WriteString(state, fr.file()) 69 } else { 70 _, _ = io.WriteString(state, path.Base(fr.file())) 71 } 72 case 'd': 73 _, _ = io.WriteString(state, strconv.Itoa(fr.line())) 74 case 'n': 75 _, _ = io.WriteString(state, funcName(fr.name())) 76 case 'v': 77 fr.Format(state, 's') 78 _, _ = io.WriteString(state, ":") 79 fr.Format(state, 'd') 80 } 81 } 82 83 // For fmt.Sprintf("%+v", frame). 84 // If json.Marshaler interface isn't implemented, the MarshalText method is used. 85 func (fr frame) MarshalText() ([]byte, error) { 86 name := fr.name() 87 if name == "unknownFunc" { 88 return []byte("unknownFrame"), nil 89 } 90 builder := strings.Builder{} 91 _, _ = builder.WriteString(name) 92 _, _ = builder.WriteString(" ") 93 _, _ = builder.WriteString(fr.file()) 94 _, _ = builder.WriteString(":") 95 _, _ = builder.WriteString(strconv.Itoa(fr.line())) 96 return []byte(builder.String()), nil 97 } 98 99 func (fr frame) MarshalJSON() ([]byte, error) { 100 name := fr.name() 101 if name == "unknownFunc" { 102 return []byte("{\"frame\":\"unknownFrame\"}"), nil 103 } 104 builder := strings.Builder{} 105 _, _ = builder.WriteString("{") 106 _, _ = builder.WriteString("\"func\":\"") 107 _, _ = builder.WriteString(name) 108 _, _ = builder.WriteString("\",") 109 _, _ = builder.WriteString("\"fileAndLine\":\"") 110 _, _ = builder.WriteString(fr.file()) 111 _, _ = builder.WriteString(":") 112 _, _ = builder.WriteString(strconv.Itoa(fr.line())) 113 _, _ = builder.WriteString("\"}") 114 return []byte(builder.String()), nil 115 } 116 117 func (fr frame) MarshalLogObject(enc zapcore.ObjectEncoder) error { 118 if enc == nil { 119 return errors.New("zapcore object marshal error") 120 } 121 name := fr.name() 122 if name == "unknownFunc" { 123 enc.AddString("frame", "unknownFrame") 124 return nil 125 } 126 enc.AddString("func", name) 127 enc.AddString("fileAndLine", fr.file()+":"+strconv.Itoa(fr.line())) 128 return nil 129 } 130 131 func funcName(name string) string { 132 i := strings.LastIndex(name, "/") 133 name = name[i+1:] 134 i = strings.Index(name, ".") 135 return name[i+1:] 136 } 137 138 type stackTrace []frame 139 140 func (st stackTrace) Format(state fmt.State, verb rune) { 141 switch verb { 142 case 'v': 143 if state.Flag('#') { 144 _, _ = io.WriteString(state, "infra.stackTrace(") 145 if len(st) > 0 { 146 _, _ = io.WriteString(state, "[") 147 for i, frame := range st { 148 if i > 0 { 149 _, _ = io.WriteString(state, ",") 150 } 151 frame.Format(state, verb) 152 } 153 _, _ = io.WriteString(state, "]") 154 } else { 155 _, _ = io.WriteString(state, "nil") 156 } 157 _, _ = io.WriteString(state, ")") 158 } else if state.Flag('+') { 159 for i, frame := range st { 160 if i > 0 { 161 _, _ = io.WriteString(state, "\n") 162 } 163 frame.Format(state, verb) 164 } 165 if len(st) <= 0 { 166 _, _ = io.WriteString(state, "nil") 167 } 168 } else { 169 st.formatSlice(state, verb) 170 } 171 case 's': 172 st.formatSlice(state, verb) 173 } 174 } 175 176 func (st stackTrace) formatSlice(state fmt.State, verb rune) { 177 if !state.Flag('+') { 178 _, _ = io.WriteString(state, "[") 179 for i, frame := range st { 180 if i > 0 { 181 _, _ = io.WriteString(state, " ") 182 } 183 frame.Format(state, verb) 184 } 185 _, _ = io.WriteString(state, "]") 186 } else { 187 for i, frame := range st { 188 if i > 0 { 189 _, _ = io.WriteString(state, "\n") 190 } 191 frame.Format(state, verb) 192 } 193 } 194 } 195 196 func (st stackTrace) MarshalText() ([]byte, error) { 197 builder := strings.Builder{} 198 for i, frame := range st { 199 if i > 0 { 200 _, _ = builder.WriteString("\n") 201 } 202 _bytes, _ := frame.MarshalText() 203 _, _ = builder.Write(_bytes) 204 } 205 return []byte(builder.String()), nil 206 } 207 208 func (st stackTrace) MarshalJSON() ([]byte, error) { 209 builder := strings.Builder{} 210 _, _ = builder.WriteString("[") 211 for i, frame := range st { 212 if i > 0 { 213 _, _ = builder.WriteString(",") 214 } 215 _bytes, _ := frame.MarshalJSON() 216 _, _ = builder.Write(_bytes) 217 } 218 _, _ = builder.WriteString("]") 219 return []byte(builder.String()), nil 220 } 221 222 func (st stackTrace) MarshalLogObject(enc zapcore.ObjectEncoder) error { 223 if enc == nil { 224 return errors.New("zapcore object marshal error") 225 } 226 if err := enc.AddArray("frames", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error { 227 if enc == nil { 228 return errors.New("zapcore array marshal error") 229 } 230 for _, frame := range st { 231 if err := enc.AppendObject(frame); err != nil { 232 return err 233 } 234 } 235 return nil 236 })); err != nil { 237 return err 238 } 239 return nil 240 } 241 242 type stack []uintptr 243 244 func (stack stack) StackTrace() stackTrace { 245 st := make(stackTrace, len(stack)) 246 for i := 0; i < len(st); i++ { 247 st[i] = frame(stack[i]) 248 } 249 return st 250 } 251 252 func (stack stack) Format(state fmt.State, verb rune) { 253 switch verb { 254 case 'v': 255 if state.Flag('#') { 256 _, _ = io.WriteString(state, "infra.stack(") 257 stack.StackTrace().Format(state, verb) 258 _, _ = io.WriteString(state, ")") 259 } else if state.Flag('+') { 260 _, _ = io.WriteString(state, "stack:\n") 261 st := stack.StackTrace() 262 _, _ = fmt.Fprintf(state, "%+v", st) 263 } else { 264 stack.StackTrace().Format(state, 'v') 265 } 266 } 267 } 268 269 func (st stack) MarshalJSON() ([]byte, error) { 270 builder := strings.Builder{} 271 _bytes, _ := st.StackTrace().MarshalJSON() 272 _, _ = builder.Write(_bytes) 273 return []byte(builder.String()), nil 274 } 275 276 func (st stack) MarshalLogObject(enc zapcore.ObjectEncoder) error { 277 if enc == nil { 278 return errors.New("zapcore object marshal error") 279 } 280 return st.StackTrace().MarshalLogObject(enc) 281 } 282 283 func (st stack) MarshalLogArray(enc zapcore.ArrayEncoder) error { 284 if enc == nil { 285 return errors.New("zapcore object marshal error") 286 } 287 for _, frame := range st.StackTrace() { 288 if err := enc.AppendObject(frame); err != nil { 289 return err 290 } 291 } 292 return nil 293 } 294 295 func getCallers(depth int8) stack { 296 pcs := make([]uintptr, depth) 297 n := runtime.Callers(3, pcs[:]) 298 var st stack = pcs[0:n] 299 return st 300 } 301 302 type ErrorStack interface { 303 Error() string 304 Unwrap() []error 305 MarshalJSON() ([]byte, error) 306 zapcore.ObjectMarshaler 307 } 308 309 type errorStack struct { 310 err error 311 stack *stack 312 upper *errorStack 313 } 314 315 // For errors.Is(err, target error) and errors.As(err error, target any). 316 func (es *errorStack) Unwrap() []error { 317 _errors := make([]error, 0, 8) 318 for err := es.err; es != nil; es = es.upper { 319 switch x := err.(type) { 320 case interface{ Unwrap() []error }: 321 _errors = append(_errors, x.Unwrap()...) 322 default: 323 _errors = append(_errors, multierr.Errors(err)...) 324 } 325 } 326 return _errors 327 } 328 329 func (es *errorStack) Error() string { 330 builder := strings.Builder{} 331 for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper { 332 if iter.err != nil { 333 if i > 0 { 334 _, _ = builder.WriteString("; ") 335 } 336 _, _ = builder.WriteString(iter.err.Error()) 337 } 338 } 339 res := builder.String() 340 if res == "" { 341 res = "[{}]" 342 } 343 return res 344 } 345 346 func (es *errorStack) Format(state fmt.State, verb rune) { 347 switch verb { 348 case 'v': 349 if state.Flag('#') { 350 _, _ = io.WriteString(state, "infra.errorStacks([") 351 for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper { 352 if i > 0 { 353 _, _ = io.WriteString(state, ",") 354 } 355 _, _ = io.WriteString(state, "infra.errorStack({") 356 _, _ = io.WriteString(state, "error(") 357 if iter.err != nil { 358 _, _ = io.WriteString(state, iter.err.Error()) 359 } else { 360 _, _ = io.WriteString(state, "nil") 361 } 362 _, _ = io.WriteString(state, "); ") 363 if iter.stack != nil { 364 iter.stack.Format(state, verb) 365 } else { 366 _, _ = io.WriteString(state, "nilStack") 367 } 368 _, _ = io.WriteString(state, "})") 369 } 370 _, _ = io.WriteString(state, "])") 371 } else if state.Flag('+') { 372 for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper { 373 if i > 0 { 374 _, _ = io.WriteString(state, "\n") 375 } 376 _, _ = io.WriteString(state, "error messages:\n\t") 377 if iter.err != nil { 378 _, _ = io.WriteString(state, iter.err.Error()) 379 } else { 380 _, _ = io.WriteString(state, "nil") 381 } 382 _, _ = io.WriteString(state, "\n") 383 if iter.stack != nil { 384 iter.stack.Format(state, verb) 385 } else { 386 _, _ = io.WriteString(state, "no stack!") 387 } 388 } 389 } else { 390 _, _ = io.WriteString(state, es.Error()) 391 } 392 case 's': 393 _, _ = io.WriteString(state, es.Error()) 394 } 395 } 396 397 func (es *errorStack) MarshalJSON() ([]byte, error) { 398 builder := strings.Builder{} 399 _, _ = builder.WriteString("[") 400 for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper { 401 if i > 0 { 402 _, _ = builder.WriteString(",") 403 } 404 _, _ = builder.WriteString("{") 405 if iter.err != nil { 406 _, _ = builder.WriteString("\"error\":\"") 407 _, _ = builder.WriteString(iter.err.Error()) 408 _, _ = builder.WriteString("\"") 409 } 410 if iter.stack != nil { 411 _, _ = builder.WriteString(",") 412 _, _ = builder.WriteString("\"errorStack\":") 413 _bytes, _ := iter.stack.MarshalJSON() 414 _, _ = builder.Write(_bytes) 415 } 416 _, _ = builder.WriteString("}") 417 } 418 _, _ = builder.WriteString("]") 419 return []byte(builder.String()), nil 420 } 421 422 func (es *errorStack) MarshalLogObject(enc zapcore.ObjectEncoder) error { 423 if es == nil || enc == nil { 424 return nil 425 } 426 if err := enc.AddArray("stacktrace", zapcore.ArrayMarshalerFunc(func(ae zapcore.ArrayEncoder) error { 427 for iter := es; iter != nil; iter = iter.upper { 428 if err := ae.AppendObject(zapcore.ObjectMarshalerFunc(func(oe zapcore.ObjectEncoder) error { 429 if iter.err != nil { 430 oe.AddString("error", iter.err.Error()) 431 } 432 if iter.stack != nil { 433 if err := oe.AddArray("errorStack", iter.stack); err != nil { 434 return err 435 } 436 } 437 return nil 438 })); err != nil { 439 return err 440 } 441 } 442 return nil 443 })); err != nil { 444 return err 445 } 446 return nil 447 } 448 449 func NewErrorStack(errMsg string) error { 450 errMsg = strings.TrimSpace(errMsg) 451 if len(errMsg) <= 0 { 452 return nil 453 } 454 s := getCallers(32) 455 return &errorStack{ 456 err: errors.New(errMsg), 457 stack: &s, 458 upper: nil, 459 } 460 } 461 462 func WrapErrorStack(err error) error { 463 if err == nil { 464 return nil 465 } 466 if es, ok := err.(*errorStack); ok && es.err != nil && es.stack != nil { 467 return es 468 } 469 s := getCallers(32) 470 return &errorStack{ 471 err: err, 472 stack: &s, 473 upper: nil, 474 } 475 } 476 477 func AppendErrorStack(es error, errors ...error) error { 478 if len(errors) <= 0 { 479 return es 480 } 481 482 var merr error 483 for _, e := range errors { 484 if e != nil { 485 merr = multierr.Append(merr, e) 486 } 487 } 488 if es == nil { 489 s := getCallers(32) 490 return &errorStack{ 491 err: merr, 492 stack: &s, 493 upper: nil, 494 } 495 } 496 if _es, ok := es.(*errorStack); ok && _es.err != nil && _es.stack != nil { 497 498 s := getCallers(32) 499 return &errorStack{ 500 err: merr, 501 stack: &s, 502 upper: _es, 503 } 504 } 505 if estr := es.Error(); len(estr) > 0 && estr != "[{}]" { 506 merr = multierr.Append(es, merr) 507 } 508 s := getCallers(32) 509 return &errorStack{ 510 err: merr, 511 stack: &s, 512 upper: nil, 513 } 514 } 515 516 func WrapErrorStackWithMessage(es error, errMsg string) error { 517 if len(errMsg) <= 0 { 518 return es 519 } 520 err := fmt.Errorf("%s", errMsg) 521 if es == nil { 522 s := getCallers(32) 523 return &errorStack{ 524 err: err, 525 stack: &s, 526 upper: nil, 527 } 528 } 529 if _es, ok := es.(*errorStack); ok && _es.err != nil && _es.stack != nil { 530 s := getCallers(32) 531 return &errorStack{ 532 err: err, 533 stack: &s, 534 upper: _es, 535 } 536 } 537 if estr := es.Error(); len(estr) > 0 && estr != "[{}]" { 538 err = multierr.Append(es, err) 539 } 540 s := getCallers(32) 541 return &errorStack{ 542 err: err, 543 stack: &s, 544 upper: nil, 545 } 546 }