Skip to content

Commit 6435448

Browse files
committed
Merge branch 'develop' into sig/param-routes-nuxt-ssr
# Conflicts: # dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/tracing.test.ts # dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/tracing.test.ts # dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/tracing.test.ts # dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.test.ts # dev-packages/e2e-tests/test-applications/nuxt-4/tests/tracing.test.ts
2 parents 6999a67 + a215bf9 commit 6435448

File tree

9 files changed

+75
-12
lines changed

9 files changed

+75
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7-
Work in this release was contributed by @Spice-King. Thank you for your contribution!
7+
Work in this release was contributed by @Spice-King and @stayallive. Thank you for your contributions!
88

99
## 9.35.0
1010

dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/tracing.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ test.describe('distributed tracing', () => {
1010
});
1111

1212
const serverTxnEventPromise = waitForTransaction('nuxt-3-dynamic-import', txnEvent => {
13-
return txnEvent.transaction.includes('GET /test-param/');
13+
return txnEvent.transaction?.includes('GET /test-param/') || false;
1414
});
1515

1616
const [_, clientTxnEvent, serverTxnEvent] = await Promise.all([
@@ -136,8 +136,8 @@ test.describe('distributed tracing', () => {
136136
expect(serverReqTxnEvent).toEqual(
137137
expect.objectContaining({
138138
type: 'transaction',
139-
transaction: `GET /api/user/${PARAM}`,
140-
transaction_info: { source: 'url' },
139+
transaction: `GET /api/user/:userId`, // parametrized route
140+
transaction_info: { source: 'route' },
141141
contexts: expect.objectContaining({
142142
trace: expect.objectContaining({
143143
op: 'http.server',

dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/tracing.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ test.describe('distributed tracing', () => {
136136
expect(serverReqTxnEvent).toEqual(
137137
expect.objectContaining({
138138
type: 'transaction',
139-
transaction: `GET /api/user/${PARAM}`,
140-
transaction_info: { source: 'url' },
139+
transaction: `GET /api/user/:userId`, // parametrized route
140+
transaction_info: { source: 'route' },
141141
contexts: expect.objectContaining({
142142
trace: expect.objectContaining({
143143
op: 'http.server',

dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/tracing.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ test.describe('distributed tracing', () => {
136136
expect(serverReqTxnEvent).toEqual(
137137
expect.objectContaining({
138138
type: 'transaction',
139-
transaction: `GET /api/user/${PARAM}`,
140-
transaction_info: { source: 'url' },
139+
transaction: `GET /api/user/:userId`, // parametrized route
140+
transaction_info: { source: 'route' },
141141
contexts: expect.objectContaining({
142142
trace: expect.objectContaining({
143143
op: 'http.server',

dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ test.describe('distributed tracing', () => {
136136
expect(serverReqTxnEvent).toEqual(
137137
expect.objectContaining({
138138
type: 'transaction',
139-
transaction: `GET /api/user/${PARAM}`,
140-
transaction_info: { source: 'url' },
139+
transaction: `GET /api/user/:userId`, // parametrized route
140+
transaction_info: { source: 'route' },
141141
contexts: expect.objectContaining({
142142
trace: expect.objectContaining({
143143
op: 'http.server',

dev-packages/e2e-tests/test-applications/nuxt-4/tests/tracing.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ test.describe('distributed tracing', () => {
136136
expect(serverReqTxnEvent).toEqual(
137137
expect.objectContaining({
138138
type: 'transaction',
139-
transaction: `GET /api/user/${PARAM}`,
140-
transaction_info: { source: 'url' },
139+
transaction: `GET /api/user/:userId`, // parametrized route
140+
transaction_info: { source: 'route' },
141141
contexts: expect.objectContaining({
142142
trace: expect.objectContaining({
143143
op: 'http.server',
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { getActiveSpan, getCurrentScope, getRootSpan, logger, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
2+
import type { H3Event } from 'h3';
3+
4+
/**
5+
* Update the root span (transaction) name for routes with parameters based on the matched route.
6+
*/
7+
export function updateRouteBeforeResponse(event: H3Event): void {
8+
if (!event.context.matchedRoute) {
9+
return;
10+
}
11+
12+
const matchedRoutePath = event.context.matchedRoute.path;
13+
14+
// If the matched route path is defined and differs from the event's path, it indicates a parametrized route
15+
// Example: Matched route is "/users/:id" and the event's path is "/users/123",
16+
if (matchedRoutePath && matchedRoutePath !== event._path) {
17+
if (matchedRoutePath === '/**') {
18+
// todo: support parametrized SSR pageload spans
19+
// If page is server-side rendered, the whole path gets transformed to `/**` (Example : `/users/123` becomes `/**` instead of `/users/:id`).
20+
return; // Skip if the matched route is a catch-all route.
21+
}
22+
23+
const method = event._method || 'GET';
24+
25+
const parametrizedTransactionName = `${method.toUpperCase()} ${matchedRoutePath}`;
26+
getCurrentScope().setTransactionName(parametrizedTransactionName);
27+
28+
const activeSpan = getActiveSpan(); // In development mode, getActiveSpan() is always undefined
29+
if (!activeSpan) {
30+
return;
31+
}
32+
33+
const rootSpan = getRootSpan(activeSpan);
34+
if (!rootSpan) {
35+
return;
36+
}
37+
38+
rootSpan.setAttributes({
39+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
40+
'http.route': matchedRoutePath,
41+
});
42+
43+
const params = event.context?.params;
44+
45+
if (params && typeof params === 'object') {
46+
Object.entries(params).forEach(([key, value]) => {
47+
// Based on this convention: https://getsentry.github.io/sentry-conventions/generated/attributes/url.html#urlpathparameterkey
48+
rootSpan.setAttributes({
49+
[`url.path.parameter.${key}`]: String(value),
50+
[`params.${key}`]: String(value),
51+
});
52+
});
53+
}
54+
55+
logger.log(`Updated transaction name for parametrized route: ${parametrizedTransactionName}`);
56+
}
57+
}

packages/nuxt/src/runtime/plugins/sentry-cloudflare.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { H3Event } from 'h3';
66
import type { NitroApp, NitroAppPlugin } from 'nitropack';
77
import type { NuxtRenderHTMLContext } from 'nuxt/app';
88
import { sentryCaptureErrorHook } from '../hooks/captureErrorHook';
9+
import { updateRouteBeforeResponse } from '../hooks/updateRouteBeforeResponse';
910
import { addSentryTracingMetaTags } from '../utils';
1011

1112
interface CfEventType {
@@ -139,6 +140,8 @@ export const sentryCloudflareNitroPlugin =
139140
},
140141
});
141142

143+
nitroApp.hooks.hook('beforeResponse', updateRouteBeforeResponse);
144+
142145
// @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context
143146
nitroApp.hooks.hook('render:html', (html: NuxtRenderHTMLContext, { event }: { event: H3Event }) => {
144147
const storedTraceData = event?.context?.cf ? traceDataMap.get(event.context.cf) : undefined;

packages/nuxt/src/runtime/plugins/sentry.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import { type EventHandler } from 'h3';
55
import { defineNitroPlugin } from 'nitropack/runtime';
66
import type { NuxtRenderHTMLContext } from 'nuxt/app';
77
import { sentryCaptureErrorHook } from '../hooks/captureErrorHook';
8+
import { updateRouteBeforeResponse } from '../hooks/updateRouteBeforeResponse';
89
import { addSentryTracingMetaTags, flushIfServerless } from '../utils';
910

1011
export default defineNitroPlugin(nitroApp => {
1112
nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler);
1213

14+
nitroApp.hooks.hook('beforeResponse', updateRouteBeforeResponse);
15+
1316
nitroApp.hooks.hook('error', sentryCaptureErrorHook);
1417

1518
// @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context

0 commit comments

Comments
 (0)