github.com/haraldrudell/parl@v0.4.176/perrors/errorglue/publics.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package errorglue 7 8 import ( 9 "errors" 10 "fmt" 11 "strings" 12 13 "github.com/haraldrudell/parl/pruntime" 14 ) 15 16 /* 17 // AllErrors obtains all error instances references by the err error. 18 // This includes error instances in its error chain and those of any separate, 19 // associated error instances. 20 // If err is nil, the returned slice is empty. 21 // Otherwise the first error of the slice is err followed by other errors, oldest first. 22 // If err does not have an error chain or any associated error instances, 23 // the returned slice contains only err. 24 // Cyclic error instances are removed 25 func AllErrors(err error) (errs []error) { 26 if err != nil { 27 errs = append(errs, err) 28 } 29 30 // traverse all found errors 31 errMap := map[error]bool{} 32 for errsIndex := 0; errsIndex < len(errs); errsIndex++ { 33 34 // avoid cyclic error graph 35 e := errs[errsIndex] 36 _, knownError := errMap[e] 37 if knownError { 38 continue 39 } 40 errMap[e] = true 41 42 // traverse the error chain except the first entry which is the error itself 43 errorChain := ErrorChainSlice(e) 44 for ecIndex := 1; ecIndex < len(errorChain); ecIndex++ { 45 46 // include a possible error list 47 errs = append(errs, ErrorList(errorChain[ecIndex])...) 48 } 49 } 50 return 51 } 52 */ 53 54 // ErrorChainSlice returns a slice of errors from a possible error chain. 55 // If err is nil, an empty slice is returned. 56 // If err does not have an error chain, a slice of only err is returned. 57 // Otherwise, the slice lists each error in the chain starting with err at index 0 ending with the oldest error of the chain 58 func ErrorChainSlice(err error) (errs []error) { 59 for err != nil { 60 errs = append(errs, err) 61 err = errors.Unwrap(err) 62 } 63 return 64 } 65 66 // ErrorsWithStack gets all errors in the err error chain 67 // that has a stack trace. 68 // Oldest innermost stack trace is returned first. 69 // if not stack trace is present, the slice is empty 70 func ErrorsWithStack(err error) (errs []error) { 71 for err != nil { 72 if _, ok := err.(ErrorCallStacker); ok { 73 errs = append([]error{err}, errs...) 74 } 75 err = errors.Unwrap(err) 76 } 77 return 78 } 79 80 // GetInnerMostStack gets the oldest stack trace in the error chain 81 // or nil if no stack trace is present 82 func GetInnerMostStack(err error) (stack pruntime.Stack) { 83 84 // find the innermost implementation of ErrorCallStacker interface 85 var e ErrorCallStacker 86 for ; err != nil; err = errors.Unwrap(err) { 87 if ecs, ok := err.(ErrorCallStacker); ok { 88 e = ecs 89 } 90 } 91 if e == nil { 92 return // no implementation found 93 } 94 95 stack = e.StackTrace() 96 return 97 } 98 99 // GetStackTrace gets the last stack trace 100 func GetStackTrace(err error) (stack pruntime.Stack) { 101 for ; err != nil; err = errors.Unwrap(err) { 102 if ecs, ok := err.(ErrorCallStacker); ok { 103 stack = ecs.StackTrace() 104 return 105 } 106 } 107 return 108 } 109 110 // GetStacks gets a slice of all stack traces, oldest first 111 func GetStacks(err error) (stacks []pruntime.Stack) { 112 for err != nil { 113 if e, ok := err.(ErrorCallStacker); ok { 114 stack := e.StackTrace() 115 stacks = append([]pruntime.Stack{stack}, stacks...) 116 } 117 err = errors.Unwrap(err) 118 } 119 return 120 } 121 122 // DumpChain retrieves a space-separated string of 123 // error implementation type-names found in the error 124 // chain of err. 125 // err can be nil 126 // 127 // fmt.Println(Stack(errors.New("an error"))) 128 // *error116.errorStack *errors.errorString 129 func DumpChain(err error) (typeNames string) { 130 var strs []string 131 for err != nil { 132 strs = append(strs, fmt.Sprintf("%T", err)) 133 err = errors.Unwrap(err) 134 } 135 typeNames = strings.Join(strs, "\x20") 136 return 137 } 138 139 // DumpGo produces a newline-separated string of 140 // type-names and Go-syntax found in the error 141 // chain of err. 142 // err can be nil 143 func DumpGo(err error) (typeNames string) { 144 var strs []string 145 for ; err != nil; err = errors.Unwrap(err) { 146 strs = append(strs, fmt.Sprintf("%T %#[1]v", err)) 147 } 148 typeNames = strings.Join(strs, "\n") 149 return 150 } 151 152 /* 153 RecoverThread is a defer function for threads. 154 On panic, the onError function is invoked with an error 155 message that contains location information 156 */ 157 func RecoverThread(label string, onError func(err error)) { 158 if onError == nil { 159 panic(fmt.Errorf("%s: onError func nil", pruntime.NewCodeLocation(1).PackFunc())) 160 } 161 if v := recover(); v != nil { 162 err, ok := v.(error) 163 if !ok { 164 err = fmt.Errorf("non-error value: %v", v) 165 } 166 onError(fmt.Errorf("%s: %w", label, err)) 167 } 168 }