From c098258e314a4d6e92570fdbfc46018ef283bf85 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 17 Jul 2026 17:03:56 +0200 Subject: [PATCH] feat(vue): Set navigation.route.id from the Vue Router route name Sets the navigation.route.id attribute (from @sentry/conventions) on pageload and navigation spans, sourced from the Vue Router route name (to.name). This is the framework-assigned route identifier, which Relay prefers over url.template for span description inference. The attribute is set independently of the routeLabel option: routeLabel only controls the span name/source, whereas the route id identifies the route regardless. This is most valuable when to.name drives the span (source 'custom'), where url.template is not set today, so navigation.route.id becomes the only structured route identifier. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test-applications/vue-3/tests/performance.test.ts | 1 + packages/vue/src/router.ts | 11 ++++++++++- packages/vue/test/router.test.ts | 6 +++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts index 291c611d4ca4..a1a9626d01cf 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts @@ -120,6 +120,7 @@ test('sends a pageload transaction with a route name as transaction name if avai 'sentry.source': 'custom', 'sentry.origin': 'auto.pageload.vue', 'sentry.op': 'pageload', + 'navigation.route.id': 'AboutView', 'url.path': '/about', 'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/about$/), }, diff --git a/packages/vue/src/router.ts b/packages/vue/src/router.ts index fefd1274ca0d..9a1c5335d81e 100644 --- a/packages/vue/src/router.ts +++ b/packages/vue/src/router.ts @@ -1,5 +1,10 @@ import { captureException, getAbsoluteUrl } from '@sentry/browser'; -import { PARAMS_KEY_BASE, URL_PATH_PARAMETER_KEY_BASE, URL_TEMPLATE } from '@sentry/conventions/attributes'; +import { + NAVIGATION_ROUTE_ID, + PARAMS_KEY_BASE, + URL_PATH_PARAMETER_KEY_BASE, + URL_TEMPLATE, +} from '@sentry/conventions/attributes'; import type { Span, SpanAttributes, StartSpanOptions, TransactionSource } from '@sentry/core'; import { getActiveSpan, @@ -101,6 +106,10 @@ export function instrumentVueRouter( attributes[URL_TEMPLATE] = spanName; } + if (to.name) { + attributes[NAVIGATION_ROUTE_ID] = to.name.toString(); + } + getCurrentScope().setTransactionName(spanName); // Update the existing page load span with parametrized route information diff --git a/packages/vue/test/router.test.ts b/packages/vue/test/router.test.ts index 498c41f31ed3..2bd2fe2b5ace 100644 --- a/packages/vue/test/router.test.ts +++ b/packages/vue/test/router.test.ts @@ -2,7 +2,7 @@ import * as SentryBrowser from '@sentry/browser'; import type { Span, SpanAttributes } from '@sentry/core'; import * as SentryCore from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; -import { URL_TEMPLATE } from '@sentry/conventions/attributes'; +import { NAVIGATION_ROUTE_ID, URL_TEMPLATE } from '@sentry/conventions/attributes'; import { afterEach, describe, expect, it, vi } from 'vitest'; import type { Route } from '../src/router'; import { instrumentVueRouter } from '../src/router'; @@ -465,5 +465,9 @@ function getAttributesForRoute(route: Route, urlTemplate?: string): SpanAttribut attributes[URL_TEMPLATE] = urlTemplate; } + if (route.name) { + attributes[NAVIGATION_ROUTE_ID] = route.name.toString(); + } + return attributes; }