diff --git a/sentry.go b/sentry.go index ce50ba3..f5cc0ee 100644 --- a/sentry.go +++ b/sentry.go @@ -36,6 +36,10 @@ type SentryHook struct { extraFilters map[string]func(interface{}) interface{} } +type Stacktracer interface { + GetStacktrace() *raven.Stacktrace +} + // StackTraceConfiguration allows for configuring stacktraces type StackTraceConfiguration struct { // whether stacktraces should be enabled @@ -125,12 +129,18 @@ func (hook *SentryHook) Fire(entry *logrus.Entry) error { stConfig := &hook.StacktraceConfiguration if stConfig.Enable && entry.Level <= stConfig.Level { - currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes) if err, ok := getAndDelError(d, logrus.ErrorKey); ok { + var currentStacktrace *raven.Stacktrace + if stacktracer, ok := err.(Stacktracer); ok { + currentStacktrace = stacktracer.GetStacktrace() + } else { + currentStacktrace = raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes) + } exc := raven.NewException(err, currentStacktrace) packet.Interfaces = append(packet.Interfaces, exc) packet.Culprit = err.Error() } else { + currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes) packet.Interfaces = append(packet.Interfaces, currentStacktrace) } } diff --git a/sentry_test.go b/sentry_test.go index 7766f07..e30684d 100644 --- a/sentry_test.go +++ b/sentry_test.go @@ -36,6 +36,7 @@ func getTestLogger() *logrus.Logger { type resultPacket struct { raven.Packet Stacktrace raven.Stacktrace `json:"stacktrace"` + Exception raven.Exception `json:"exception"` } func WithTestDSN(t *testing.T, tf func(string, <-chan *resultPacket)) { @@ -195,7 +196,7 @@ func TestSentryStacktrace(t *testing.T) { hook.StacktraceConfiguration.Enable = true logger.Error(message) // this is the call that the last frame of stacktrace should capture - expectedLineno := 197 //this should be the line number of the previous line + expectedLineno := 198 //this should be the line number of the previous line packet = <-pch stacktraceSize = len(packet.Stacktrace.Frames) @@ -232,6 +233,16 @@ func TestSentryStacktrace(t *testing.T) { if !lastFrame.InApp { t.Error("Frame should be identified as in_app") } + + logger.WithError(myStacktracerError{}).Error(message) // use an error that implements Stacktracer + packet = <-pch + var frames []*raven.StacktraceFrame + if packet.Exception.Stacktrace != nil { + frames = packet.Exception.Stacktrace.Frames + } + if len(frames) != 1 || frames[0].Filename != escpectedStackFrameFilename { + t.Error("Stacktrace should be taken from err if it implements the Stacktracer interface") + } }) } @@ -392,3 +403,17 @@ func (myStringer) String() string { return "myStringer!" } type notStringer struct{} func (notStringer) String() {} + +type myStacktracerError struct{} + +func (myStacktracerError) Error() string { return "myStacktracerError!" } + +const escpectedStackFrameFilename = "errorFile.go" + +func (myStacktracerError) GetStacktrace() *raven.Stacktrace { + return &raven.Stacktrace{ + Frames: []*raven.StacktraceFrame{ + {Filename: escpectedStackFrameFilename}, + }, + } +}