Skip to content
Snippets Groups Projects
panic_middleware.go 1.17 KiB
Newer Older
package orusapi

import (
Axel Prel's avatar
Axel Prel committed
	"context"
	"fmt"
	"net/http"
	"runtime/debug"

	"github.com/getsentry/sentry-go"
	jsoniter "github.com/json-iterator/go"
	"github.com/rs/zerolog"
)

// CatchPanics catches the panics and log them as errors.
func CatchPanics(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
Axel Prel's avatar
Axel Prel committed
		defer func(ctx context.Context) {
			if r := recover(); r != nil {
Axel Prel's avatar
Axel Prel committed
				log := zerolog.Ctx(ctx)

				exc := zerolog.Dict().
					Str("type", fmt.Sprintf("%T", r))
				if err, ok := r.(error); ok {
					exc = exc.AnErr("value", err)
				} else {
					exc = exc.Str("value", fmt.Sprintf("%#v", r))
				}
				stack := sentry.NewStacktrace()
				stack.Frames = stack.Frames[:len(stack.Frames)-1]
				b, err := jsoniter.Marshal(stack)
				if err != nil {
					log.Err(err).Msg("Could not marshal stacktrace, fallback to plain debug.Stack()")
					b = debug.Stack()
					exc = exc.Str("plainstack", string(b))
				} else {
					exc = exc.RawJSON("stacktrace", b)
				}

				log.Error().Dict("exception", exc).Msg("Caught a panic")

				rw.WriteHeader(http.StatusInternalServerError)
			}
Axel Prel's avatar
Axel Prel committed
		}(req.Context())
		next.ServeHTTP(rw, req)
	})
}