From 6a00322d56765b4540368636cdda012c5f32d510 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Mon, 24 Aug 2026 16:06:08 -0400 Subject: [PATCH] feat(sveltekit): Register a route provider backed by reported route ids SvelteKit has no public route matcher and `page.route.id` is not available synchronously, so the provider answers from route ids the instrumentation has already seen rather than by matching. Uses core's cached provider, so the statefulness stays private and the API stays URL in, string out. --- .../src/client/browserTracingIntegration.ts | 6 ++++++ packages/sveltekit/src/client/routeCache.ts | 15 +++++++++++++++ .../sveltekit/src/client/svelte4BrowserTracing.ts | 5 +++++ .../sveltekit/src/client/svelte5BrowserTracing.ts | 5 +++++ 4 files changed, 31 insertions(+) create mode 100644 packages/sveltekit/src/client/routeCache.ts diff --git a/packages/sveltekit/src/client/browserTracingIntegration.ts b/packages/sveltekit/src/client/browserTracingIntegration.ts index 3fb9b65bd1a7..fa1299f7ca77 100644 --- a/packages/sveltekit/src/client/browserTracingIntegration.ts +++ b/packages/sveltekit/src/client/browserTracingIntegration.ts @@ -1,5 +1,7 @@ import type { Integration } from '@sentry/core'; +import { setRouteProvider } from '@sentry/core'; import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/svelte'; +import { routeProvider } from './routeCache'; // The `sentrySvelteKit()` Vite plugin redirects this to the Svelte 4 or Svelte 5 variant per Kit // version; without the plugin it resolves via `exports` to the Svelte 4 variant, so builds don't break. import { instrumentSvelteKitTracing } from '@sentry/sveltekit/browser-tracing-variant'; @@ -20,6 +22,10 @@ export function browserTracingIntegration( return { ...integration, + setup: client => { + setRouteProvider(routeProvider, client); + integration.setup?.(client); + }, afterAllSetup: client => { integration.afterAllSetup(client); instrumentSvelteKitTracing(client, options); diff --git a/packages/sveltekit/src/client/routeCache.ts b/packages/sveltekit/src/client/routeCache.ts new file mode 100644 index 000000000000..ec61ee2741a2 --- /dev/null +++ b/packages/sveltekit/src/client/routeCache.ts @@ -0,0 +1,15 @@ +import { createCachedRouteProvider } from '@sentry/core'; + +// SvelteKit has no public route matcher, and `page.route.id` is not available synchronously, so the +// provider answers from route ids the instrumentation has already seen rather than by matching. +export const routeProvider = createCachedRouteProvider(); + +/** + * Records the parameterized route id SvelteKit reported for a path. + * + * Called from both the Kit 2 and Kit 3 instrumentation, since `page.route.id` is the only place the + * route id is available. + */ +export function recordRouteId(pathname: string | undefined, routeId: string | null | undefined): void { + routeProvider.record(pathname, routeId); +} diff --git a/packages/sveltekit/src/client/svelte4BrowserTracing.ts b/packages/sveltekit/src/client/svelte4BrowserTracing.ts index 818a8ffe2f61..5dbfd4de479e 100644 --- a/packages/sveltekit/src/client/svelte4BrowserTracing.ts +++ b/packages/sveltekit/src/client/svelte4BrowserTracing.ts @@ -17,6 +17,7 @@ import type { Navigation, Page } from '@sveltejs/kit'; // eslint-disable-next-line typescript/no-deprecated import { navigating, page } from '$app/stores'; import type { Readable } from 'svelte/store'; +import { recordRouteId } from './routeCache'; /** * SvelteKit 2 / Svelte 4 browser tracing (`$app/stores`). Selected at build time, so it's only @@ -62,6 +63,8 @@ function _instrumentPageload(client: Client, pageStore: Readable): void { const routeId = pageState.route?.id; + recordRouteId(pageState.url?.pathname, routeId); + if (routeId) { pageloadSpan.updateName(routeId); pageloadSpan.setAttributes({ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', [URL_TEMPLATE]: routeId }); @@ -105,6 +108,8 @@ function _instrumentNavigations(client: Client, navigatingStore: Readable { + recordRouteId(WINDOW.location?.pathname, routeId); + if (routeId) { pageLoadSpan.updateName(routeId); pageLoadSpan.setAttributes({ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', [URL_TEMPLATE]: routeId }); @@ -88,6 +91,8 @@ function _instrumentNavigations(client: Client): void { const parameterizedRouteOrigin = from?.route.id; const parameterizedRouteDestination = to?.route.id; + recordRouteId(to?.url.pathname, parameterizedRouteDestination); + routingSpan?.end(); const navigationInfo = {