Skip to content

Commit 24ef5c3

Browse files
Lms24cursoragent
andauthored
feat(solid): Set url attributes on solid router and tanstack router spans (#22090)
Adds `url.template`, `url.path`, and `url.full` to pageload and navigation spans for Solid Router and TanStack Solid Router integrations, including redirect and back-button navigation cases. Ports `locationToSpanUrlAttributes` from the React TanStack Router integration and updates Solid Router to pass the destination URL when starting navigation spans. Includes e2e test coverage for solidstart and solid-tanstack-router. part of #21921 Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 893a48c commit 24ef5c3

9 files changed

Lines changed: 418 additions & 49 deletions

File tree

dev-packages/e2e-tests/test-applications/solid-tanstack-router/tests/routing-instrumentation.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ test('sends a pageload transaction with a parameterized URL', async ({ page }) =
1818
'sentry.origin': 'auto.pageload.solid.tanstack_router',
1919
'sentry.op': 'pageload',
2020
'url.path.parameter.postId': '456',
21+
'url.template': '/posts/$postId',
22+
'url.path': '/posts/456',
23+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/posts\/456$/),
2124
},
2225
op: 'pageload',
2326
origin: 'auto.pageload.solid.tanstack_router',
@@ -59,6 +62,9 @@ test('sends a navigation transaction with a parameterized URL', async ({ page })
5962
'sentry.origin': 'auto.navigation.solid.tanstack_router',
6063
'sentry.op': 'navigation',
6164
'url.path.parameter.postId': '2',
65+
'url.template': '/posts/$postId',
66+
'url.path': '/posts/2',
67+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/posts\/2$/),
6268
},
6369
op: 'navigation',
6470
origin: 'auto.navigation.solid.tanstack_router',
@@ -90,6 +96,9 @@ test('sends a pageload transaction named after the resolved route when a redirec
9096
'sentry.origin': 'auto.pageload.solid.tanstack_router',
9197
'sentry.op': 'pageload',
9298
'url.path.parameter.postId': '1',
99+
'url.template': '/posts/$postId',
100+
'url.path': '/posts/1',
101+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/posts\/1$/),
93102
},
94103
op: 'pageload',
95104
origin: 'auto.pageload.solid.tanstack_router',
@@ -130,6 +139,9 @@ test('sends a navigation transaction when a redirect is thrown in beforeLoad', a
130139
'sentry.origin': 'auto.navigation.solid.tanstack_router',
131140
'sentry.op': 'navigation',
132141
'url.path.parameter.postId': '1',
142+
'url.template': '/posts/$postId',
143+
'url.path': '/posts/1',
144+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/posts\/1$/),
133145
},
134146
op: 'navigation',
135147
origin: 'auto.navigation.solid.tanstack_router',
@@ -175,6 +187,9 @@ test('sends a navigation transaction for a normal navigation that happens after
175187
'sentry.origin': 'auto.navigation.solid.tanstack_router',
176188
'sentry.op': 'navigation',
177189
'url.path.parameter.postId': '2',
190+
'url.template': '/posts/$postId',
191+
'url.path': '/posts/2',
192+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/posts\/2$/),
178193
},
179194
op: 'navigation',
180195
origin: 'auto.navigation.solid.tanstack_router',
@@ -201,6 +216,12 @@ test('sends pageload transaction with web vitals measurements', async ({ page })
201216
trace: {
202217
op: 'pageload',
203218
origin: 'auto.pageload.solid.tanstack_router',
219+
data: {
220+
'sentry.source': 'route',
221+
'url.template': '/',
222+
'url.path': '/',
223+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
224+
},
204225
},
205226
},
206227
transaction: '/',

dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/performance.client.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ test('sends a pageload transaction', async ({ page }) => {
1414
trace: {
1515
op: 'pageload',
1616
origin: 'auto.pageload.browser',
17+
data: {
18+
'sentry.source': 'route',
19+
'url.template': '/',
20+
'url.path': '/',
21+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
22+
},
1723
},
1824
},
1925
transaction: '/',
@@ -37,6 +43,12 @@ test('sends a navigation transaction with parametrized route', async ({ page })
3743
trace: {
3844
op: 'navigation',
3945
origin: 'auto.navigation.solidstart.solidrouter',
46+
data: {
47+
'sentry.source': 'route',
48+
'url.template': '/users/:id',
49+
'url.path': '/users/5',
50+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/),
51+
},
4052
},
4153
},
4254
transaction: '/users/:id',
@@ -63,6 +75,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
6375
trace: {
6476
op: 'navigation',
6577
origin: 'auto.navigation.solidstart.solidrouter',
78+
data: {
79+
'sentry.source': 'route',
80+
'url.template': '/users/:id',
81+
'url.path': '/users/6',
82+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/),
83+
},
6684
},
6785
},
6886
transaction: '/users/:id',
@@ -85,6 +103,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
85103
trace: {
86104
op: 'navigation',
87105
origin: 'auto.navigation.solidstart.solidrouter',
106+
data: {
107+
'sentry.source': 'route',
108+
'url.template': '/back-navigation',
109+
'url.path': '/back-navigation',
110+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/),
111+
},
88112
},
89113
},
90114
transaction: '/back-navigation',

