forked from vikunja/vikunja
konrad
d02d413c5e
Use sentry echo integration to send errors Only capture errors not already handled by echo Add sentry panic handler Add sentry library Add sentry init Add sentry config Co-authored-by: kolaente <k@knt.li> Reviewed-on: vikunja/api#591
88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
package sentryecho
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
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 {
|
|
handler := handler{
|
|
repanic: false,
|
|
timeout: time.Second * 2,
|
|
waitForDelivery: false,
|
|
}
|
|
|
|
if options.Repanic {
|
|
handler.repanic = true
|
|
}
|
|
|
|
if options.Timeout != 0 {
|
|
handler.timeout = options.Timeout
|
|
}
|
|
|
|
if options.WaitForDelivery {
|
|
handler.waitForDelivery = true
|
|
}
|
|
|
|
return handler.handle
|
|
}
|
|
|
|
func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(ctx echo.Context) error {
|
|
hub := sentry.CurrentHub().Clone()
|
|
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
|
|
}
|