diff --git a/env/vars.go b/env/vars.go index 2b202cb6..bd726a20 100644 --- a/env/vars.go +++ b/env/vars.go @@ -20,5 +20,7 @@ var ( ScopeConfiguration = newSliceEnvVar([]string{tags.PlatformName, tags.PlatformArchitecture, tags.GoVersion}, "SCOPE_CONFIGURATION") ScopeMetadata = newMapEnvVar(nil, "SCOPE_METADATA") ScopeInstrumentationHttpPayloads = newBooleanEnvVar(false, "SCOPE_INSTRUMENTATION_HTTP_PAYLOADS") + ScopeInstrumentationHttpStacktrace = newBooleanEnvVar(false, "SCOPE_INSTRUMENTATION_HTTP_STACKTRACE") ScopeInstrumentationDbStatementValues = newBooleanEnvVar(false, "SCOPE_INSTRUMENTATION_DB_STATEMENT_VALUES") + ScopeInstrumentationDbStacktrace = newBooleanEnvVar(false, "SCOPE_INSTRUMENTATION_DB_STACKTRACE") ) diff --git a/errors/handler.go b/errors/handler.go index 6b20a8ce..f3ec2020 100644 --- a/errors/handler.go +++ b/errors/handler.go @@ -2,6 +2,7 @@ package errors import ( "fmt" + "path/filepath" "strings" "time" @@ -57,7 +58,7 @@ func LogErrorInRawSpan(rawSpan *tracer.RawSpan, err **errors.Error) { } // Gets the current stack frames array -func GetCurrentStackFrames(skip int) []StackFrames { +func getCurrentStackFrames(skip int) []StackFrames { skip = skip + 1 err := errors.New(nil) errStack := err.StackFrames() @@ -65,20 +66,36 @@ func GetCurrentStackFrames(skip int) []StackFrames { if nLength < 0 { return nil } - stackFrames := make([]StackFrames, nLength) + stackFrames := make([]StackFrames, 0) for idx, frame := range errStack { if idx >= skip { - stackFrames[idx-skip] = StackFrames{ - File: frame.File, + stackFrames = append(stackFrames, StackFrames{ + File: filepath.Clean(frame.File), LineNumber: frame.LineNumber, Name: frame.Name, Package: frame.Package, - } + }) } } return stackFrames } +// Gets the current stacktrace +func GetCurrentStackTrace(skip int) map[string]interface{} { + var exFrames []map[string]interface{} + for _, frame := range getCurrentStackFrames(skip + 1) { + exFrames = append(exFrames, map[string]interface{}{ + "name": frame.Name, + "module": frame.Package, + "file": frame.File, + "line": frame.LineNumber, + }) + } + return map[string]interface{}{ + "frames": exFrames, + } +} + // Get the current error with the fixed stacktrace func GetCurrentError(recoverData interface{}) *errors.Error { return errors.Wrap(recoverData, 1) @@ -138,7 +155,7 @@ func getExceptionFrameData(errMessage string, errStack []errors.StackFrame) map[ exFrames = append(exFrames, map[string]interface{}{ "name": frame.Name, "module": frame.Package, - "file": frame.File, + "file": filepath.Clean(frame.File), "line": frame.LineNumber, }) } diff --git a/instrumentation/nethttp/client.go b/instrumentation/nethttp/client.go index 1f72ae9c..274a5b7a 100644 --- a/instrumentation/nethttp/client.go +++ b/instrumentation/nethttp/client.go @@ -14,6 +14,7 @@ import ( "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/log" + scopeerrors "go.undefinedlabs.com/scopeagent/errors" "go.undefinedlabs.com/scopeagent/instrumentation" ) @@ -35,6 +36,9 @@ type Transport struct { // Enable payload instrumentation PayloadInstrumentation bool + + // Enable stacktrace + Stacktrace bool } type clientOptions struct { @@ -178,6 +182,10 @@ func (t *Transport) doRoundTrip(req *http.Request) (*http.Response, error) { tracer.start(req) + if t.Stacktrace { + tracer.sp.SetTag("stacktrace", scopeerrors.GetCurrentStackTrace(2)) + } + ext.HTTPMethod.Set(tracer.sp, req.Method) ext.HTTPUrl.Set(tracer.sp, req.URL.String()) tracer.opts.spanObserver(tracer.sp, req) diff --git a/instrumentation/nethttp/nethttp_test.go b/instrumentation/nethttp/nethttp_test.go index ffbedc01..1efab075 100644 --- a/instrumentation/nethttp/nethttp_test.go +++ b/instrumentation/nethttp/nethttp_test.go @@ -16,7 +16,7 @@ import ( var r *tracer.InMemorySpanRecorder func TestMain(m *testing.M) { - PatchHttpDefaultClient(WithPayloadInstrumentation()) + PatchHttpDefaultClient(WithPayloadInstrumentation(), WithStacktrace()) // Test tracer r = tracer.NewInMemoryRecorder() diff --git a/instrumentation/nethttp/patch.go b/instrumentation/nethttp/patch.go index ac0359f5..8b39fb37 100644 --- a/instrumentation/nethttp/patch.go +++ b/instrumentation/nethttp/patch.go @@ -17,6 +17,13 @@ func WithPayloadInstrumentation() Option { } } +// Enables stacktrace +func WithStacktrace() Option { + return func(t *Transport) { + t.Stacktrace = true + } +} + // Patches the default http client with the instrumented transport func PatchHttpDefaultClient(options ...Option) { once.Do(func() { @@ -25,6 +32,7 @@ func PatchHttpDefaultClient(options ...Option) { option(transport) } transport.PayloadInstrumentation = transport.PayloadInstrumentation || env.ScopeInstrumentationHttpPayloads.Value + transport.Stacktrace = transport.Stacktrace || env.ScopeInstrumentationHttpStacktrace.Value http.DefaultClient = &http.Client{Transport: transport} }) } diff --git a/instrumentation/sql/driver.go b/instrumentation/sql/driver.go index 2e3661e2..5b235f22 100644 --- a/instrumentation/sql/driver.go +++ b/instrumentation/sql/driver.go @@ -5,11 +5,14 @@ import ( "database/sql/driver" "errors" "fmt" + "reflect" + "strings" + "github.com/opentracing/opentracing-go" + "go.undefinedlabs.com/scopeagent/env" + scopeerrors "go.undefinedlabs.com/scopeagent/errors" "go.undefinedlabs.com/scopeagent/instrumentation" - "reflect" - "strings" ) type ( @@ -22,6 +25,7 @@ type ( driverConfiguration struct { t opentracing.Tracer statementValues bool + stacktrace bool connString string componentName string peerService string @@ -41,6 +45,13 @@ func WithStatementValues() Option { } } +// Enable span stacktrace +func WithStacktrace() Option { + return func(d *instrumentedDriver) { + d.configuration.stacktrace = true + } +} + // Wraps the current sql driver to add instrumentation func WrapDriver(d driver.Driver, options ...Option) driver.Driver { wrapper := &instrumentedDriver{ @@ -54,6 +65,7 @@ func WrapDriver(d driver.Driver, options ...Option) driver.Driver { option(wrapper) } wrapper.configuration.statementValues = wrapper.configuration.statementValues || env.ScopeInstrumentationDbStatementValues.Value + wrapper.configuration.stacktrace = wrapper.configuration.stacktrace || env.ScopeInstrumentationDbStacktrace.Value return wrapper } @@ -106,6 +118,11 @@ func (t *driverConfiguration) newSpan(operationName string, query string, args [ "db.instance": c.instance, "peer.hostname": c.host, }) + if t.stacktrace { + opts = append(opts, opentracing.Tags{ + "stacktrace": scopeerrors.GetCurrentStackTrace(2), + }) + } if query != "" { stIndex := strings.IndexRune(query, ' ') var method string