errortracking/sentry_tracker.go (65 lines of code) (raw):

package errortracking import ( "reflect" "github.com/getsentry/sentry-go" ) // maxErrorDepth is the maximum number of errors reported in a chain of errors. // This protects the SDK from an arbitrarily long chain of wrapped errors. // // An additional consideration is that arguably reporting a long chain of errors // is of little use when debugging production errors with Sentry. The Sentry UI // is not optimized for long chains either. The top-level error together with a // stack trace is often the most useful information. const maxErrorDepth = 10 type hub interface { CaptureEvent(event *sentry.Event) *sentry.EventID } type sentryTracker struct { hub hub } func newSentryTracker(hub hub) *sentryTracker { return &sentryTracker{ hub: hub, } } func (s *sentryTracker) Capture(err error, opts ...CaptureOption) { cfg, event := applyCaptureOptions(opts) if cfg.attachStackTrace { attachExceptions(event, err) } else { singleException(event, err) } s.hub.CaptureEvent(event) } func singleException(event *sentry.Event, err error) { event.Exception = []sentry.Exception{ { Type: reflect.TypeOf(err).String(), Value: err.Error(), Stacktrace: sentry.ExtractStacktrace(err), }, } } // attachExceptions is a modified copy of https://github.com/getsentry/sentry-go/blob/d5877e5a5ddc56d2fd9b48b7a5162a5d7ee47cd8/client.go // which allows the stack trace with maxErrorDepth to be attached. This allows the event to keep the CaptureOptions // in place, as using sentry.CaptureException would remove those fields. func attachExceptions(event *sentry.Event, err error) { if err == nil { return } for i := 0; i < maxErrorDepth && err != nil; i++ { event.Exception = append(event.Exception, sentry.Exception{ Value: err.Error(), Type: reflect.TypeOf(err).String(), Stacktrace: sentry.ExtractStacktrace(err), }) switch previous := err.(type) { case interface{ Unwrap() error }: err = previous.Unwrap() case interface{ Cause() error }: err = previous.Cause() default: err = nil } } // Add a trace of the current stack to the most recent error in a chain if // it doesn't have a stack trace yet. // We only add to the most recent error to avoid duplication and because the // current stack is most likely unrelated to errors deeper in the chain. if event.Exception[0].Stacktrace == nil { event.Exception[0].Stacktrace = sentry.NewStacktrace() } // event.Exception should be sorted such that the most recent error is last. reverse(event.Exception) } // reverse reverses the slice a in place. func reverse(a []sentry.Exception) { for i := len(a)/2 - 1; i >= 0; i-- { opp := len(a) - 1 - i a[i], a[opp] = a[opp], a[i] } }