From 0f1d2aafe1b4d9858b044057b567e144fd164f98 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 31 Jul 2024 13:35:34 +0200 Subject: [PATCH 1/4] fix(nuxt): Filter out Nuxt build assets --- packages/nuxt/src/server/sdk.ts | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/server/sdk.ts b/packages/nuxt/src/server/sdk.ts index 4faae8e6ef1a..3a37fb2b8fb3 100644 --- a/packages/nuxt/src/server/sdk.ts +++ b/packages/nuxt/src/server/sdk.ts @@ -1,6 +1,6 @@ -import { applySdkMetadata } from '@sentry/core'; +import { applySdkMetadata, getGlobalScope } from '@sentry/core'; import { init as initNode } from '@sentry/node'; -import type { Client } from '@sentry/types'; +import type { Client, EventProcessor } from '@sentry/types'; import type { SentryNuxtOptions } from '../common/types'; /** @@ -15,5 +15,29 @@ export function init(options: SentryNuxtOptions): Client | undefined { applySdkMetadata(sentryOptions, 'nuxt', ['nuxt', 'node']); - return initNode(sentryOptions); + const client = initNode(sentryOptions); + + getGlobalScope().addEventProcessor( + Object.assign( + (event => { + if (event.type === 'transaction') { + // Filter out transactions for Nuxt build assets + // This regex matches the default path to the nuxt-generated build assets (`_nuxt`). + if (event.transaction?.match(/^GET \/_nuxt\//)) { + options.debug && + // eslint-disable-next-line no-console + console.log('[Sentry] NuxtLowQualityTransactionsFilter filtered transaction: ', event.transaction); + return null; + } + + return event; + } else { + return event; + } + }) satisfies EventProcessor, + { id: 'NuxtLowQualityTransactionsFilter' }, + ), + ); + + return client; } From 1f4dee113a3227aba5508f337dd1e86b7cbd46f1 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 1 Aug 2024 09:49:44 +0200 Subject: [PATCH 2/4] add tests --- .../test-applications/nuxt-3/package.json | 2 +- .../nuxt-3/public/instrument.server.mjs | 8 ++++ .../nuxt-3/tests/performance.client.test.ts | 47 +++++++++++++++++++ packages/nuxt/test/server/sdk.test.ts | 42 +++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3/public/instrument.server.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts create mode 100644 packages/nuxt/test/server/sdk.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/package.json b/dev-packages/e2e-tests/test-applications/nuxt-3/package.json index 72acea9f33b6..a487d61a144b 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/package.json @@ -6,7 +6,7 @@ "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", - "preview": "nuxt preview", + "preview": "NODE_OPTIONS='--import ./public/instrument.server.mjs' nuxt preview", "clean": "npx nuxi cleanup", "test": "playwright test", "test:build": "pnpm install && npx playwright install && pnpm build", diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/public/instrument.server.mjs b/dev-packages/e2e-tests/test-applications/nuxt-3/public/instrument.server.mjs new file mode 100644 index 000000000000..729b2296c683 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/public/instrument.server.mjs @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/nuxt'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + environment: 'qa', // dynamic sampling bias to keep transactions + tracesSampleRate: 1.0, // Capture 100% of the transactions + tunnel: 'http://localhost:3031/', // proxy server +}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts new file mode 100644 index 000000000000..506137cd0b8f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts @@ -0,0 +1,47 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; +import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; + +test('sends a server action transaction on pageload', async ({ page }) => { + const transactionPromise = waitForTransaction('nuxt-3', transactionEvent => { + return transactionEvent.transaction.includes('GET /test-param/'); + }); + + await page.goto('/test-param/1234'); + + const transaction = await transactionPromise; + + expect(transaction.contexts.trace).toEqual( + expect.objectContaining({ + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.http', + }), + }), + ); +}); + +test('does not send transactions for build asset folder "_nuxt"', async ({ page }) => { + let buildAssetFolderOccurred = false; + + waitForTransaction('nuxt-3', transactionEvent => { + if (transactionEvent.transaction.includes('/_nuxt/')) { + buildAssetFolderOccurred = true; + } + return false; // expects to return a boolean (but not relevant here) + }); + + const transactionEventPromise = waitForTransaction('nuxt-3', transactionEvent => { + return transactionEvent.transaction.includes('GET /test-param/'); + }); + + await page.goto('/test-param/1234'); + + const transactionEvent = await transactionEventPromise; + + expect(buildAssetFolderOccurred).toBe(false); + + // todo: url not yet parametrized + expect(transactionEvent.transaction).toBe('GET /test-param/1234'); + expect(buildAssetFolderOccurred).toBe(false); +}); diff --git a/packages/nuxt/test/server/sdk.test.ts b/packages/nuxt/test/server/sdk.test.ts new file mode 100644 index 000000000000..8d84dc8b15c8 --- /dev/null +++ b/packages/nuxt/test/server/sdk.test.ts @@ -0,0 +1,42 @@ +import * as SentryNode from '@sentry/node'; +import { SDK_VERSION } from '@sentry/node'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { init } from '../../src/server'; + +const nodeInit = vi.spyOn(SentryNode, 'init'); + +describe('Nuxt Server SDK', () => { + describe('init', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('Adds Nuxt metadata to the SDK options', () => { + expect(nodeInit).not.toHaveBeenCalled(); + + init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const expectedMetadata = { + _metadata: { + sdk: { + name: 'sentry.javascript.nuxt', + version: SDK_VERSION, + packages: [ + { name: 'npm:@sentry/nuxt', version: SDK_VERSION }, + { name: 'npm:@sentry/node', version: SDK_VERSION }, + ], + }, + }, + }; + + expect(nodeInit).toHaveBeenCalledTimes(1); + expect(nodeInit).toHaveBeenLastCalledWith(expect.objectContaining(expectedMetadata)); + }); + + it('returns client from init', () => { + expect(init({})).not.toBeUndefined(); + }); + }); +}); From 71385dc19fc16b06ef8a74a78435bbcdf4fbe50f Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 1 Aug 2024 10:15:40 +0200 Subject: [PATCH 3/4] rename file to "server" --- .../{performance.client.test.ts => performance.server.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dev-packages/e2e-tests/test-applications/nuxt-3/tests/{performance.client.test.ts => performance.server.test.ts} (100%) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts rename to dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts From 3771be3f2377afec3d53bf73d9fe366570b2d83d Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 1 Aug 2024 11:26:47 +0200 Subject: [PATCH 4/4] add todo comment; change test slightly --- .../test-applications/nuxt-3/tests/performance.server.test.ts | 3 +-- packages/nuxt/src/server/sdk.ts | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts index 506137cd0b8f..5b78e235e564 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts @@ -25,7 +25,7 @@ test('does not send transactions for build asset folder "_nuxt"', async ({ page let buildAssetFolderOccurred = false; waitForTransaction('nuxt-3', transactionEvent => { - if (transactionEvent.transaction.includes('/_nuxt/')) { + if (transactionEvent.transaction?.match(/^GET \/_nuxt\//)) { buildAssetFolderOccurred = true; } return false; // expects to return a boolean (but not relevant here) @@ -43,5 +43,4 @@ test('does not send transactions for build asset folder "_nuxt"', async ({ page // todo: url not yet parametrized expect(transactionEvent.transaction).toBe('GET /test-param/1234'); - expect(buildAssetFolderOccurred).toBe(false); }); diff --git a/packages/nuxt/src/server/sdk.ts b/packages/nuxt/src/server/sdk.ts index 3a37fb2b8fb3..f14cc23ab8cd 100644 --- a/packages/nuxt/src/server/sdk.ts +++ b/packages/nuxt/src/server/sdk.ts @@ -23,6 +23,7 @@ export function init(options: SentryNuxtOptions): Client | undefined { if (event.type === 'transaction') { // Filter out transactions for Nuxt build assets // This regex matches the default path to the nuxt-generated build assets (`_nuxt`). + // todo: the buildAssetDir could be changed in the nuxt config - change this to a more generic solution if (event.transaction?.match(/^GET \/_nuxt\//)) { options.debug && // eslint-disable-next-line no-console