From d3f8cd9f078c3e64117d8c3e28e51d9b933669b9 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 17 Nov 2025 09:54:24 +0100 Subject: [PATCH 1/2] emit processed metric --- .../metrics/afterCaptureMetric/subject.js | 15 +++++ .../metrics/afterCaptureMetric/test.ts | 67 +++++++++++++++++++ packages/core/src/metrics/internal.ts | 2 +- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js new file mode 100644 index 000000000000..1d1cccf0f1e7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js @@ -0,0 +1,15 @@ +// Store captured metrics from the afterCaptureMetric event +window.capturedMetrics = []; + +const client = Sentry.getClient(); + +client.on('afterCaptureMetric', metric => { + window.capturedMetrics.push(metric); +}); + +Sentry.metrics.count('test.counter', 1, { attributes: { endpoint: '/api/test' } }); +Sentry.metrics.gauge('test.gauge', 42, { unit: 'millisecond', attributes: { server: 'test-1' } }); +Sentry.setUser({ id: 'user-123', email: 'test@example.com', username: 'testuser' }); +Sentry.metrics.distribution('test.distribution', 200, { unit: 'second', attributes: { priority: 'high' } }); + +Sentry.flush(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts new file mode 100644 index 000000000000..a677fe364812 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts @@ -0,0 +1,67 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; + +sentryTest('should emit afterCaptureMetric event for captured metrics', async ({ getLocalTestUrl, page }) => { + const bundle = process.env.PW_BUNDLE || ''; + if (bundle.startsWith('bundle') || bundle.startsWith('loader')) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + await page.waitForFunction(() => { + return (window as any).capturedMetrics.length >= 3; + }); + + const capturedMetrics = await page.evaluate(() => { + return (window as any).capturedMetrics; + }); + + expect(capturedMetrics).toHaveLength(3); + + expect(capturedMetrics[0]).toMatchObject({ + name: 'test.counter', + type: 'counter', + value: 1, + attributes: { + endpoint: '/api/test', + 'sentry.release': '1.0.0', + 'sentry.environment': 'test', + 'sentry.sdk.name': 'sentry.javascript.browser', + }, + }); + + expect(capturedMetrics[1]).toMatchObject({ + name: 'test.gauge', + type: 'gauge', + unit: 'millisecond', + value: 42, + attributes: { + server: 'test-1', + 'sentry.release': '1.0.0', + 'sentry.environment': 'test', + 'sentry.sdk.name': 'sentry.javascript.browser', + }, + }); + + expect(capturedMetrics[2]).toMatchObject({ + name: 'test.distribution', + type: 'distribution', + unit: 'second', + value: 200, + attributes: { + priority: 'high', + 'user.id': 'user-123', + 'user.email': 'test@example.com', + 'user.name': 'testuser', + 'sentry.release': '1.0.0', + 'sentry.environment': 'test', + 'sentry.sdk.name': 'sentry.javascript.browser', + }, + }); + + expect(capturedMetrics[0].attributes['sentry.sdk.version']).toBeDefined(); + expect(capturedMetrics[1].attributes['sentry.sdk.version']).toBeDefined(); + expect(capturedMetrics[2].attributes['sentry.sdk.version']).toBeDefined(); +}); diff --git a/packages/core/src/metrics/internal.ts b/packages/core/src/metrics/internal.ts index 5371ecba8dfd..dd8bb1afd581 100644 --- a/packages/core/src/metrics/internal.ts +++ b/packages/core/src/metrics/internal.ts @@ -241,7 +241,7 @@ export function _INTERNAL_captureMetric(beforeMetric: Metric, options?: Internal captureSerializedMetric(client, serializedMetric); - client.emit('afterCaptureMetric', enrichedMetric); + client.emit('afterCaptureMetric', processedMetric); } /** From e8740fcf87ba1e07f632d973246478f6e7b13afb Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 17 Nov 2025 10:00:17 +0100 Subject: [PATCH 2/2] . --- .../metrics/afterCaptureMetric/init.js | 25 ++++ .../metrics/afterCaptureMetric/subject.js | 5 +- .../metrics/afterCaptureMetric/test.ts | 120 ++++++++---------- 3 files changed, 83 insertions(+), 67 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/init.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/init.js b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/init.js new file mode 100644 index 000000000000..5590fbb90547 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/init.js @@ -0,0 +1,25 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0.0', + environment: 'test', + integrations: integrations => { + return integrations.filter(integration => integration.name !== 'BrowserSession'); + }, + beforeSendMetric: metric => { + if (metric.name === 'test.counter') { + return { + ...metric, + attributes: { + ...metric.attributes, + modified: 'by-beforeSendMetric', + original: undefined, + }, + }; + } + return metric; + }, +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js index 1d1cccf0f1e7..e7b9940c7f6f 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/subject.js @@ -7,9 +7,8 @@ client.on('afterCaptureMetric', metric => { window.capturedMetrics.push(metric); }); -Sentry.metrics.count('test.counter', 1, { attributes: { endpoint: '/api/test' } }); +// Capture metrics - these should be processed by beforeSendMetric +Sentry.metrics.count('test.counter', 1, { attributes: { endpoint: '/api/test', original: 'value' } }); Sentry.metrics.gauge('test.gauge', 42, { unit: 'millisecond', attributes: { server: 'test-1' } }); -Sentry.setUser({ id: 'user-123', email: 'test@example.com', username: 'testuser' }); -Sentry.metrics.distribution('test.distribution', 200, { unit: 'second', attributes: { priority: 'high' } }); Sentry.flush(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts index a677fe364812..a89bdea81902 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/afterCaptureMetric/test.ts @@ -1,67 +1,59 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -sentryTest('should emit afterCaptureMetric event for captured metrics', async ({ getLocalTestUrl, page }) => { - const bundle = process.env.PW_BUNDLE || ''; - if (bundle.startsWith('bundle') || bundle.startsWith('loader')) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - await page.waitForFunction(() => { - return (window as any).capturedMetrics.length >= 3; - }); - - const capturedMetrics = await page.evaluate(() => { - return (window as any).capturedMetrics; - }); - - expect(capturedMetrics).toHaveLength(3); - - expect(capturedMetrics[0]).toMatchObject({ - name: 'test.counter', - type: 'counter', - value: 1, - attributes: { - endpoint: '/api/test', - 'sentry.release': '1.0.0', - 'sentry.environment': 'test', - 'sentry.sdk.name': 'sentry.javascript.browser', - }, - }); - - expect(capturedMetrics[1]).toMatchObject({ - name: 'test.gauge', - type: 'gauge', - unit: 'millisecond', - value: 42, - attributes: { - server: 'test-1', - 'sentry.release': '1.0.0', - 'sentry.environment': 'test', - 'sentry.sdk.name': 'sentry.javascript.browser', - }, - }); - - expect(capturedMetrics[2]).toMatchObject({ - name: 'test.distribution', - type: 'distribution', - unit: 'second', - value: 200, - attributes: { - priority: 'high', - 'user.id': 'user-123', - 'user.email': 'test@example.com', - 'user.name': 'testuser', - 'sentry.release': '1.0.0', - 'sentry.environment': 'test', - 'sentry.sdk.name': 'sentry.javascript.browser', - }, - }); - - expect(capturedMetrics[0].attributes['sentry.sdk.version']).toBeDefined(); - expect(capturedMetrics[1].attributes['sentry.sdk.version']).toBeDefined(); - expect(capturedMetrics[2].attributes['sentry.sdk.version']).toBeDefined(); -}); +sentryTest( + 'should emit afterCaptureMetric event with processed metric from beforeSendMetric', + async ({ getLocalTestUrl, page }) => { + const bundle = process.env.PW_BUNDLE || ''; + if (bundle.startsWith('bundle') || bundle.startsWith('loader')) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + await page.waitForFunction(() => { + return (window as any).capturedMetrics.length >= 2; + }); + + const capturedMetrics = await page.evaluate(() => { + return (window as any).capturedMetrics; + }); + + expect(capturedMetrics).toHaveLength(2); + + // Verify the counter metric was modified by beforeSendMetric + expect(capturedMetrics[0]).toMatchObject({ + name: 'test.counter', + type: 'counter', + value: 1, + attributes: { + endpoint: '/api/test', + modified: 'by-beforeSendMetric', + 'sentry.release': '1.0.0', + 'sentry.environment': 'test', + 'sentry.sdk.name': 'sentry.javascript.browser', + }, + }); + + // Verify the 'original' attribute was removed by beforeSendMetric + expect(capturedMetrics[0].attributes.original).toBeUndefined(); + + // Verify the gauge metric was not modified (no beforeSendMetric processing) + expect(capturedMetrics[1]).toMatchObject({ + name: 'test.gauge', + type: 'gauge', + unit: 'millisecond', + value: 42, + attributes: { + server: 'test-1', + 'sentry.release': '1.0.0', + 'sentry.environment': 'test', + 'sentry.sdk.name': 'sentry.javascript.browser', + }, + }); + + expect(capturedMetrics[0].attributes['sentry.sdk.version']).toBeDefined(); + expect(capturedMetrics[1].attributes['sentry.sdk.version']).toBeDefined(); + }, +);