From cb21badc08c4d899aea49ef7c8956b832d15424c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 5 Aug 2024 14:42:33 +0200 Subject: [PATCH 01/10] fix(sveltekit): Ensure trace meta tags are always injected --- packages/sveltekit/src/server/handle.ts | 32 ++++++------------- packages/sveltekit/test/server/handle.test.ts | 20 +++++++----- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index 7f4b8d980ad8..cee8520400c1 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -5,19 +5,16 @@ import { getCurrentScope, getDefaultIsolationScope, getIsolationScope, - getRootSpan, + getTraceMetaTags, setHttpStatus, - spanToTraceHeader, withIsolationScope, } from '@sentry/core'; import { startSpan } from '@sentry/core'; import { continueTrace } from '@sentry/node'; import type { Span } from '@sentry/types'; -import { dynamicSamplingContextToSentryBaggageHeader, logger, winterCGRequestToRequestData } from '@sentry/utils'; +import { logger, winterCGRequestToRequestData } from '@sentry/utils'; import type { Handle, ResolveOptions } from '@sveltejs/kit'; -import { getDynamicSamplingContextFromSpan } from '@sentry/opentelemetry'; - import { DEBUG_BUILD } from '../common/debug-build'; import { flushIfServerless, getTracePropagationData, sendErrorToSentry } from './utils'; @@ -81,25 +78,14 @@ export function addSentryCodeToPage(options: SentryHandleOptions): NonNullable { - const activeSpan = getActiveSpan(); - const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined; - if (rootSpan) { - const traceparentData = spanToTraceHeader(rootSpan); - const dynamicSamplingContext = dynamicSamplingContextToSentryBaggageHeader( - getDynamicSamplingContextFromSpan(rootSpan), - ); - const contentMeta = ` - - - `; - const contentScript = shouldInjectScript ? `` : ''; - - const content = `${contentMeta}\n${contentScript}`; - - return html.replace('', content); - } + const metaTags = getTraceMetaTags(); + const headWithMetaTags = metaTags ? `\n${metaTags}` : ''; + + const headWithFetchScript = shouldInjectScript ? `\n` : ''; + + const modifiedHead = `${headWithMetaTags}${headWithFetchScript}`; - return html; + return html.replace('', modifiedHead); }; } diff --git a/packages/sveltekit/test/server/handle.test.ts b/packages/sveltekit/test/server/handle.test.ts index ad49e9a16ef8..7b5b86d541b2 100644 --- a/packages/sveltekit/test/server/handle.test.ts +++ b/packages/sveltekit/test/server/handle.test.ts @@ -429,10 +429,14 @@ describe('addSentryCodeToPage', () => { `; - it('does not add meta tags if no active transaction', () => { + it("Adds add meta tags and fetch proxy script if there's no active transaction", () => { const transformPageChunk = addSentryCodeToPage({}); const transformed = transformPageChunk({ html, done: true }); - expect(transformed).toEqual(html); + + expect(transformed).toContain(' { @@ -442,6 +446,7 @@ describe('addSentryCodeToPage', () => { expect(transformed).toContain('${FETCH_PROXY_SCRIPT}`); }); }); @@ -453,18 +458,17 @@ describe('addSentryCodeToPage', () => { expect(transformed).toContain('${FETCH_PROXY_SCRIPT}`); }); }); it('does not add the fetch proxy script if the `injectFetchProxyScript` option is false', () => { const transformPageChunk = addSentryCodeToPage({ injectFetchProxyScript: false }); - SentryNode.startSpan({ name: 'test' }, () => { - const transformed = transformPageChunk({ html, done: true }) as string; + const transformed = transformPageChunk({ html, done: true }) as string; - expect(transformed).toContain('${FETCH_PROXY_SCRIPT}`); - }); + expect(transformed).toContain('${FETCH_PROXY_SCRIPT}`); }); }); From ecdec58ecba9b12813b62355e215dae8dfe93662 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 5 Aug 2024 14:47:02 +0200 Subject: [PATCH 02/10] cleanup --- packages/sveltekit/src/server/handle.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index cee8520400c1..1124973ea177 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -5,14 +5,21 @@ import { getCurrentScope, getDefaultIsolationScope, getIsolationScope, +<<<<<<< HEAD getTraceMetaTags, +======= +>>>>>>> af5d32206 (cleanup) setHttpStatus, withIsolationScope, } from '@sentry/core'; import { startSpan } from '@sentry/core'; import { continueTrace } from '@sentry/node'; import type { Span } from '@sentry/types'; +<<<<<<< HEAD import { logger, winterCGRequestToRequestData } from '@sentry/utils'; +======= +import { logger, objectify, winterCGRequestToRequestData } from '@sentry/utils'; +>>>>>>> af5d32206 (cleanup) import type { Handle, ResolveOptions } from '@sveltejs/kit'; import { DEBUG_BUILD } from '../common/debug-build'; From 0f6793bcbb2bc2bf3afa0873343af38849032929 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 9 Aug 2024 10:28:06 +0200 Subject: [PATCH 03/10] add e2e test app --- .../sveltekit-2-twp/.gitignore | 10 ++++ .../test-applications/sveltekit-2-twp/.npmrc | 2 + .../sveltekit-2-twp/README.md | 3 ++ .../sveltekit-2-twp/package.json | 35 +++++++++++++ .../sveltekit-2-twp/playwright.config.mjs | 8 +++ .../sveltekit-2-twp/src/app.d.ts | 13 +++++ .../sveltekit-2-twp/src/app.html | 12 +++++ .../sveltekit-2-twp/src/hooks.client.ts | 17 ++++++ .../sveltekit-2-twp/src/hooks.server.ts | 13 +++++ .../sveltekit-2-twp/src/routes/+layout.svelte | 15 ++++++ .../sveltekit-2-twp/src/routes/+page.svelte | 8 +++ .../src/routes/errors/+page.server.ts | 13 +++++ .../src/routes/errors/+page.svelte | 17 ++++++ .../sveltekit-2-twp/start-event-proxy.mjs | 6 +++ .../sveltekit-2-twp/static/favicon.png | Bin 0 -> 1571 bytes .../sveltekit-2-twp/svelte.config.js | 18 +++++++ .../sveltekit-2-twp/tests/errors.test.ts | 5 ++ .../sveltekit-2-twp/tests/tracing.test.ts | 23 ++++++++ .../sveltekit-2-twp/tests/utils.ts | 49 ++++++++++++++++++ .../sveltekit-2-twp/tsconfig.json | 19 +++++++ .../sveltekit-2-twp/vite.config.ts | 12 +++++ 21 files changed, 298 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/README.md create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/package.json create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.html create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+layout.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/static/favicon.png create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/svelte.config.js create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/tracing.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/utils.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-twp/vite.config.ts diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.gitignore b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.gitignore new file mode 100644 index 000000000000..6635cf554275 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/README.md b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/README.md new file mode 100644 index 000000000000..38a3b388452b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/README.md @@ -0,0 +1,3 @@ +# SvelteKit 2 TwP + +E2E test app for testing Tracing Without Performance in a (SvelteKit) meta framework scenario diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/package.json new file mode 100644 index 000000000000..a88decc74e7b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/package.json @@ -0,0 +1,35 @@ +{ + "name": "sveltekit-2-svelte-5", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "proxy": "node start-event-proxy.mjs", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "test:prod": "TEST_ENV=production playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm test:prod" + }, + "dependencies": { + "@sentry/sveltekit": "latest || *" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/types": "latest || *", + "@sentry/utils": "latest || *", + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^5.0.0-next.115", + "svelte-check": "^3.6.0", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^5.0.3" + }, + "type": "module" +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/playwright.config.mjs new file mode 100644 index 000000000000..0c468af7d879 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/playwright.config.mjs @@ -0,0 +1,8 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: 'pnpm preview --port 3030', + port: 3030, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.d.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.d.ts new file mode 100644 index 000000000000..ede601ab93e2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.html b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.html new file mode 100644 index 000000000000..77a5ff52c923 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts new file mode 100644 index 000000000000..00c174516022 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts @@ -0,0 +1,17 @@ +import { env } from '$env/dynamic/public'; +import * as Sentry from '@sentry/sveltekit'; + +console.log('dsn', env.PUBLIC_E2E_TEST_DSN); + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: env.PUBLIC_E2E_TEST_DSN, + release: '1.0.0', + tunnel: `http://localhost:3031/`, // proxy server +}); + +const myErrorHandler = ({ error, event }: any) => { + console.error('An error occurred on the client side:', error, event); +}; + +export const handleError = Sentry.handleErrorWithSentry(myErrorHandler); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts new file mode 100644 index 000000000000..e60e51b25968 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts @@ -0,0 +1,13 @@ +import { E2E_TEST_DSN } from '$env/static/private'; +import * as Sentry from '@sentry/sveltekit'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: E2E_TEST_DSN, + tunnel: `http://localhost:3031/`, // proxy server +}); + +// not logging anything to console to avoid noise in the test output +export const handleError = Sentry.handleErrorWithSentry(() => {}); + +export const handle = Sentry.sentryHandle(); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+layout.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+layout.svelte new file mode 100644 index 000000000000..d1fadd2ea5a3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+layout.svelte @@ -0,0 +1,15 @@ + + +

