feat(backend): also add sentry tracing

This commit is contained in:
sam 2023-09-20 03:40:07 +02:00
parent b04ed68832
commit 9f266ee1a8
No known key found for this signature in database
GPG Key ID: B4EF20DDE721CAA1
4 changed files with 102 additions and 13 deletions

View File

@ -28,7 +28,11 @@ func run(c *cli.Context) error {
if dsn := os.Getenv("SENTRY_DSN"); dsn != "" { if dsn := os.Getenv("SENTRY_DSN"); dsn != "" {
sentry.Init(sentry.ClientOptions{ sentry.Init(sentry.ClientOptions{
Dsn: dsn, Dsn: dsn,
Debug: true,
Release: server.Tag, Release: server.Tag,
EnableTracing: true,
TracesSampleRate: 1.0,
ProfilesSampleRate: 1.0,
}) })
} }

View File

@ -1,7 +1,6 @@
package server package server
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
@ -15,16 +14,10 @@ import (
// The inner HandlerFunc additionally returns an error. // The inner HandlerFunc additionally returns an error.
func WrapHandler(hn func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc { func WrapHandler(hn func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
hub := sentry.CurrentHub().Clone() hub := sentry.GetHubFromContext(r.Context())
if hub == nil {
defer func(hub *sentry.Hub, r *http.Request) { hub = sentry.CurrentHub().Clone()
if err := recover(); err != nil {
hub.RecoverWithContext(
context.WithValue(r.Context(), sentry.RequestContextKey, r),
err,
)
} }
}(hub, r)
err := hn(w, r) err := hn(w, r)
if err != nil { if err != nil {

89
backend/server/sentry.go Normal file
View File

@ -0,0 +1,89 @@
package server
import (
"context"
"fmt"
"net/http"
"github.com/getsentry/sentry-go"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func (s *Server) sentry(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
ctx := r.Context()
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
options := []sentry.SpanOption{
sentry.WithOpName("http.server"),
sentry.ContinueFromRequest(r),
sentry.WithTransactionSource(sentry.SourceURL),
}
// We don't mind getting an existing transaction back so we don't need to
// check if it is.
transaction := sentry.StartTransaction(ctx,
fmt.Sprintf("%s %s", r.Method, r.URL.Path),
options...,
)
defer transaction.Finish()
r = r.WithContext(transaction.Context())
hub.Scope().SetRequest(r)
defer recoverWithSentry(hub, r)
handler.ServeHTTP(ww, r)
transaction.Status = httpStatusToSentryStatus(ww.Status())
rctx := chi.RouteContext(r.Context())
transaction.Name = rctx.RouteMethod + " " + rctx.RoutePattern()
})
}
func recoverWithSentry(hub *sentry.Hub, r *http.Request) {
if err := recover(); err != nil {
hub.RecoverWithContext(
context.WithValue(r.Context(), sentry.RequestContextKey, r),
err,
)
}
}
func httpStatusToSentryStatus(status int) sentry.SpanStatus {
// c.f. https://develop.sentry.dev/sdk/event-payloads/span/
if status >= 200 && status < 400 {
return sentry.SpanStatusOK
}
switch status {
case 499:
return sentry.SpanStatusCanceled
case 500:
return sentry.SpanStatusInternalError
case 400:
return sentry.SpanStatusInvalidArgument
case 504:
return sentry.SpanStatusDeadlineExceeded
case 404:
return sentry.SpanStatusNotFound
case 409:
return sentry.SpanStatusAlreadyExists
case 403:
return sentry.SpanStatusPermissionDenied
case 429:
return sentry.SpanStatusResourceExhausted
case 501:
return sentry.SpanStatusUnimplemented
case 503:
return sentry.SpanStatusUnavailable
case 401:
return sentry.SpanStatusUnauthenticated
default:
return sentry.SpanStatusUnknown
}
}

View File

@ -50,6 +50,9 @@ func New() (*Server, error) {
s.Router.Use(middleware.Logger) s.Router.Use(middleware.Logger)
} }
s.Router.Use(middleware.Recoverer) s.Router.Use(middleware.Recoverer)
// add Sentry tracing handler
s.Router.Use(s.sentry)
// add CORS // add CORS
s.Router.Use(cors.Handler(cors.Options{ s.Router.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"}, AllowedOrigins: []string{"https://*", "http://*"},