dev-packages/e2e-tests/test-applications/solidstart-spa/tests/performance.client.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ test('sends a pageload transaction', async ({ page }) => {
1414
trace: {
1515
op: 'pageload',
1616
origin: 'auto.pageload.browser',
17+
data: {
18+
'sentry.source': 'route',
19+
'url.template': '/',
20+
'url.path': '/',
21+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
22+
},
1723
},
1824
},
1925
transaction: '/',
@@ -37,6 +43,12 @@ test('sends a navigation transaction with parametrized route', async ({ page })
3743
trace: {
3844
op: 'navigation',
3945
origin: 'auto.navigation.solidstart.solidrouter',
46+
data: {
47+
'sentry.source': 'route',
48+
'url.template': '/users/:id',
49+
'url.path': '/users/5',
50+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/),
51+
},
4052
},
4153
},
4254
transaction: '/users/:id',
@@ -63,6 +75,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
6375
trace: {
6476
op: 'navigation',
6577
origin: 'auto.navigation.solidstart.solidrouter',
78+
data: {
79+
'sentry.source': 'route',
80+
'url.template': '/users/:id',
81+
'url.path': '/users/6',
82+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/),
83+
},
6684
},
6785
},
6886
transaction: '/users/:id',
@@ -85,6 +103,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
85103
trace: {
86104
op: 'navigation',
87105
origin: 'auto.navigation.solidstart.solidrouter',
106+
data: {
107+
'sentry.source': 'route',
108+
'url.template': '/back-navigation',
109+
'url.path': '/back-navigation',
110+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/),
111+
},
88112
},
89113
},
90114
transaction: '/back-navigation',

dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/performance.client.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ test('sends a pageload transaction', async ({ page }) => {
1414
trace: {
1515
op: 'pageload',
1616
origin: 'auto.pageload.browser',
17+
data: {
18+
'sentry.source': 'route',
19+
'url.template': '/',
20+
'url.path': '/',
21+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
22+
},
1723
},
1824
},
1925
transaction: '/',
@@ -37,6 +43,12 @@ test('sends a navigation transaction with parametrized route', async ({ page })
3743
trace: {
3844
op: 'navigation',
3945
origin: 'auto.navigation.solidstart.solidrouter',
46+
data: {
47+
'sentry.source': 'route',
48+
'url.template': '/users/:id',
49+
'url.path': '/users/5',
50+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/),
51+
},
4052
},
4153
},
4254
transaction: '/users/:id',
@@ -63,6 +75,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
6375
trace: {
6476
op: 'navigation',
6577
origin: 'auto.navigation.solidstart.solidrouter',
78+
data: {
79+
'sentry.source': 'route',
80+
'url.template': '/users/:id',
81+
'url.path': '/users/6',
82+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/),
83+
},
6684
},
6785
},
6886
transaction: '/users/:id',
@@ -85,6 +103,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
85103
trace: {
86104
op: 'navigation',
87105
origin: 'auto.navigation.solidstart.solidrouter',
106+
data: {
107+
'sentry.source': 'route',
108+
'url.template': '/back-navigation',
109+
'url.path': '/back-navigation',
110+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/),
111+
},
88112
},
89113
},
90114
transaction: '/back-navigation',

dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ test('sends a pageload transaction', async ({ page }) => {
1414
trace: {
1515
op: 'pageload',
1616
origin: 'auto.pageload.browser',
17+
data: {
18+
'sentry.source': 'route',
19+
'url.template': '/',
20+
'url.path': '/',
21+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
22+
},
1723
},
1824
},
1925
transaction: '/',
@@ -37,6 +43,12 @@ test('sends a navigation transaction with parametrized route', async ({ page })
3743
trace: {
3844
op: 'navigation',
3945
origin: 'auto.navigation.solidstart.solidrouter',
46+
data: {
47+
'sentry.source': 'route',
48+
'url.template': '/users/:id',
49+
'url.path': '/users/5',
50+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/),
51+
},
4052
},
4153
},
4254
transaction: '/users/:id',
@@ -63,6 +75,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
6375
trace: {
6476
op: 'navigation',
6577
origin: 'auto.navigation.solidstart.solidrouter',
78+
data: {
79+
'sentry.source': 'route',
80+
'url.template': '/users/:id',
81+
'url.path': '/users/6',
82+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/),
83+
},
6684
},
6785
},
6886
transaction: '/users/:id',
@@ -85,6 +103,12 @@ test('updates the transaction when using the back button', async ({ page }) => {
85103
trace: {
86104
op: 'navigation',
87105
origin: 'auto.navigation.solidstart.solidrouter',
106+
data: {
107+
'sentry.source': 'route',
108+
'url.template': '/back-navigation',
109+
'url.path': '/back-navigation',
110+
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/),
111+
},
88112
},
89113
},
90114
transaction: '/back-navigation',

