github.com/timandy/routine@v1.1.4-0.20240507073150-e4a3e1fe2ba5/error.go (about) 1 package routine 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "runtime" 8 "strconv" 9 "unicode" 10 ) 11 12 const ( 13 newLine = "\n" 14 innerErrorPrefix = " ---> " 15 endOfInnerErrorStack = "--- End of inner error stack trace ---" 16 endOfErrorStack = "--- End of error stack trace ---" 17 wordAt = "at" 18 wordIn = "in" 19 wordCreatedBy = "created by" 20 ) 21 22 type runtimeError struct { 23 goid int64 24 gopc uintptr 25 message string 26 stackTrace []uintptr 27 cause RuntimeError 28 } 29 30 func (re *runtimeError) Goid() int64 { 31 return re.goid 32 } 33 34 func (re *runtimeError) Gopc() uintptr { 35 return re.gopc 36 } 37 38 func (re *runtimeError) Message() string { 39 return re.message 40 } 41 42 func (re *runtimeError) StackTrace() []uintptr { 43 return re.stackTrace 44 } 45 46 func (re *runtimeError) Cause() RuntimeError { 47 return re.cause 48 } 49 50 func (re *runtimeError) Error() string { 51 return runtimeErrorError(re) 52 } 53 54 func runtimeErrorNew(cause any) (goid int64, gopc uintptr, msg string, stackTrace []uintptr, innerErr RuntimeError) { 55 runtimeErr, isRuntimeErr := cause.(RuntimeError) 56 if !isRuntimeErr { 57 if err, isErr := cause.(error); isErr { 58 msg = err.Error() 59 } else if cause != nil { 60 msg = fmt.Sprint(cause) 61 } 62 } 63 gp := getg() 64 return gp.goid, *gp.gopc, msg, captureStackTrace(2, 100), runtimeErr 65 } 66 67 func runtimeErrorNewWithMessage(message string) (goid int64, gopc uintptr, msg string, stackTrace []uintptr, innerErr RuntimeError) { 68 gp := getg() 69 return gp.goid, *gp.gopc, message, captureStackTrace(2, 100), nil 70 } 71 72 func runtimeErrorNewWithMessageCause(message string, cause any) (goid int64, gopc uintptr, msg string, stackTrace []uintptr, innerErr RuntimeError) { 73 runtimeErr, isRuntimeErr := cause.(RuntimeError) 74 if !isRuntimeErr { 75 causeMsg := "" 76 if err, isErr := cause.(error); isErr { 77 causeMsg = err.Error() 78 } else if cause != nil { 79 causeMsg = fmt.Sprint(cause) 80 } 81 if len(message) == 0 { 82 message = causeMsg 83 } else if len(causeMsg) != 0 { 84 message += " - " + causeMsg 85 } 86 } 87 gp := getg() 88 return gp.goid, *gp.gopc, message, captureStackTrace(2, 100), runtimeErr 89 } 90 91 func runtimeErrorError(re RuntimeError) string { 92 builder := &bytes.Buffer{} 93 runtimeErrorPrintStackTrace(re, builder) 94 runtimeErrorPrintCreatedBy(re, builder) 95 return builder.String() 96 } 97 98 func runtimeErrorPrintStackTrace(re RuntimeError, builder *bytes.Buffer) { 99 builder.WriteString(runtimeErrorTypeName(re)) 100 message := re.Message() 101 if len(message) > 0 { 102 builder.WriteString(": ") 103 builder.WriteString(message) 104 } 105 cause := re.Cause() 106 if cause != nil { 107 builder.WriteString(newLine) 108 builder.WriteString(innerErrorPrefix) 109 runtimeErrorPrintStackTrace(cause, builder) 110 builder.WriteString(newLine) 111 builder.WriteString(" ") 112 builder.WriteString(endOfInnerErrorStack) 113 } 114 stackTrace := re.StackTrace() 115 if stackTrace != nil { 116 savePoint := builder.Len() 117 skippedPanic := false 118 frames := runtime.CallersFrames(stackTrace) 119 for { 120 frame, more := frames.Next() 121 if showFrame(frame.Function) { 122 builder.WriteString(newLine) 123 builder.WriteString(" ") 124 builder.WriteString(wordAt) 125 builder.WriteString(" ") 126 builder.WriteString(frame.Function) 127 builder.WriteString("() ") 128 builder.WriteString(wordIn) 129 builder.WriteString(" ") 130 builder.WriteString(frame.File) 131 builder.WriteString(":") 132 builder.WriteString(strconv.Itoa(frame.Line)) 133 } else if skipFrame(frame.Function, skippedPanic) { 134 builder.Truncate(savePoint) 135 skippedPanic = true 136 } 137 if !more { 138 break 139 } 140 } 141 } 142 } 143 144 func runtimeErrorPrintCreatedBy(re RuntimeError, builder *bytes.Buffer) { 145 goid := re.Goid() 146 if goid == 1 { 147 return 148 } 149 pc := re.Gopc() 150 frame, _ := runtime.CallersFrames([]uintptr{pc}).Next() 151 if frame.Func == nil { 152 return 153 } 154 builder.WriteString(newLine) 155 builder.WriteString(" ") 156 builder.WriteString(endOfErrorStack) 157 builder.WriteString(newLine) 158 builder.WriteString(" ") 159 builder.WriteString(wordCreatedBy) 160 builder.WriteString(" ") 161 builder.WriteString(frame.Function) 162 builder.WriteString("() ") 163 builder.WriteString(wordIn) 164 builder.WriteString(" ") 165 builder.WriteString(frame.File) 166 builder.WriteString(":") 167 builder.WriteString(strconv.Itoa(frame.Line)) 168 } 169 170 func runtimeErrorTypeName(re RuntimeError) string { 171 typeName := []rune(reflect.TypeOf(re).Elem().Name()) 172 typeName[0] = unicode.ToUpper(typeName[0]) 173 return string(typeName) 174 }