Skip to content

Commit

Permalink
Add special handling for *fmt.wrapError to make events in Sentry more…
Browse files Browse the repository at this point in the history
… useful
  • Loading branch information
Pr0Ger committed Feb 1, 2023
1 parent c57ac6a commit f146e2c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 26 deletions.
70 changes: 45 additions & 25 deletions sentry_core.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package logger

import (
"fmt"
"reflect"
"time"

Expand Down Expand Up @@ -125,39 +126,23 @@ func (s *SentryCore) captureEvent(ent zapcore.Entry, data *zapcore.MapObjectEnco
event.Message = ent.Message
event.Extra = data.Fields

for i := 0; i < 10 && errField != nil; i++ {
event.Exception = append(event.Exception, sentry.Exception{
Value: errField.Error(),
Type: reflect.TypeOf(errField).String(),
Stacktrace: extractStacktrace(errField),
})
switch wrapped := errField.(type) { //nolint:errorlint
case interface{ Unwrap() error }:
errField = wrapped.Unwrap()
case interface{ Cause() error }:
errField = wrapped.Cause()
default:
errField = nil
}
if errField != nil {
event.Exception = s.convertErrorToException(errField)
}

event.Threads = []sentry.Thread{{
ID: "current",
Current: true,
Crashed: ent.Level >= zapcore.DPanicLevel,
}}

if len(event.Exception) != 0 {
if event.Exception[0].Stacktrace == nil {
event.Exception[0].Stacktrace = newStacktrace()
}
event.Exception[0].ThreadID = "current"
event.Threads = []sentry.Thread{{
ID: "current",
Current: true,
Crashed: ent.Level >= zapcore.DPanicLevel,
}}
} else {
event.Threads = []sentry.Thread{{
ID: "current",
Stacktrace: newStacktrace(),
Current: true,
Crashed: ent.Level >= zapcore.DPanicLevel,
}}
event.Threads[0].Stacktrace = newStacktrace()
}

// event.Exception should be sorted such that the most recent error is last
Expand All @@ -169,6 +154,41 @@ func (s *SentryCore) captureEvent(ent zapcore.Entry, data *zapcore.MapObjectEnco
s.hub.CaptureEvent(event)
}

func (s *SentryCore) convertErrorToException(errValue error) []sentry.Exception {
exceptions := make([]sentry.Exception, 0)
firstMeaningfulError := -1
for i := 0; i < 10 && errValue != nil; i++ {
errorType := reflect.TypeOf(errValue).String()
exceptions = append(exceptions, sentry.Exception{
Value: errValue.Error(),
Type: errorType,
Stacktrace: extractStacktrace(errValue),
})

if errorType != "*fmt.wrapError" && firstMeaningfulError == -1 {
firstMeaningfulError = i
}

switch wrapped := errValue.(type) { //nolint:errorlint
case interface{ Unwrap() error }:
errValue = wrapped.Unwrap()
case interface{ Cause() error }:
errValue = wrapped.Cause()
default:
errValue = nil
}
}

// If the first errors are wrapped errors, we want to show actual error type instead of *fmt.wrapError
if firstMeaningfulError != -1 {
for i := 0; i < firstMeaningfulError; i++ {
exceptions[i].Type = fmt.Sprintf("wrapped<%s>", exceptions[firstMeaningfulError].Type)
}
}

return exceptions
}

func (s *SentryCore) Sync() error {
s.hub.Flush(30 * time.Second)
return nil
Expand Down
22 changes: 21 additions & 1 deletion sentry_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (suite *SentryCoreSuite) TestWriteChainedErrors() {
suite.Equal("simple error", event.Exception[1].Value)
suite.NotNil(event.Exception[1].Stacktrace)

suite.Equal("*fmt.wrapError", event.Exception[2].Type)
suite.Equal("wrapped<*errors.withStack>", event.Exception[2].Type)
suite.Equal("wrap with fmt.Errorf: simple error", event.Exception[2].Value)
suite.NotNil(event.Exception[2].Stacktrace)

Expand All @@ -248,6 +248,26 @@ func (suite *SentryCoreSuite) TestWriteChainedErrors() {
logger.Error("message with chained error", zap.Error(err))
}

func (suite *SentryCoreSuite) TestStrippingWrappedErrors() {
core := NewSentryCore(suite.hub).(*SentryCore)

err := stderrors.New("simple error")
err = fmt.Errorf("first wrap: %w", err)
err = fmt.Errorf("second wrap: %w", err)

exceptions := core.convertErrorToException(err)
suite.Len(exceptions, 3)

suite.Equal("wrapped<*errors.errorString>", exceptions[0].Type)
suite.Equal("second wrap: first wrap: simple error", exceptions[0].Value)

suite.Equal("wrapped<*errors.errorString>", exceptions[1].Type)
suite.Equal("first wrap: simple error", exceptions[1].Value)

suite.Equal("*errors.errorString", exceptions[2].Type)
suite.Equal("simple error", exceptions[2].Value)
}

func TestSentryCore(t *testing.T) {
suite.Run(t, new(SentryCoreSuite))
}

0 comments on commit f146e2c

Please sign in to comment.