Name Sentry transactions after the route they matched (closes #185)
check / check (push) Failing after 4m15s
check / check (push) Failing after 4m15s
sentryhttp names each transaction after the concrete path, which the BeforeSendTransaction hook has to withhold, so with tracing on every transaction would land in one Sentry group, POST /(redacted). A middleware installed beside every sentryhttp handler now renames the transaction to the method and chi route pattern once the request is routed, and sets its source to route. The hook keeps a route-sourced name and withholds every other name as before, so a request that matched no route still reads POST /(redacted). Tracing stays off. Model: opus-5-5
This commit is contained in:
@@ -71,10 +71,15 @@ func scrubSentryRequest(
|
||||
|
||||
pattern := sentryRoutePattern(hint)
|
||||
|
||||
// Only transaction events carry a Transaction name, and the SDK
|
||||
// Only transaction events carry a Transaction name. sentryhttp
|
||||
// builds it from the concrete path too (sentryhttp.go:105 via
|
||||
// tracing.go:553). Rewritten on the same terms.
|
||||
if event.Transaction != "" {
|
||||
// tracing.go:553) and marks it SourceURL, so it is rewritten on
|
||||
// the same terms. Only a name nameSentryTransaction set from the
|
||||
// route pattern, which it marks SourceRoute, is kept.
|
||||
namedByRoute := event.TransactionInfo != nil &&
|
||||
event.TransactionInfo.Source == sentry.SourceRoute
|
||||
|
||||
if event.Transaction != "" && !namedByRoute {
|
||||
event.Transaction = sentryTransactionName(
|
||||
event.Transaction, pattern,
|
||||
)
|
||||
@@ -120,7 +125,8 @@ func scrubSentryRequest(
|
||||
// The transaction dispatch has no such request: Span.doFinish calls
|
||||
// hub.CaptureEvent (tracing.go:356), which passes a nil hint that the
|
||||
// client replaces with an empty one (client.go:620-622). The pattern
|
||||
// is therefore always "" there, and the callers fall back.
|
||||
// is therefore always "" there, and the callers fall back. A routed
|
||||
// transaction is named by nameSentryTransaction instead.
|
||||
func sentryRoutePattern(hint *sentry.EventHint) string {
|
||||
if hint == nil || hint.Context == nil {
|
||||
return ""
|
||||
@@ -142,6 +148,45 @@ func sentryRoutePattern(hint *sentry.EventHint) string {
|
||||
return rctx.RoutePattern()
|
||||
}
|
||||
|
||||
// nameSentryTransaction renames the request's Sentry transaction to
|
||||
// "METHOD <route pattern>", such as "POST /h/{uuid}". sentryhttp names
|
||||
// it after the concrete path, which scrubSentryRequest withholds;
|
||||
// since Sentry groups transactions by name, every transaction in the
|
||||
// service would otherwise land in one group, "POST /(redacted)".
|
||||
//
|
||||
// It has to run inside the sentryhttp handler, where the transaction
|
||||
// is on the request context. It renames on the way out, once chi has
|
||||
// routed the request, and does so in a defer so that a request whose
|
||||
// handler panics is named too. A request that matched no route has no
|
||||
// pattern; its name is left as the SDK set it, for
|
||||
// scrubSentryRequest to withhold.
|
||||
func nameSentryTransaction(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
transaction := sentry.TransactionFromContext(
|
||||
r.Context(),
|
||||
)
|
||||
rctx := chi.RouteContext(r.Context())
|
||||
|
||||
if transaction == nil || rctx == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pattern := rctx.RoutePattern()
|
||||
if pattern == "" {
|
||||
return
|
||||
}
|
||||
|
||||
transaction.Name = r.Method + " " + pattern
|
||||
transaction.Source = sentry.SourceRoute
|
||||
}()
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// sentryRouteURL rebuilds an event's request URL with the route
|
||||
// pattern in place of the concrete path.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user