Skip to content
This repository was archived by the owner on Aug 17, 2020. It is now read-only.

Commit 6be8558

Browse files
authored
Improves panic reports to scope and fixes source field on events (#170)
* Improves panic reports to scope and fixes source field on events * restore missing method * changes * Changes based in the review * fixes after rebase * path fixes after rebase
1 parent 9509c96 commit 6be8558

File tree

6 files changed

+96
-47
lines changed

6 files changed

+96
-47
lines changed

agent/agent.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,12 @@ func NewAgent(options ...Option) (*Agent, error) {
292292
agent.metadata[tags.Dependencies] = getDependencyMap()
293293

294294
// Expand '~' in source root
295+
var sourceRoot string
295296
if sRoot, ok := agent.metadata[tags.SourceRoot]; ok {
296297
cSRoot := sRoot.(string)
297298
cSRoot = filepath.Clean(cSRoot)
298299
if sRootEx, err := homedir.Expand(cSRoot); err == nil {
300+
sourceRoot = sRootEx
299301
agent.metadata[tags.SourceRoot] = sRootEx
300302
}
301303
}
@@ -335,10 +337,11 @@ func NewAgent(options ...Option) (*Agent, error) {
335337
},
336338
MaxLogsPerSpan: 10000,
337339
// Log the error in the current span
338-
OnSpanFinishPanic: scopeError.LogErrorInRawSpan,
340+
OnSpanFinishPanic: scopeError.WriteExceptionEventInRawSpan,
339341
})
340342
instrumentation.SetTracer(agent.tracer)
341343
instrumentation.SetLogger(agent.logger)
344+
instrumentation.SetSourceRoot(sourceRoot)
342345
if agent.setGlobalTracer || env.ScopeTracerGlobal.Value {
343346
opentracing.SetGlobalTracer(agent.Tracer())
344347
}
@@ -352,7 +355,7 @@ func (a *Agent) setupLogging() error {
352355
if err != nil {
353356
return err
354357
}
355-
a.recorderFilename = path.Join(dir, filename)
358+
a.recorderFilename = filepath.Join(dir, filename)
356359

357360
file, err := os.OpenFile(a.recorderFilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
358361
if err != nil {
@@ -442,7 +445,7 @@ func getLogPath() (string, error) {
442445
}
443446

444447
// If the log folder can't be used we return a temporal path, so we don't miss the agent logs
445-
logFolder = path.Join(os.TempDir(), "scope")
448+
logFolder = filepath.Join(os.TempDir(), "scope")
446449
if _, err := os.Stat(logFolder); err == nil {
447450
return logFolder, nil
448451
} else if os.IsNotExist(err) && os.Mkdir(logFolder, 0755) == nil {

errors/handler.go

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package errors
22

33
import (
4+
"context"
45
"fmt"
56
"path/filepath"
67
"strings"
@@ -10,6 +11,8 @@ import (
1011
"github.com/opentracing/opentracing-go"
1112
"github.com/opentracing/opentracing-go/log"
1213

14+
"go.undefinedlabs.com/scopeagent/instrumentation"
15+
"go.undefinedlabs.com/scopeagent/tags"
1316
"go.undefinedlabs.com/scopeagent/tracer"
1417
)
1518

@@ -28,32 +31,33 @@ type StackFrames struct {
2831
Package string
2932
}
3033

31-
var MarkSpanAsError = errors.New("")
34+
var markSpanAsError = errors.New("")
3235

3336
// Write exception event in span using the recover data from panic
34-
func LogError(span opentracing.Span, recoverData interface{}, skipFrames int) {
35-
var exceptionFields = getExceptionLogFields(recoverData, skipFrames+1)
36-
span.LogFields(exceptionFields...)
37+
func WriteExceptionEvent(span opentracing.Span, recoverData interface{}, skipFrames int) {
3738
span.SetTag("error", true)
39+
if recoverData == markSpanAsError {
40+
return
41+
}
42+
var exceptionFields = getExceptionLogFields("error", recoverData, skipFrames+1)
43+
span.LogFields(exceptionFields...)
3844
}
3945

40-
func LogErrorInRawSpan(rawSpan *tracer.RawSpan, err **errors.Error) {
46+
func WriteExceptionEventInRawSpan(rawSpan *tracer.RawSpan, err **errors.Error) {
4147
if rawSpan.Tags == nil {
4248
rawSpan.Tags = opentracing.Tags{}
4349
}
44-
if *err == MarkSpanAsError {
45-
rawSpan.Tags["error"] = true
46-
} else {
47-
var exceptionFields = getExceptionLogFields(*err, 1)
50+
rawSpan.Tags["error"] = true
51+
if *err != markSpanAsError {
52+
var exceptionFields = getExceptionLogFields("error", *err, 1)
4853
if rawSpan.Logs == nil {
4954
rawSpan.Logs = []opentracing.LogRecord{}
5055
}
5156
rawSpan.Logs = append(rawSpan.Logs, opentracing.LogRecord{
5257
Timestamp: time.Now(),
5358
Fields: exceptionFields,
5459
})
55-
rawSpan.Tags["error"] = true
56-
*err = MarkSpanAsError
60+
*err = markSpanAsError
5761
}
5862
}
5963

@@ -80,6 +84,17 @@ func getCurrentStackFrames(skip int) []StackFrames {
8084
return stackFrames
8185
}
8286

87+
// Write log event with stacktrace in span using the recover data from panic
88+
func LogPanic(ctx context.Context, recoverData interface{}, skipFrames int) {
89+
span := opentracing.SpanFromContext(ctx)
90+
if span == nil {
91+
return
92+
}
93+
var exceptionFields = getExceptionLogFields(tags.LogEvent, recoverData, skipFrames+1)
94+
exceptionFields = append(exceptionFields, log.String(tags.LogEventLevel, tags.LogLevel_ERROR))
95+
span.LogFields(exceptionFields...)
96+
}
97+
8398
// Gets the current stacktrace
8499
func GetCurrentStackTrace(skip int) map[string]interface{} {
85100
var exFrames []map[string]interface{}
@@ -101,25 +116,27 @@ func GetCurrentError(recoverData interface{}) *errors.Error {
101116
return errors.Wrap(recoverData, 1)
102117
}
103118

104-
func getExceptionLogFields(recoverData interface{}, skipFrames int) []log.Field {
119+
func getExceptionLogFields(eventType string, recoverData interface{}, skipFrames int) []log.Field {
105120
if recoverData != nil {
106121
err := errors.Wrap(recoverData, 2+skipFrames)
107122
errMessage := err.Error()
108-
errStack := err.StackFrames() //filterStackFrames(err.StackFrames())
123+
errStack := err.StackFrames()
109124
exceptionData := getExceptionFrameData(errMessage, errStack)
110125
source := ""
111126

112127
if errStack != nil && len(errStack) > 0 {
128+
sourceRoot := instrumentation.GetSourceRoot()
113129
for _, currentFrame := range errStack {
114-
if currentFrame.Package != "runtime" && currentFrame.File != "" {
130+
dir := filepath.Dir(currentFrame.File)
131+
if strings.Index(dir, sourceRoot) != -1 {
115132
source = fmt.Sprintf("%s:%d", currentFrame.File, currentFrame.LineNumber)
116133
break
117134
}
118135
}
119136
}
120137

121138
fields := make([]log.Field, 5)
122-
fields[0] = log.String(EventType, "error")
139+
fields[0] = log.String(EventType, eventType)
123140
fields[1] = log.String(EventSource, source)
124141
fields[2] = log.String(EventMessage, errMessage)
125142
fields[3] = log.String(EventStack, getStringStack(err, errStack))
@@ -137,18 +154,6 @@ func getStringStack(err *errors.Error, errStack []errors.StackFrame) string {
137154
return fmt.Sprintf("[%s]: %s\n\n%s", err.TypeName(), err.Error(), strings.Join(frames, ""))
138155
}
139156

140-
// Filter stack frames from the go-agent
141-
func filterStackFrames(errStack []errors.StackFrame) []errors.StackFrame {
142-
var stack []errors.StackFrame
143-
for _, frame := range errStack {
144-
if strings.Contains(frame.Package, "undefinedlabs/go-agent") {
145-
continue
146-
}
147-
stack = append(stack, frame)
148-
}
149-
return stack
150-
}
151-
152157
func getExceptionFrameData(errMessage string, errStack []errors.StackFrame) map[string]interface{} {
153158
var exFrames []map[string]interface{}
154159
for _, frame := range errStack {

instrumentation/nethttp/server.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ func middlewareFunc(tr opentracing.Tracer, h http.HandlerFunc, options ...MWOpti
181181
}
182182

183183
if r := recover(); r != nil {
184-
if r != errors.MarkSpanAsError {
185-
errors.LogError(sp, r, 1)
186-
}
184+
errors.WriteExceptionEvent(sp, r, 1)
187185
sp.Finish()
188186
panic(r)
189187
}

instrumentation/testing/tb.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package testing
22

33
import (
44
"fmt"
5-
"github.com/opentracing/opentracing-go/log"
6-
"go.undefinedlabs.com/scopeagent/tags"
75
"path/filepath"
86
"runtime"
7+
8+
"github.com/opentracing/opentracing-go/log"
9+
10+
"go.undefinedlabs.com/scopeagent/errors"
11+
"go.undefinedlabs.com/scopeagent/instrumentation"
12+
"go.undefinedlabs.com/scopeagent/tags"
913
)
1014

1115
// ***************************
@@ -217,15 +221,19 @@ func (test *Test) Helper() {
217221
test.t.Helper()
218222
}
219223

224+
// Log panic data with stacktrace
225+
func (test *Test) LogPanic(recoverData interface{}, skipFrames int) {
226+
errors.LogPanic(test.ctx, recoverData, skipFrames+1)
227+
}
228+
220229
func getSourceFileAndNumber() string {
221230
var source string
222-
if pc, file, line, ok := runtime.Caller(2); ok == true {
231+
if pc, file, line, ok := instrumentation.GetCallerInsideSourceRoot(2); ok == true {
223232
pcEntry := runtime.FuncForPC(pc).Entry()
224233
// Try to detect the patch function
225234
if isAPatchPointer(pcEntry) {
226235
// The monkey patching version adds 4 frames to the stack.
227-
if _, file, line, ok := runtime.Caller(6); ok == true {
228-
file = filepath.Clean(file)
236+
if _, file, line, ok := instrumentation.GetCallerInsideSourceRoot(6); ok == true {
229237
source = fmt.Sprintf("%s:%d", file, line)
230238
}
231239
} else {

instrumentation/testing/testing.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,7 @@ func (test *Test) end() {
172172

173173
if r := recover(); r != nil {
174174
test.span.SetTag("test.status", tags.TestStatus_FAIL)
175-
test.span.SetTag("error", true)
176-
if r != errors.MarkSpanAsError {
177-
errors.LogError(test.span, r, 1)
178-
}
175+
errors.WriteExceptionEvent(test.span, r, 1)
179176
test.span.FinishWithOptions(finishOptions)
180177
panic(r)
181178
}
@@ -262,8 +259,7 @@ func PanicAllRunningTests(e interface{}, skip int) {
262259
for _, v := range tmp {
263260
delete(autoInstrumentedTests, v.t)
264261
v.t.Fail()
265-
v.span.SetTag("error", true)
266-
errors.LogError(v.span, e, 1+skip)
262+
errors.WriteExceptionEvent(v.span, e, 1+skip)
267263
v.end()
268264
}
269265
}

instrumentation/tracer.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package instrumentation
22

33
import (
4-
"github.com/opentracing/opentracing-go"
54
"io/ioutil"
65
"log"
6+
"path/filepath"
7+
"runtime"
8+
"strings"
79
"sync"
10+
11+
"github.com/opentracing/opentracing-go"
812
)
913

1014
var (
11-
tracer opentracing.Tracer = opentracing.NoopTracer{}
12-
logger = log.New(ioutil.Discard, "", 0)
15+
tracer opentracing.Tracer = opentracing.NoopTracer{}
16+
logger = log.New(ioutil.Discard, "", 0)
17+
sourceRoot = ""
1318

1419
m sync.RWMutex
1520
)
@@ -41,3 +46,37 @@ func Logger() *log.Logger {
4146

4247
return logger
4348
}
49+
50+
func SetSourceRoot(root string) {
51+
m.Lock()
52+
defer m.Unlock()
53+
54+
sourceRoot = root
55+
}
56+
57+
func GetSourceRoot() string {
58+
m.RLock()
59+
defer m.RUnlock()
60+
61+
return sourceRoot
62+
}
63+
64+
//go:noinline
65+
func GetCallerInsideSourceRoot(skip int) (pc uintptr, file string, line int, ok bool) {
66+
pcs := make([]uintptr, 64)
67+
count := runtime.Callers(skip+2, pcs)
68+
pcs = pcs[0:count]
69+
frames := runtime.CallersFrames(pcs)
70+
for {
71+
frame, more := frames.Next()
72+
file := filepath.Clean(frame.File)
73+
dir := filepath.Dir(file)
74+
if strings.Index(dir, sourceRoot) != -1 {
75+
return frame.PC, file, frame.Line, true
76+
}
77+
if !more {
78+
break
79+
}
80+
}
81+
return
82+
}

0 commit comments

Comments
 (0)