errorreport.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package errorreport
  2. import (
  3. "context"
  4. "net/http"
  5. "github.com/imgproxy/imgproxy/v3/errorreport/airbrake"
  6. "github.com/imgproxy/imgproxy/v3/errorreport/bugsnag"
  7. "github.com/imgproxy/imgproxy/v3/errorreport/honeybadger"
  8. "github.com/imgproxy/imgproxy/v3/errorreport/sentry"
  9. )
  10. // reporter is an interface that all error reporters must implement.
  11. // most of our reporters are singletons, so in most cases close is noop.
  12. type reporter interface {
  13. Report(err error, req *http.Request, meta map[string]any)
  14. Close()
  15. }
  16. // metaCtxKey is the context.Context key for request metadata
  17. type metaCtxKey struct{}
  18. type Reporter struct {
  19. // initialized reporters
  20. reporters []reporter
  21. }
  22. // Init initializes all configured error reporters and returns a Reporter instance.
  23. func New(config *Config) (*Reporter, error) {
  24. if err := config.Validate(); err != nil {
  25. return nil, err
  26. }
  27. reporters := make([]reporter, 0)
  28. if r, err := bugsnag.New(&config.Bugsnag); err != nil {
  29. return nil, err
  30. } else if r != nil {
  31. reporters = append(reporters, r)
  32. }
  33. if r, err := honeybadger.New(&config.Honeybadger); err != nil {
  34. return nil, err
  35. } else if r != nil {
  36. reporters = append(reporters, r)
  37. }
  38. if r, err := sentry.New(&config.Sentry); err != nil {
  39. return nil, err
  40. } else if r != nil {
  41. reporters = append(reporters, r)
  42. }
  43. if r, err := airbrake.New(&config.Airbrake); err != nil {
  44. return nil, err
  45. } else if r != nil {
  46. reporters = append(reporters, r)
  47. }
  48. return &Reporter{
  49. reporters: reporters,
  50. }, nil
  51. }
  52. // StartRequest initializes metadata storage in the request context.
  53. func StartRequest(req *http.Request) context.Context {
  54. meta := make(map[string]any)
  55. return context.WithValue(req.Context(), metaCtxKey{}, meta)
  56. }
  57. // SetMetadata sets a metadata key-value pair in the request context.
  58. func SetMetadata(req *http.Request, key string, value any) {
  59. meta, ok := req.Context().Value(metaCtxKey{}).(map[string]any)
  60. if !ok || meta == nil {
  61. return
  62. }
  63. meta[key] = value
  64. }
  65. // Report reports an error to all configured reporters with the request and its metadata.
  66. func (r *Reporter) Report(err error, req *http.Request) {
  67. meta, ok := req.Context().Value(metaCtxKey{}).(map[string]any)
  68. if !ok {
  69. meta = nil
  70. }
  71. for _, reporter := range r.reporters {
  72. reporter.Report(err, req, meta)
  73. }
  74. }
  75. // Close closes all reporters
  76. func (r *Reporter) Close() {
  77. for _, reporter := range r.reporters {
  78. reporter.Close()
  79. }
  80. }