sentry.go 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. package sentry
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/getsentry/sentry-go"
  6. )
  7. const (
  8. // flushTimeout is the maximum time to wait for Sentry to send events
  9. flushTimeout = 5 * time.Second
  10. )
  11. // reporter is a Sentry error reporter
  12. type reporter struct{}
  13. // New creates and configures a new Sentry reporter
  14. func New(config *Config) (*reporter, error) {
  15. if err := config.Validate(); err != nil {
  16. return nil, err
  17. }
  18. if len(config.DSN) == 0 {
  19. return nil, nil
  20. }
  21. sentry.Init(sentry.ClientOptions{
  22. Dsn: config.DSN,
  23. Release: config.Release,
  24. Environment: config.Environment,
  25. })
  26. return &reporter{}, nil
  27. }
  28. func (r *reporter) Report(err error, req *http.Request, meta map[string]any) {
  29. hub := sentry.CurrentHub().Clone()
  30. hub.Scope().SetRequest(req)
  31. hub.Scope().SetLevel(sentry.LevelError)
  32. if meta != nil {
  33. hub.Scope().SetContext("Processing context", meta)
  34. }
  35. // imgproxy wraps almost all errors into *ierrors.Error, so Sentry will show
  36. // the same error type for all errors. We need to fix it.
  37. //
  38. // Instead of using hub.CaptureException(err), we need to create an event
  39. // manually and replace `*ierrors.Error` with the wrapped error type
  40. // (which is the previous exception type in the exception chain).
  41. if event := hub.Client().EventFromException(err, sentry.LevelError); event != nil {
  42. for i := 1; i < len(event.Exception); i++ {
  43. if event.Exception[i].Type == "*ierrors.Error" {
  44. event.Exception[i].Type = event.Exception[i-1].Type
  45. }
  46. }
  47. eventID := hub.CaptureEvent(event)
  48. if eventID != nil {
  49. hub.Flush(flushTimeout)
  50. }
  51. }
  52. }
  53. func (r *reporter) Close() {
  54. sentry.Flush(flushTimeout)
  55. }