From d83bd21d39675d6c74b891b135bedb73583f39ed Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 20 Jul 2026 13:53:19 +0100 Subject: [PATCH] fix(webapp): report full route path on Remix request spans The @sentry/remix OpenTelemetry instrumentation sets each request span's name and http.route from only the matched leaf route's own path segment. For nested routes that made distinct pages collapse to one ambiguous segment (e.g. ":runParam"), so traces could not tell which route served a request. Reconstruct the full matched path by joining every segment in the match chain, falling back to the leaf path. Flat routes such as "api/v1/tasks/:taskId/trigger" are unchanged; nested routes now carry their full URL pattern. Applied to both the CJS and ESM builds via the existing pnpm patch. --- patches/@sentry__remix@9.46.0.patch | 42 ++++++++++++++++++++++++++--- pnpm-lock.yaml | 6 ++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/patches/@sentry__remix@9.46.0.patch b/patches/@sentry__remix@9.46.0.patch index e30781631b4..1a0625924ad 100644 --- a/patches/@sentry__remix@9.46.0.patch +++ b/patches/@sentry__remix@9.46.0.patch @@ -1,8 +1,24 @@ diff --git a/build/cjs/vendor/instrumentation.js b/build/cjs/vendor/instrumentation.js -index 84e18d1051f57d5807e65c8b8ce858ceee7d4557..640a5253d565650338fe33e0ea52c8dffc63e4e7 100644 +index 84e18d1051f57d5807e65c8b8ce858ceee7d4557..098a18992044ff4e52de986782bfb52761e50ab7 100644 --- a/build/cjs/vendor/instrumentation.js +++ b/build/cjs/vendor/instrumentation.js -@@ -238,7 +238,7 @@ class RemixInstrumentation extends instrumentation.InstrumentationBase { +@@ -140,7 +140,14 @@ class RemixInstrumentation extends instrumentation.InstrumentationBase { + + const route = (result || []).slice(-1)[0]?.route; + +- const routePath = route?.path; ++ // Join every matched route segment so nested routes report their ++ // full URL pattern on http.route (and the span name), not just the ++ // leaf route's own path segment. ++ const routePath = ++ (result || []) ++ .map((match) => match?.route?.path) ++ .filter((segment) => typeof segment === 'string' && segment.length > 0) ++ .join('/') || route?.path; + if (span && routePath) { + span.setAttribute(semanticConventions.SemanticAttributes.HTTP_ROUTE, routePath); + span.updateName(`remix.request ${routePath}`); +@@ -238,7 +245,7 @@ class RemixInstrumentation extends instrumentation.InstrumentationBase { return function callRouteAction(original) { return async function patchCallRouteAction( ...args) { const [params] = args; @@ -11,7 +27,7 @@ index 84e18d1051f57d5807e65c8b8ce858ceee7d4557..640a5253d565650338fe33e0ea52c8df const span = plugin.tracer.startSpan( `ACTION ${params.routeId}`, { attributes: { [semanticConventions.SemanticAttributes.CODE_FUNCTION]: 'action' } }, -@@ -257,25 +257,6 @@ class RemixInstrumentation extends instrumentation.InstrumentationBase { +@@ -257,25 +264,6 @@ class RemixInstrumentation extends instrumentation.InstrumentationBase { .then(async response => { addResponseAttributesToSpan(span, response); @@ -37,3 +53,23 @@ index 84e18d1051f57d5807e65c8b8ce858ceee7d4557..640a5253d565650338fe33e0ea52c8df return response; }) .catch(async error => { +diff --git a/build/esm/vendor/instrumentation.js b/build/esm/vendor/instrumentation.js +index e33b84eecdffe3b10a5e50f13bafaf90775747c5..652a89c9194ea1d0bb0d748fb8f7f060ca2cf5ba 100644 +--- a/build/esm/vendor/instrumentation.js ++++ b/build/esm/vendor/instrumentation.js +@@ -138,7 +138,14 @@ class RemixInstrumentation extends InstrumentationBase { + + const route = (result || []).slice(-1)[0]?.route; + +- const routePath = route?.path; ++ // Join every matched route segment so nested routes report their ++ // full URL pattern on http.route (and the span name), not just the ++ // leaf route's own path segment. ++ const routePath = ++ (result || []) ++ .map((match) => match?.route?.path) ++ .filter((segment) => typeof segment === 'string' && segment.length > 0) ++ .join('/') || route?.path; + if (span && routePath) { + span.setAttribute(SemanticAttributes.HTTP_ROUTE, routePath); + span.updateName(`remix.request ${routePath}`); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8dc0f272d0..3e49b25e50f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,7 +63,7 @@ patchedDependencies: hash: 33e2966e9ef36aa09955cec9922c5063227b83429157a71ba226369a08627ac9 path: patches/@remix-run__router@1.23.3.patch '@sentry/remix@9.46.0': - hash: 146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b + hash: 81622d55455fe21cad379bf42bce1536202b8858028aa312c228c7a59ffe41cf path: patches/@sentry__remix@9.46.0.patch '@upstash/ratelimit@1.1.3': hash: e5922e50fbefb7b2b24950c4b1c5c9ddc4cd25464439c9548d2298c432debe74 @@ -437,7 +437,7 @@ importers: version: 0.22.10(supports-color@10.0.0) '@sentry/remix': specifier: 9.46.0 - version: 9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.17.5(typescript@5.5.4))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@remix-run/server-runtime@2.17.5(typescript@5.5.4))(encoding@0.1.13)(react@18.3.1) + version: 9.46.0(patch_hash=81622d55455fe21cad379bf42bce1536202b8858028aa312c228c7a59ffe41cf)(@remix-run/node@2.17.5(typescript@5.5.4))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@remix-run/server-runtime@2.17.5(typescript@5.5.4))(encoding@0.1.13)(react@18.3.1) '@slack/web-api': specifier: 7.16.0 version: 7.16.0 @@ -21282,7 +21282,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/remix@9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.17.5(typescript@5.5.4))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@remix-run/server-runtime@2.17.5(typescript@5.5.4))(encoding@0.1.13)(react@18.3.1)': + '@sentry/remix@9.46.0(patch_hash=81622d55455fe21cad379bf42bce1536202b8858028aa312c228c7a59ffe41cf)(@remix-run/node@2.17.5(typescript@5.5.4))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4))(@remix-run/server-runtime@2.17.5(typescript@5.5.4))(encoding@0.1.13)(react@18.3.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.1)