Skip to content

Commit 9f29e0a

Browse files
authored
feat(v9/nuxt): Do not inject trace meta-tags on cached HTML pages (#17305) (#17319)
> Backport of #17305 Don't inject tracing meta tags on cached HTML pages (SWR and pre-rendered pages). This means won't have connected traces for those pages anymore. But before, multiple pageloads were listed in one trace as they were all connected to one backend request. Closes #16045 I spent most of the time creating the tests for this PR and verifying it works - the logic is so small 😅
1 parent b9c468c commit 9f29e0a

18 files changed

+460
-7
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// https://nuxt.com/docs/api/configuration/nuxt-config
22
export default defineNuxtConfig({
33
modules: ['@sentry/nuxt/module'],
4-
imports: {
5-
autoImport: false,
4+
imports: { autoImport: false },
5+
6+
routeRules: {
7+
'/rendering-modes/client-side-only-page': { ssr: false },
8+
'/rendering-modes/isr-cached-page': { isr: true },
9+
'/rendering-modes/isr-1h-cached-page': { isr: 3600 },
10+
'/rendering-modes/swr-cached-page': { swr: true },
11+
'/rendering-modes/swr-1h-cached-page': { swr: 3600 },
12+
'/rendering-modes/pre-rendered-page': { prerender: true },
613
},
14+
715
runtimeConfig: {
816
public: {
917
sentry: {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>Client Side Only Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>ISR 1h Cached Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>ISR Cached Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>Pre-Rendered Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>SWR 1h Cached Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>SWR Cached Page</p></template>
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { expect, test, type Page } from '@playwright/test';
2+
import { waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
test.describe('Rendering Modes with Cached HTML', () => {
5+
test('changes tracing meta tags with multiple requests on Client-Side only page', async ({ page }) => {
6+
await testChangingTracingMetaTagsOnISRPage(page, '/rendering-modes/client-side-only-page', 'Client Side Only Page');
7+
});
8+
9+
test('changes tracing meta tags with multiple requests on ISR-cached page', async ({ page }) => {
10+
await testChangingTracingMetaTagsOnISRPage(page, '/rendering-modes/isr-cached-page', 'ISR Cached Page');
11+
});
12+
13+
test('changes tracing meta tags with multiple requests on 1h ISR-cached page', async ({ page }) => {
14+
await testChangingTracingMetaTagsOnISRPage(page, '/rendering-modes/isr-1h-cached-page', 'ISR 1h Cached Page');
15+
});
16+
17+
test('exclude tracing meta tags on SWR-cached page', async ({ page }) => {
18+
await testExcludeTracingMetaTagsOnCachedPage(page, '/rendering-modes/swr-cached-page', 'SWR Cached Page');
19+
});
20+
21+
test('exclude tracing meta tags on SWR 1h cached page', async ({ page }) => {
22+
await testExcludeTracingMetaTagsOnCachedPage(page, '/rendering-modes/swr-1h-cached-page', 'SWR 1h Cached Page');
23+
});
24+
25+
test('exclude tracing meta tags on pre-rendered page', async ({ page }) => {
26+
await testExcludeTracingMetaTagsOnCachedPage(page, '/rendering-modes/pre-rendered-page', 'Pre-Rendered Page');
27+
});
28+
});
29+
30+
/**
31+
* Tests that tracing meta-tags change with multiple requests on ISR-cached pages
32+
* This utility handles the common pattern of:
33+
* 1. Making two requests to an ISR-cached page
34+
* 2. Verifying tracing meta-tags are present and change between requests
35+
* 3. Verifying distributed tracing works correctly for both requests
36+
* 4. Verifying trace IDs are different between requests
37+
*
38+
* @param page - Playwright page object
39+
* @param routePath - The route path to test (e.g., '/rendering-modes/isr-cached-page')
40+
* @param expectedPageText - The text to verify is visible on the page (e.g., 'ISR Cached Page')
41+
*/
42+
export async function testChangingTracingMetaTagsOnISRPage(
43+
page: Page,
44+
routePath: string,
45+
expectedPageText: string,
46+
): Promise<void> {
47+
// === 1. Request ===
48+
const clientTxnEventPromise1 = waitForTransaction('nuxt-3-min', txnEvent => {
49+
return txnEvent.transaction === routePath;
50+
});
51+
52+
const serverTxnEventPromise1 = waitForTransaction('nuxt-3-min', txnEvent => {
53+
return txnEvent.transaction?.includes(`GET ${routePath}`) ?? false;
54+
});
55+
56+
const [_1, clientTxnEvent1, serverTxnEvent1] = await Promise.all([
57+
page.goto(routePath),
58+
clientTxnEventPromise1,
59+
serverTxnEventPromise1,
60+
expect(page.getByText(expectedPageText, { exact: true })).toBeVisible(),
61+
]);
62+
63+
const baggageMetaTagContent1 = await page.locator('meta[name="baggage"]').getAttribute('content');
64+
const sentryTraceMetaTagContent1 = await page.locator('meta[name="sentry-trace"]').getAttribute('content');
65+
const [htmlMetaTraceId1] = sentryTraceMetaTagContent1?.split('-') || [];
66+
67+
// === 2. Request ===
68+
69+
const clientTxnEventPromise2 = waitForTransaction('nuxt-3-min', txnEvent => {
70+
return txnEvent.transaction === routePath;
71+
});
72+
73+
const serverTxnEventPromise2 = waitForTransaction('nuxt-3-min', txnEvent => {
74+
return txnEvent.transaction?.includes(`GET ${routePath}`) ?? false;
75+
});
76+
77+
const [_2, clientTxnEvent2, serverTxnEvent2] = await Promise.all([
78+
page.goto(routePath),
79+
clientTxnEventPromise2,
80+
serverTxnEventPromise2,
81+
expect(page.getByText(expectedPageText, { exact: true })).toBeVisible(),
82+
]);
83+
84+
const baggageMetaTagContent2 = await page.locator('meta[name="baggage"]').getAttribute('content');
85+
const sentryTraceMetaTagContent2 = await page.locator('meta[name="sentry-trace"]').getAttribute('content');
86+
const [htmlMetaTraceId2] = sentryTraceMetaTagContent2?.split('-') || [];
87+
88+
const serverTxnEvent1TraceId = serverTxnEvent1.contexts?.trace?.trace_id;
89+
const serverTxnEvent2TraceId = serverTxnEvent2.contexts?.trace?.trace_id;
90+
91+
await test.step('Test distributed trace from 1. request', () => {
92+
expect(baggageMetaTagContent1).toContain(`sentry-trace_id=${serverTxnEvent1TraceId}`);
93+
94+
expect(clientTxnEvent1.contexts?.trace?.trace_id).toBe(serverTxnEvent1TraceId);
95+
expect(clientTxnEvent1.contexts?.trace?.parent_span_id).toBe(serverTxnEvent1.contexts?.trace?.span_id);
96+
expect(serverTxnEvent1.contexts?.trace?.trace_id).toBe(htmlMetaTraceId1);
97+
});
98+
99+
await test.step('Test distributed trace from 2. request', () => {
100+
expect(baggageMetaTagContent2).toContain(`sentry-trace_id=${serverTxnEvent2TraceId}`);
101+
102+
expect(clientTxnEvent2.contexts?.trace?.trace_id).toBe(serverTxnEvent2TraceId);
103+
expect(clientTxnEvent2.contexts?.trace?.parent_span_id).toBe(serverTxnEvent2.contexts?.trace?.span_id);
104+
expect(serverTxnEvent2.contexts?.trace?.trace_id).toBe(htmlMetaTraceId2);
105+
});
106+
107+
await test.step('Test that trace IDs from subsequent requests are different', () => {
108+
// Different trace IDs for the server transactions
109+
expect(serverTxnEvent1TraceId).toBeDefined();
110+
expect(serverTxnEvent1TraceId).not.toBe(serverTxnEvent2TraceId);
111+
expect(serverTxnEvent1TraceId).not.toBe(htmlMetaTraceId2);
112+
});
113+
}
114+
115+
/**
116+
* Tests that tracing meta-tags are excluded on cached pages (SWR, pre-rendered, etc.)
117+
* This utility handles the common pattern of:
118+
* 1. Making two requests to a cached page
119+
* 2. Verifying no tracing meta-tags are present
120+
* 3. Verifying only the first request creates a server transaction
121+
* 4. Verifying traces are not distributed
122+
*
123+
* @param page - Playwright page object
124+
* @param routePath - The route path to test (e.g., '/rendering-modes/swr-cached-page')
125+
* @param expectedPageText - The text to verify is visible on the page (e.g., 'SWR Cached Page')
126+
* @returns Object containing transaction events for additional custom assertions
127+
*/
128+
export async function testExcludeTracingMetaTagsOnCachedPage(
129+
page: Page,
130+
routePath: string,
131+
expectedPageText: string,
132+
): Promise<void> {
133+
// === 1. Request ===
134+
const clientTxnEventPromise1 = waitForTransaction('nuxt-3-min', txnEvent => {
135+
return txnEvent.transaction === routePath;
136+
});
137+
138+
// Only the 1. request creates a server transaction
139+
const serverTxnEventPromise1 = waitForTransaction('nuxt-3-min', txnEvent => {
140+
return txnEvent.transaction?.includes(`GET ${routePath}`) ?? false;
141+
});
142+
143+
const [_1, clientTxnEvent1, serverTxnEvent1] = await Promise.all([
144+
page.goto(routePath),
145+
clientTxnEventPromise1,
146+
serverTxnEventPromise1,
147+
expect(page.getByText(expectedPageText, { exact: true })).toBeVisible(),
148+
]);
149+
150+
// Verify no baggage and sentry-trace meta-tags are present on first request
151+
expect(await page.locator('meta[name="baggage"]').count()).toBe(0);
152+
expect(await page.locator('meta[name="sentry-trace"]').count()).toBe(0);
153+
154+
// === 2. Request ===
155+
156+
await page.goto(routePath);
157+
158+
const clientTxnEventPromise2 = waitForTransaction('nuxt-3-min', txnEvent => {
159+
return txnEvent.transaction === routePath;
160+
});
161+
162+
let serverTxnEvent2 = undefined;
163+
const serverTxnEventPromise2 = Promise.race([
164+
waitForTransaction('nuxt-3-min', txnEvent => {
165+
return txnEvent.transaction?.includes(`GET ${routePath}`) ?? false;
166+
}),
167+
new Promise((_, reject) => setTimeout(() => reject(new Error('No second server transaction expected')), 2000)),
168+
]);
169+
170+
try {
171+
serverTxnEvent2 = await serverTxnEventPromise2;
172+
throw new Error('Second server transaction should not have been sent');
173+
} catch (error) {
174+
expect(error.message).toBe('No second server transaction expected');
175+
}
176+
177+
const [clientTxnEvent2] = await Promise.all([
178+
clientTxnEventPromise2,
179+
expect(page.getByText(expectedPageText, { exact: true })).toBeVisible(),
180+
]);
181+
182+
const clientTxnEvent1TraceId = clientTxnEvent1.contexts?.trace?.trace_id;
183+
const clientTxnEvent2TraceId = clientTxnEvent2.contexts?.trace?.trace_id;
184+
185+
const serverTxnEvent1TraceId = serverTxnEvent1.contexts?.trace?.trace_id;
186+
const serverTxnEvent2TraceId = serverTxnEvent2?.contexts?.trace?.trace_id;
187+
188+
await test.step('No baggage and sentry-trace meta-tags are present on second request', async () => {
189+
expect(await page.locator('meta[name="baggage"]').count()).toBe(0);
190+
expect(await page.locator('meta[name="sentry-trace"]').count()).toBe(0);
191+
});
192+
193+
await test.step('1. Server Transaction and all Client Transactions are defined', () => {
194+
expect(serverTxnEvent1TraceId).toBeDefined();
195+
expect(clientTxnEvent1TraceId).toBeDefined();
196+
expect(clientTxnEvent2TraceId).toBeDefined();
197+
expect(serverTxnEvent2).toBeUndefined();
198+
expect(serverTxnEvent2TraceId).toBeUndefined();
199+
});
200+
201+
await test.step('Trace is not distributed', () => {
202+
// Cannot create distributed trace as HTML Meta Tags are not added (caching leads to multiple usages of the same server trace id)
203+
expect(clientTxnEvent1TraceId).not.toBe(clientTxnEvent2TraceId);
204+
expect(clientTxnEvent1TraceId).not.toBe(serverTxnEvent1TraceId);
205+
});
206+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>Client Side Only Page</p></template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template><p>ISR 1h Cached Page</p></template>

0 commit comments

Comments
 (0)