packages/solid/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
},
5656
"dependencies": {
5757
"@sentry/browser": "10.64.0",
58-
"@sentry/core": "10.64.0"
58+
"@sentry/core": "10.64.0",
59+
"@sentry/conventions": "^0.15.1"
5960
},
6061
"peerDependencies": {
6162
"@solidjs/router": "^0.13.4 || ^0.14.0 || ^0.15.0",

packages/solid/src/solidrouter.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
22
browserTracingIntegration,
3+
getAbsoluteUrl,
34
getActiveSpan,
45
getRootSpan,
56
spanToJSON,
67
startBrowserTracingNavigationSpan,
78
} from '@sentry/browser';
9+
import { PARAMS_KEY, URL_FULL, URL_PATH, URL_PATH_PARAMETER_KEY, URL_TEMPLATE } from '@sentry/conventions/attributes';
810
import type { Client, Integration, Span } from '@sentry/core';
911
import {
1012
getClient,
@@ -27,6 +29,15 @@ import { createComponent } from 'solid-js/web';
2729

2830
const CLIENTS_WITH_INSTRUMENT_NAVIGATION = new WeakSet<Client>();
2931

32+
function locationToSpanUrlAttributes(pathname: string, search: string = '', hash: string = ''): Record<string, string> {
33+
const pathWithSearch = `${pathname}${search}${hash}`;
34+
35+
return {
36+
[URL_PATH]: pathname,
37+
[URL_FULL]: getAbsoluteUrl(pathWithSearch),
38+
};
39+
}
40+
3041
function handleNavigation(location: string): void {
3142
const client = getClient();
3243
if (!client || !CLIENTS_WITH_INSTRUMENT_NAVIGATION.has(client)) {
@@ -40,14 +51,20 @@ function handleNavigation(location: string): void {
4051
const { name } = metaData?.sdk || {};
4152
const framework = name?.includes('solidstart') ? 'solidstart' : 'solid';
4253

43-
startBrowserTracingNavigationSpan(client, {
44-
name: location,
45-
attributes: {
46-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
47-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.${framework}.solidrouter`,
48-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
54+
const isBackNavigation = location === '-1';
55+
56+
startBrowserTracingNavigationSpan(
57+
client,
58+
{
59+
name: location,
60+
attributes: {
61+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
62+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.${framework}.solidrouter`,
63+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
64+
},
4965
},
50-
});
66+
isBackNavigation ? undefined : { url: getAbsoluteUrl(location) },
67+
);
5168
}
5269

5370
function getActiveRootSpan(): Span | undefined {
@@ -101,16 +118,22 @@ function withSentryRouterRoot(Root: Component<RouteSectionProps>): Component<Rou
101118
const currentMatches = matches();
102119
const lastMatch = currentMatches[currentMatches.length - 1];
103120

121+
const urlAttributes = locationToSpanUrlAttributes(name, location.search, location.hash);
122+
104123
if (lastMatch) {
105124
const parametrizedRoute = lastMatch.route.pattern || name;
106125
rootSpan.updateName(parametrizedRoute);
107-
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
126+
rootSpan.setAttributes({
127+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
128+
[URL_TEMPLATE]: parametrizedRoute,
129+
...urlAttributes,
130+
});
108131

109132
const params = lastMatch.params;
110133
for (const [key, value] of Object.entries(params)) {
111134
if (value !== undefined) {
112-
rootSpan.setAttribute(`url.path.parameter.${key}`, value);
113-
rootSpan.setAttribute(`params.${key}`, value);
135+
rootSpan.setAttribute(URL_PATH_PARAMETER_KEY.replace('<key>', key), value);
136+
rootSpan.setAttribute(PARAMS_KEY.replace('<key>', key), value);
114137
}
115138
}
116139
} else {
@@ -119,7 +142,7 @@ function withSentryRouterRoot(Root: Component<RouteSectionProps>): Component<Rou
119142
if (op === 'navigation' && description === '-1') {
120143
rootSpan.updateName(name);
121144
}
122-
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'url');
145+
rootSpan.setAttributes({ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', ...urlAttributes });
123146
}
124147
});
125148

0 commit comments

Comments
 (0)