github.com/duomi520/utils@v0.0.0-20240430123446-e03a4cddd6ec/errors.go (about) 1 package utils 2 3 import ( 4 "errors" 5 "fmt" 6 "log/slog" 7 "runtime" 8 "strings" 9 ) 10 11 /* 12 Errors are just values not sentinel errors 防止包引入 13 handle not just check errors 14 Only handle errors once 15 要求传入的字符串首字母小写,结尾不带标点符号 16 将 errors 看成黑盒,判断它的行为,而不是类型 17 使用 Wrap Unwrap 18 最上层打印 19 */ 20 21 type wError struct { 22 stack string 23 err error 24 } 25 26 func (m wError) Error() string { 27 return m.err.Error() 28 } 29 30 func ErrorWithStack(err error) string { 31 if err == nil { 32 return "" 33 } 34 m, ok := err.(wError) 35 if ok { 36 return m.stack 37 } 38 return err.Error() 39 40 } 41 42 func WrapStack(skip int, err error) error { 43 if err == nil { 44 return nil 45 } 46 _, f, l, _ := runtime.Caller(skip) 47 result := strings.Split(f, "/") 48 m := wError{ 49 stack: fmt.Sprintf("[%s:%d] %s", result[len(result)-1], l, err), 50 err: err, 51 } 52 return m 53 } 54 func ErrorWithFullStack(w error) string { 55 v, ok := w.(wError) 56 if ok { 57 var builder strings.Builder 58 builder.WriteString(v.stack) 59 builder.WriteString("\n") 60 e := errors.Unwrap(v.err) 61 for e != nil { 62 w, ok := e.(wError) 63 if ok { 64 e = w.err 65 builder.WriteString(w.stack) 66 builder.WriteString("\n") 67 } else { 68 builder.WriteString(e.Error()) 69 builder.WriteString("\n") 70 } 71 e = errors.Unwrap(e) 72 } 73 return builder.String()[:builder.Len()-2] 74 } 75 return w.Error() 76 } 77 func ReplaceAttr(_ []string, a slog.Attr) slog.Attr { 78 switch a.Value.Kind() { 79 case slog.KindAny: 80 switch v := a.Value.Any().(type) { 81 case wError: 82 a = slog.String("trace", ErrorWithFullStack(v)) 83 } 84 } 85 return a 86 } 87 88 func FormatRecover() ([]byte, any) { 89 if r := recover(); r != nil { 90 const size = 65536 91 buf := make([]byte, size) 92 end := runtime.Stack(buf, false) 93 if end > size { 94 end = size 95 } 96 return buf[:end], r 97 } 98 return nil, nil 99 } 100 101 // https://zhuanlan.zhihu.com/p/82985617