package sentryecho import ( "context" "net/http" "time" "github.com/getsentry/sentry-go" "github.com/labstack/echo/v4" ) // The identifier of the Echo SDK. const sdkIdentifier = "sentry.go.echo" const valuesKey = "sentry" type handler struct { repanic bool waitForDelivery bool timeout time.Duration } type Options struct { // Repanic configures whether Sentry should repanic after recovery, in most cases it should be set to true, // as echo includes it's own Recover middleware what handles http responses. Repanic bool // WaitForDelivery configures whether you want to block the request before moving forward with the response. // Because Echo's Recover handler doesn't restart the application, // it's safe to either skip this option or set it to false. WaitForDelivery bool // Timeout for the event delivery requests. Timeout time.Duration } // New returns a function that satisfies echo.HandlerFunc interface // It can be used with Use() methods. func New(options Options) echo.MiddlewareFunc { timeout := options.Timeout if timeout == 0 { timeout = 2 * time.Second } return (&handler{ repanic: options.Repanic, timeout: timeout, waitForDelivery: options.WaitForDelivery, }).handle } func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc { return func(ctx echo.Context) error { hub := sentry.GetHubFromContext(ctx.Request().Context()) if hub == nil { hub = sentry.CurrentHub().Clone() } if client := hub.Client(); client != nil { client.SetSDKIdentifier(sdkIdentifier) } hub.Scope().SetRequest(ctx.Request()) ctx.Set(valuesKey, hub) defer h.recoverWithSentry(hub, ctx.Request()) return next(ctx) } } func (h *handler) recoverWithSentry(hub *sentry.Hub, r *http.Request) { if err := recover(); err != nil { eventID := hub.RecoverWithContext( context.WithValue(r.Context(), sentry.RequestContextKey, r), err, ) if eventID != nil && h.waitForDelivery { hub.Flush(h.timeout) } if h.repanic { panic(err) } } } // GetHubFromContext retrieves attached *sentry.Hub instance from echo.Context. func GetHubFromContext(ctx echo.Context) *sentry.Hub { if hub, ok := ctx.Get(valuesKey).(*sentry.Hub); ok { return hub } return nil }