diff --git a/.size-limit.js b/.size-limit.js index cad516a0a49a..043b1d8555bf 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -8,7 +8,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init'), gzip: true, - limit: '26 KB', + limit: '27 KB', }, { name: '@sentry/browser - with treeshaking flags', @@ -59,7 +59,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '83 KB', + limit: '84 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', @@ -96,7 +96,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'), gzip: true, - limit: '100 KB', + limit: '101 KB', }, { name: '@sentry/browser (incl. Feedback)', @@ -138,7 +138,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'metrics', 'logger'), gzip: true, - limit: '28 KB', + limit: '29 KB', }, // React SDK (ESM) { @@ -178,7 +178,7 @@ module.exports = [ path: 'packages/svelte/build/esm/index.js', import: createImport('init'), gzip: true, - limit: '26 KB', + limit: '27 KB', }, // Browser CDN bundles { @@ -197,7 +197,7 @@ module.exports = [ name: 'CDN Bundle (incl. Logs, Metrics)', path: createCDNPath('bundle.logs.metrics.min.js'), gzip: true, - limit: '30 KB', + limit: '31 KB', }, { name: 'CDN Bundle (incl. Tracing, Logs, Metrics)', @@ -283,14 +283,14 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '258.5 KB', + limit: '259 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed', path: createCDNPath('bundle.tracing.replay.feedback.min.js'), gzip: false, brotli: false, - limit: '268 KB', + limit: '269 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed', diff --git a/dev-packages/browser-integration-tests/suites/public-api/metrics/simple/test.ts b/dev-packages/browser-integration-tests/suites/public-api/metrics/simple/test.ts index 66f44878ac86..a50d6c8b2b78 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/metrics/simple/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/metrics/simple/test.ts @@ -27,6 +27,8 @@ sentryTest('should capture all metric types', async ({ getLocalTestUrl, page }) content_type: 'application/vnd.sentry.items.trace-metric+json', }, { + version: 2, + ingest_settings: { infer_ip: 'never', infer_user_agent: 'never' }, items: [ { timestamp: expect.any(Number), diff --git a/dev-packages/node-core-integration-tests/suites/light-mode/metrics/test.ts b/dev-packages/node-core-integration-tests/suites/light-mode/metrics/test.ts index c0c9d291de78..d2a67f8df890 100644 --- a/dev-packages/node-core-integration-tests/suites/light-mode/metrics/test.ts +++ b/dev-packages/node-core-integration-tests/suites/light-mode/metrics/test.ts @@ -11,6 +11,7 @@ describe('light mode metrics', () => { .unignore('trace_metric') .expect({ trace_metric: { + version: 2, items: [ { timestamp: expect.any(Number), diff --git a/dev-packages/node-core-integration-tests/suites/public-api/metrics/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/metrics/test.ts index 9494ce2a99ca..303eb22f3285 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/metrics/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/metrics/test.ts @@ -11,6 +11,7 @@ describe('metrics', () => { .unignore('trace_metric') .expect({ trace_metric: { + version: 2, items: [ { timestamp: expect.any(Number), diff --git a/dev-packages/node-integration-tests/suites/public-api/metrics/server-address-option/test.ts b/dev-packages/node-integration-tests/suites/public-api/metrics/server-address-option/test.ts index 825d94f41624..dfb3094f1bb9 100644 --- a/dev-packages/node-integration-tests/suites/public-api/metrics/server-address-option/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/metrics/server-address-option/test.ts @@ -10,6 +10,7 @@ describe('metrics server.address', () => { const runner = createRunner(__dirname, 'scenario.ts') .expect({ trace_metric: { + version: 2, items: [ { timestamp: expect.any(Number), diff --git a/dev-packages/node-integration-tests/suites/public-api/metrics/server-address/test.ts b/dev-packages/node-integration-tests/suites/public-api/metrics/server-address/test.ts index 048513da3c19..86eb295e0c5d 100644 --- a/dev-packages/node-integration-tests/suites/public-api/metrics/server-address/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/metrics/server-address/test.ts @@ -10,6 +10,7 @@ describe('metrics server.address', () => { const runner = createRunner(__dirname, 'scenario.ts') .expect({ trace_metric: { + version: 2, items: [ { timestamp: expect.any(Number), diff --git a/dev-packages/node-integration-tests/suites/public-api/metrics/test.ts b/dev-packages/node-integration-tests/suites/public-api/metrics/test.ts index ff67b73e9ad3..9b266552b052 100644 --- a/dev-packages/node-integration-tests/suites/public-api/metrics/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/metrics/test.ts @@ -10,6 +10,7 @@ describe('metrics', () => { const runner = createRunner(__dirname, 'scenario.ts') .expect({ trace_metric: { + version: 2, items: [ { timestamp: expect.any(Number), diff --git a/packages/core/src/metrics/envelope.ts b/packages/core/src/metrics/envelope.ts index 71ef0832667b..565b957c4f6d 100644 --- a/packages/core/src/metrics/envelope.ts +++ b/packages/core/src/metrics/envelope.ts @@ -4,14 +4,21 @@ import type { SerializedMetric } from '../types-hoist/metric'; import type { SdkMetadata } from '../types-hoist/sdkmetadata'; import { dsnToString } from '../utils/dsn'; import { createEnvelope } from '../utils/envelope'; +import { isBrowser } from '../utils/isBrowser'; /** * Creates a metric container envelope item for a list of metrics. * * @param items - The metrics to include in the envelope. + * @param inferUserData - If true, tells Relay to infer the end-user IP and User-Agent from the incoming request. + * Only emitted as `ingest_settings` in browser environments. * @returns The created metric container envelope item. */ -export function createMetricContainerEnvelopeItem(items: Array): MetricContainerItem { +export function createMetricContainerEnvelopeItem( + items: Array, + inferUserData?: boolean, +): MetricContainerItem { + const inferSetting = inferUserData ? 'auto' : 'never'; return [ { type: 'trace_metric', @@ -19,6 +26,10 @@ export function createMetricContainerEnvelopeItem(items: Array content_type: 'application/vnd.sentry.items.trace-metric+json', } as MetricContainerItem[0], { + version: 2, + ...(isBrowser() && { + ingest_settings: { infer_ip: inferSetting, infer_user_agent: inferSetting }, + }), items, }, ]; @@ -33,6 +44,7 @@ export function createMetricContainerEnvelopeItem(items: Array * @param metadata - The metadata to include in the envelope. * @param tunnel - The tunnel to include in the envelope. * @param dsn - The DSN to include in the envelope. + * @param inferUserData - If true, tells Relay to infer the end-user IP and User-Agent from the incoming request. * @returns The created envelope. */ export function createMetricEnvelope( @@ -40,6 +52,7 @@ export function createMetricEnvelope( metadata?: SdkMetadata, tunnel?: string, dsn?: DsnComponents, + inferUserData?: boolean, ): MetricEnvelope { const headers: MetricEnvelope[0] = {}; @@ -54,5 +67,5 @@ export function createMetricEnvelope( headers.dsn = dsnToString(dsn); } - return createEnvelope(headers, [createMetricContainerEnvelopeItem(metrics)]); + return createEnvelope(headers, [createMetricContainerEnvelopeItem(metrics, inferUserData)]); } diff --git a/packages/core/src/metrics/internal.ts b/packages/core/src/metrics/internal.ts index 0545414654ef..26cc11fc0422 100644 --- a/packages/core/src/metrics/internal.ts +++ b/packages/core/src/metrics/internal.ts @@ -225,7 +225,13 @@ export function _INTERNAL_flushMetricsBuffer(client: Client, maybeMetricBuffer?: } const clientOptions = client.getOptions(); - const envelope = createMetricEnvelope(metricBuffer, clientOptions._metadata, clientOptions.tunnel, client.getDsn()); + const envelope = createMetricEnvelope( + metricBuffer, + clientOptions._metadata, + clientOptions.tunnel, + client.getDsn(), + clientOptions.sendDefaultPii, + ); // Clear the metric buffer after envelopes have been constructed. _getBufferMap().set(client, []); diff --git a/packages/core/src/types-hoist/metric.ts b/packages/core/src/types-hoist/metric.ts index 976fc9fe863f..1b6380cbc471 100644 --- a/packages/core/src/types-hoist/metric.ts +++ b/packages/core/src/types-hoist/metric.ts @@ -77,5 +77,10 @@ export interface SerializedMetric { } export type SerializedMetricContainer = { + version?: number; + ingest_settings?: { + infer_ip?: 'auto' | 'never'; + infer_user_agent?: 'auto' | 'never'; + }; items: Array; }; diff --git a/packages/core/test/lib/metrics/envelope.test.ts b/packages/core/test/lib/metrics/envelope.test.ts index 87132e4bcaa0..25bf5e61923b 100644 --- a/packages/core/test/lib/metrics/envelope.test.ts +++ b/packages/core/test/lib/metrics/envelope.test.ts @@ -5,6 +5,7 @@ import type { SerializedMetric } from '../../../src/types-hoist/metric'; import type { SdkMetadata } from '../../../src/types-hoist/sdkmetadata'; import * as utilsDsn from '../../../src/utils/dsn'; import * as utilsEnvelope from '../../../src/utils/envelope'; +import { isBrowser } from '../../../src/utils/isBrowser'; vi.mock('../../../src/utils/dsn', () => ({ dsnToString: vi.fn(dsn => `https://${dsn.publicKey}@${dsn.host}/`), @@ -12,9 +13,16 @@ vi.mock('../../../src/utils/dsn', () => ({ vi.mock('../../../src/utils/envelope', () => ({ createEnvelope: vi.fn((_headers, items) => [_headers, items]), })); +vi.mock('../../../src/utils/isBrowser', () => ({ + isBrowser: vi.fn(() => false), +})); + +afterEach(() => { + vi.mocked(isBrowser).mockReturnValue(false); +}); describe('createMetricContainerEnvelopeItem', () => { - it('creates an envelope item with correct structure', () => { + it('emits version: 2 without ingest_settings when not in browser', () => { const mockMetric: SerializedMetric = { timestamp: 1713859200, trace_id: '3d9355f71e9c444b81161599adac6e29', @@ -26,15 +34,63 @@ describe('createMetricContainerEnvelopeItem', () => { attributes: {}, }; - const result = createMetricContainerEnvelopeItem([mockMetric, mockMetric]); + const result = createMetricContainerEnvelopeItem([mockMetric], true); - expect(result).toHaveLength(2); expect(result[0]).toEqual({ type: 'trace_metric', - item_count: 2, + item_count: 1, content_type: 'application/vnd.sentry.items.trace-metric+json', }); - expect(result[1]).toEqual({ items: [mockMetric, mockMetric] }); + expect(result[1]).toEqual({ + version: 2, + items: [mockMetric], + }); + }); + + it("includes ingest_settings with 'auto' values when in browser and inferUserData is true", () => { + vi.mocked(isBrowser).mockReturnValue(true); + + const mockMetric: SerializedMetric = { + timestamp: 1713859200, + trace_id: '3d9355f71e9c444b81161599adac6e29', + span_id: '8b5f5e5e5e5e5e5e', + name: 'test.metric', + type: 'counter', + value: 1, + unit: 'count', + attributes: {}, + }; + + const result = createMetricContainerEnvelopeItem([mockMetric], true); + + expect(result[1]).toEqual({ + version: 2, + ingest_settings: { infer_ip: 'auto', infer_user_agent: 'auto' }, + items: [mockMetric], + }); + }); + + it("includes ingest_settings with 'never' values when in browser and inferUserData is false", () => { + vi.mocked(isBrowser).mockReturnValue(true); + + const mockMetric: SerializedMetric = { + timestamp: 1713859200, + trace_id: '3d9355f71e9c444b81161599adac6e29', + span_id: '8b5f5e5e5e5e5e5e', + name: 'test.metric', + type: 'counter', + value: 1, + unit: 'count', + attributes: {}, + }; + + const result = createMetricContainerEnvelopeItem([mockMetric], false); + + expect(result[1]).toEqual({ + version: 2, + ingest_settings: { infer_ip: 'never', infer_user_agent: 'never' }, + items: [mockMetric], + }); }); }); @@ -165,7 +221,7 @@ describe('createMetricEnvelope', () => { expect.arrayContaining([ expect.arrayContaining([ { type: 'trace_metric', item_count: 2, content_type: 'application/vnd.sentry.items.trace-metric+json' }, - { items: mockMetrics }, + { version: 2, items: mockMetrics }, ]), ]), );