Sveltekit E2E Test app

+
+ +
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+page.svelte new file mode 100644 index 000000000000..bbf21b25c01b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/+page.svelte @@ -0,0 +1,8 @@ +

Welcome to SvelteKit 2 with Svelte 5!

+

Visit kit.svelte.dev to read the documentation

+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts new file mode 100644 index 000000000000..a8405926f90b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts @@ -0,0 +1,13 @@ +import * as Sentry from '@sentry/sveltekit'; + +export const load = async ({ url }) => { + if (!url.search) { + Sentry.captureException(new Error('No search query provided')); + return { + error: 'No search query provided', + }; + } + return { + message: 'hi', + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte new file mode 100644 index 000000000000..3e48135488b9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte @@ -0,0 +1,17 @@ + + +

+ Data: + {data.message || 'nothing'} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/start-event-proxy.mjs new file mode 100644 index 000000000000..01e1095d6956 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'sveltekit-2-twp', +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/static/favicon.png b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH { + // TODO once browserTracingIntegration is always added +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/tracing.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/tracing.test.ts new file mode 100644 index 000000000000..6ed20d16b5e9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/tracing.test.ts @@ -0,0 +1,23 @@ +import { expect, test } from '@playwright/test'; + +test('Initially loaded page contains trace meta tags from backend trace', async ({ page }) => { + await page.goto('/'); + + const sentryTraceMetaTag = page.locator('meta[name="sentry-trace"]').first(); + const sentryTraceContent = await sentryTraceMetaTag.getAttribute('content'); + + const baggageMetaTag = page.locator('meta[name="baggage"]').first(); + const baggageContent = await baggageMetaTag.getAttribute('content'); + + // ensure that we do not pass a sampled -1 or -0 flag at the end: + expect(sentryTraceContent).toMatch(/^[a-f0-9]{32}-[a-f0-9]{16}$/); + + expect(baggageContent?.length).toBeGreaterThan(0); + + const traceId = sentryTraceContent!.split('-')[0]; + + expect(baggageContent).toContain('sentry-environment=qa'); + expect(baggageContent).toContain(`sentry-trace_id=${traceId}`); + // ensure baggage also doesn't contain a sampled flag + expect(baggageContent).not.toContain('sentry-sampled='); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/utils.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/utils.ts new file mode 100644 index 000000000000..e77e2eb742e2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/utils.ts @@ -0,0 +1,49 @@ +import { Page } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +/** + * Helper function that waits for the initial pageload to complete. + * + * This function + * - loads the given route ("/" by default) + * - waits for SvelteKit's hydration + * - waits for the pageload transaction to be sent (doesn't assert on it though) + * + * Useful for tests that test outcomes of _navigations_ after an initial pageload. + * Waiting on the pageload transaction excludes edge cases where navigations occur + * so quickly that the pageload idle transaction is still active. This might lead + * to cases where the routing span would be attached to the pageload transaction + * and hence eliminates a lot of flakiness. + * + */ +export async function waitForInitialPageload( + page: Page, + opts?: { route?: string; parameterizedRoute?: string; debug?: boolean }, +) { + const route = opts?.route ?? '/'; + const txnName = opts?.parameterizedRoute ?? route; + const debug = opts?.debug ?? false; + + const clientPageloadTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + debug && + console.log({ + txn: txnEvent?.transaction, + op: txnEvent.contexts?.trace?.op, + trace: txnEvent.contexts?.trace?.trace_id, + span: txnEvent.contexts?.trace?.span_id, + parent: txnEvent.contexts?.trace?.parent_span_id, + }); + + return txnEvent?.transaction === txnName && txnEvent.contexts?.trace?.op === 'pageload'; + }); + + await Promise.all([ + page.goto(route), + // the test app adds the "hydrated" class to the body when hydrating + page.waitForSelector('body.hydrated'), + // also waiting for the initial pageload txn so that later navigations don't interfere + clientPageloadTxnEventPromise, + ]); + + debug && console.log('hydrated'); +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tsconfig.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tsconfig.json new file mode 100644 index 000000000000..ba6aa4e6610a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + }, + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/vite.config.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/vite.config.ts new file mode 100644 index 000000000000..1a410bee7e11 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/vite.config.ts @@ -0,0 +1,12 @@ +import { sentrySvelteKit } from '@sentry/sveltekit'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sentrySvelteKit({ + autoUploadSourceMaps: false, + }), + sveltekit(), + ], +}); From 5740af6afbce381bbe4a8e198d3f4d0caee19dff Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 9 Aug 2024 10:28:41 +0200 Subject: [PATCH 04/10] add e2e to build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 624e4d975b27..5ed034e8cdc2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -915,6 +915,7 @@ jobs: 'sveltekit', 'sveltekit-2', 'sveltekit-2-svelte-5', + 'sveltekit-2-twp', 'tanstack-router', 'generic-ts3.8', 'node-fastify', From 39a7c4112bc43699c4c1c263b89c5a0f373f19d4 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 14 Aug 2024 12:45:47 +0200 Subject: [PATCH 05/10] fix rebase conflict --- packages/sveltekit/src/server/handle.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index 1124973ea177..cee8520400c1 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -5,21 +5,14 @@ import { getCurrentScope, getDefaultIsolationScope, getIsolationScope, -<<<<<<< HEAD getTraceMetaTags, -======= ->>>>>>> af5d32206 (cleanup) setHttpStatus, withIsolationScope, } from '@sentry/core'; import { startSpan } from '@sentry/core'; import { continueTrace } from '@sentry/node'; import type { Span } from '@sentry/types'; -<<<<<<< HEAD import { logger, winterCGRequestToRequestData } from '@sentry/utils'; -======= -import { logger, objectify, winterCGRequestToRequestData } from '@sentry/utils'; ->>>>>>> af5d32206 (cleanup) import type { Handle, ResolveOptions } from '@sveltejs/kit'; import { DEBUG_BUILD } from '../common/debug-build'; From c0f00c46273c70ed054a62c78e8012c373f4ee69 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 14 Aug 2024 13:51:06 +0200 Subject: [PATCH 06/10] wip - still an error in backend error trace prop --- .../tracing-without-performance/test.ts | 32 ++++++++ .../sveltekit-2-twp/src/hooks.client.ts | 4 + .../sveltekit-2-twp/src/hooks.server.ts | 4 + .../sveltekit-2-twp/tests/errors.test.ts | 75 ++++++++++++++++++- 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance/test.ts index 76a618f79989..8fceec718447 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/tracing-without-performance/test.ts @@ -12,6 +12,38 @@ const META_TAG_PARENT_SPAN_ID = '1234567890123456'; const META_TAG_BAGGAGE = 'sentry-trace_id=12345678901234567890123456789012,sentry-public_key=public,sentry-release=1.0.0,sentry-environment=prod'; +sentryTest('error on initial page has traceId from meta tag', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + const errorEventPromise = getFirstSentryEnvelopeRequest( + page, + undefined, + eventAndTraceHeaderRequestParser, + ); + + await page.locator('#errorBtn').click(); + const [errorEvent, errorTraceHeader] = await errorEventPromise; + + expect(errorEvent.type).toEqual(undefined); + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + + expect(errorTraceHeader).toEqual({ + environment: 'prod', + public_key: 'public', + release: '1.0.0', + trace_id: META_TAG_TRACE_ID, + }); +}); + sentryTest('error has new traceId after navigation', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts index 00c174516022..051b4a1d3c61 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.client.ts @@ -8,6 +8,10 @@ Sentry.init({ dsn: env.PUBLIC_E2E_TEST_DSN, release: '1.0.0', tunnel: `http://localhost:3031/`, // proxy server + beforeSend(event) { + console.log('beforeSend', event.contexts?.trace?.trace_id); + return event; + }, }); const myErrorHandler = ({ error, event }: any) => { diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts index e60e51b25968..f70c5dfcf585 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/hooks.server.ts @@ -5,6 +5,10 @@ Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server + beforeSend(event) { + console.log('beforeSend', event.contexts?.trace?.trace_id); + return event; + }, }); // not logging anything to console to avoid noise in the test output diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/errors.test.ts index 5871ad2795d7..46a98027fd26 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/tests/errors.test.ts @@ -1,5 +1,74 @@ -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; +test('errors on frontend and backend are connected by the same trace', async ({ page }) => { + const clientErrorPromise = waitForError('sveltekit-2-twp', evt => { + return evt.exception?.values?.[0].value === 'Client Error'; + }); -test.skip('errors on frontend and backend are connected by the same trace', async ({ page }) => { - // TODO once browserTracingIntegration is always added + const serverErrorPromise = waitForError('sveltekit-2-twp', evt => { + return evt.exception?.values?.[0].value === 'No search query provided'; + }); + + await page.goto('/errors'); + + const clientError = await clientErrorPromise; + const serverError = await serverErrorPromise; + + expect(clientError).toMatchObject({ + contexts: { + trace: { + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + environment: 'qa', + exception: { + values: [ + { + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: expect.any(Object), + type: 'Error', + value: 'Client Error', + }, + ], + }, + level: 'error', + platform: 'javascript', + release: '1.0.0', + timestamp: expect.any(Number), + transaction: '/errors', + }); + + expect(serverError).toMatchObject({ + contexts: { + trace: { + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + environment: 'qa', + exception: { + values: [ + { + mechanism: { + handled: true, + type: 'generic', + }, + stacktrace: {}, + }, + ], + }, + platform: 'node', + timestamp: expect.any(Number), + transaction: 'GET /errors', + }); + + const clientTraceId = clientError.contexts?.trace?.trace_id; + const serverTraceId = serverError.contexts?.trace?.trace_id; + + expect(clientTraceId).toBe(serverTraceId); }); From a5b28a6843a004b74b3b97ecbd1b92c03d896441 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 14 Aug 2024 16:50:35 +0200 Subject: [PATCH 07/10] some more wip --- .../sveltekit-2-twp/src/routes/errors/+page.server.ts | 3 +++ .../sveltekit-2-twp/src/routes/errors/+page.svelte | 2 ++ 2 files changed, 5 insertions(+) diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts index a8405926f90b..c9658daa0396 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts @@ -2,6 +2,9 @@ import * as Sentry from '@sentry/sveltekit'; export const load = async ({ url }) => { if (!url.search) { + console.log('traceData: ', Sentry.getTraceData()); + console.log('spanToTrace', Sentry.spanToTraceHeader(Sentry.getActiveSpan()!)); + console.log('activeSpan', Sentry.getActiveSpan()); Sentry.captureException(new Error('No search query provided')); return { error: 'No search query provided', diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte index 3e48135488b9..79c7293f7fc3 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte @@ -1,11 +1,13 @@ -

Sveltekit E2E Test app

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts index c9658daa0396..a8405926f90b 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.server.ts @@ -2,9 +2,6 @@ import * as Sentry from '@sentry/sveltekit'; export const load = async ({ url }) => { if (!url.search) { - console.log('traceData: ', Sentry.getTraceData()); - console.log('spanToTrace', Sentry.spanToTraceHeader(Sentry.getActiveSpan()!)); - console.log('activeSpan', Sentry.getActiveSpan()); Sentry.captureException(new Error('No search query provided')); return { error: 'No search query provided', diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte index 79c7293f7fc3..f0dde3f9b6d6 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-twp/src/routes/errors/+page.svelte @@ -1,13 +1,10 @@