go.undefinedlabs.com/scopeagent@v0.4.2/errors/handler.go (about) 1 package errors 2 3 import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/go-errors/errors" 11 "github.com/opentracing/opentracing-go" 12 "github.com/opentracing/opentracing-go/log" 13 14 "go.undefinedlabs.com/scopeagent/instrumentation" 15 "go.undefinedlabs.com/scopeagent/tags" 16 "go.undefinedlabs.com/scopeagent/tracer" 17 ) 18 19 type StackFrames struct { 20 File string 21 LineNumber int 22 Name string 23 Package string 24 } 25 26 var markSpanAsError = errors.New("") 27 28 // Write exception event in span using the recover data from panic 29 func WriteExceptionEvent(span opentracing.Span, recoverData interface{}, skipFrames int) { 30 span.SetTag("error", true) 31 if recoverData == markSpanAsError { 32 return 33 } 34 var exceptionFields = getExceptionLogFields("error", recoverData, skipFrames+1) 35 span.LogFields(exceptionFields...) 36 } 37 38 func WriteExceptionEventInRawSpan(rawSpan *tracer.RawSpan, err **errors.Error) { 39 if rawSpan.Tags == nil { 40 rawSpan.Tags = opentracing.Tags{} 41 } 42 rawSpan.Tags["error"] = true 43 if *err != markSpanAsError { 44 var exceptionFields = getExceptionLogFields("error", *err, 1) 45 if rawSpan.Logs == nil { 46 rawSpan.Logs = []opentracing.LogRecord{} 47 } 48 rawSpan.Logs = append(rawSpan.Logs, opentracing.LogRecord{ 49 Timestamp: time.Now(), 50 Fields: exceptionFields, 51 }) 52 *err = markSpanAsError 53 } 54 } 55 56 // Gets the current stack frames array 57 func getCurrentStackFrames(skip int) []StackFrames { 58 skip = skip + 1 59 err := errors.New(nil) 60 errStack := err.StackFrames() 61 nLength := len(errStack) - skip 62 if nLength < 0 { 63 return nil 64 } 65 stackFrames := make([]StackFrames, 0) 66 for idx, frame := range errStack { 67 if idx >= skip { 68 stackFrames = append(stackFrames, StackFrames{ 69 File: filepath.Clean(frame.File), 70 LineNumber: frame.LineNumber, 71 Name: frame.Name, 72 Package: frame.Package, 73 }) 74 } 75 } 76 return stackFrames 77 } 78 79 // Write log event with stacktrace in span using the recover data from panic 80 func LogPanic(ctx context.Context, recoverData interface{}, skipFrames int) { 81 span := opentracing.SpanFromContext(ctx) 82 if span == nil { 83 return 84 } 85 var exceptionFields = getExceptionLogFields(tags.LogEvent, recoverData, skipFrames+1) 86 exceptionFields = append(exceptionFields, log.String(tags.LogEventLevel, tags.LogLevel_ERROR)) 87 span.LogFields(exceptionFields...) 88 } 89 90 // Gets the current stacktrace 91 func GetCurrentStackTrace(skip int) map[string]interface{} { 92 var exFrames []map[string]interface{} 93 for _, frame := range getCurrentStackFrames(skip + 1) { 94 exFrames = append(exFrames, map[string]interface{}{ 95 "name": frame.Name, 96 "module": frame.Package, 97 "file": frame.File, 98 "line": frame.LineNumber, 99 }) 100 } 101 return map[string]interface{}{ 102 "frames": exFrames, 103 } 104 } 105 106 // Get the current error with the fixed stacktrace 107 func GetCurrentError(recoverData interface{}) *errors.Error { 108 return errors.Wrap(recoverData, 1) 109 } 110 111 func getExceptionLogFields(eventType string, recoverData interface{}, skipFrames int) []log.Field { 112 if recoverData != nil { 113 err := errors.Wrap(recoverData, 2+skipFrames) 114 errMessage := err.Error() 115 errStack := err.StackFrames() 116 exceptionData := getExceptionFrameData(errMessage, errStack) 117 source := "" 118 119 if errStack != nil && len(errStack) > 0 { 120 sourceRoot := instrumentation.GetSourceRoot() 121 for _, currentFrame := range errStack { 122 dir := filepath.Dir(currentFrame.File) 123 if strings.Index(dir, sourceRoot) != -1 { 124 source = fmt.Sprintf("%s:%d", currentFrame.File, currentFrame.LineNumber) 125 break 126 } 127 } 128 } 129 130 fields := make([]log.Field, 5) 131 fields[0] = log.String(tags.EventType, eventType) 132 fields[1] = log.String(tags.EventSource, source) 133 fields[2] = log.String(tags.EventMessage, errMessage) 134 fields[3] = log.String(tags.EventStack, getStringStack(err, errStack)) 135 fields[4] = log.Object(tags.EventException, exceptionData) 136 return fields 137 } 138 return nil 139 } 140 141 func getStringStack(err *errors.Error, errStack []errors.StackFrame) string { 142 var frames []string 143 for _, frame := range errStack { 144 frames = append(frames, frame.String()) 145 } 146 return fmt.Sprintf("[%s]: %s\n\n%s", err.TypeName(), err.Error(), strings.Join(frames, "")) 147 } 148 149 func getExceptionFrameData(errMessage string, errStack []errors.StackFrame) map[string]interface{} { 150 var exFrames []map[string]interface{} 151 for _, frame := range errStack { 152 exFrames = append(exFrames, map[string]interface{}{ 153 "name": frame.Name, 154 "module": frame.Package, 155 "file": filepath.Clean(frame.File), 156 "line": frame.LineNumber, 157 }) 158 } 159 exStack := map[string]interface{}{ 160 "frames": exFrames, 161 } 162 return map[string]interface{}{ 163 "message": errMessage, 164 "stacktrace": exStack, 165 } 166 }