Skip to content

Commit 80cb35a

Browse files
authored
feat(browser): Add ingest_settings to v2 metrics envelope payload (#20454)
Adds `version: 2` and `ingest_settings` to the metrics envelope payload so Relay can infer the end-user IP address and User-Agent from the incoming request ([link to spec](https://develop.sentry.dev/sdk/telemetry/logs/#wire-format)). This is only emitted by browser SDKs. Both settings are gated behind `sendDefaultPii` (modeled after how `event.sdk.settings.infer_ip` works today). Closes #20276
1 parent ff23d65 commit 80cb35a

10 files changed

Lines changed: 96 additions & 9 deletions

File tree

dev-packages/browser-integration-tests/suites/public-api/metrics/simple/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ sentryTest('should capture all metric types', async ({ getLocalTestUrl, page })
2727
content_type: 'application/vnd.sentry.items.trace-metric+json',
2828
},
2929
{
30+
version: 2,
31+
ingest_settings: { infer_ip: 'never', infer_user_agent: 'never' },
3032
items: [
3133
{
3234
timestamp: expect.any(Number),

dev-packages/node-core-integration-tests/suites/light-mode/metrics/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('light mode metrics', () => {
1111
.unignore('trace_metric')
1212
.expect({
1313
trace_metric: {
14+
version: 2,
1415
items: [
1516
{
1617
timestamp: expect.any(Number),

dev-packages/node-core-integration-tests/suites/public-api/metrics/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('metrics', () => {
1111
.unignore('trace_metric')
1212
.expect({
1313
trace_metric: {
14+
version: 2,
1415
items: [
1516
{
1617
timestamp: expect.any(Number),

dev-packages/node-integration-tests/suites/public-api/metrics/server-address-option/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ describe('metrics server.address', () => {
1010
const runner = createRunner(__dirname, 'scenario.ts')
1111
.expect({
1212
trace_metric: {
13+
version: 2,
1314
items: [
1415
{
1516
timestamp: expect.any(Number),

dev-packages/node-integration-tests/suites/public-api/metrics/server-address/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ describe('metrics server.address', () => {
1010
const runner = createRunner(__dirname, 'scenario.ts')
1111
.expect({
1212
trace_metric: {
13+
version: 2,
1314
items: [
1415
{
1516
timestamp: expect.any(Number),

dev-packages/node-integration-tests/suites/public-api/metrics/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ describe('metrics', () => {
1010
const runner = createRunner(__dirname, 'scenario.ts')
1111
.expect({
1212
trace_metric: {
13+
version: 2,
1314
items: [
1415
{
1516
timestamp: expect.any(Number),

packages/core/src/metrics/envelope.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,32 @@ import type { SerializedMetric } from '../types-hoist/metric';
44
import type { SdkMetadata } from '../types-hoist/sdkmetadata';
55
import { dsnToString } from '../utils/dsn';
66
import { createEnvelope } from '../utils/envelope';
7+
import { isBrowser } from '../utils/isBrowser';
78

89
/**
910
* Creates a metric container envelope item for a list of metrics.
1011
*
1112
* @param items - The metrics to include in the envelope.
13+
* @param inferUserData - If true, tells Relay to infer the end-user IP and User-Agent from the incoming request.
14+
* Only emitted as `ingest_settings` in browser environments.
1215
* @returns The created metric container envelope item.
1316
*/
14-
export function createMetricContainerEnvelopeItem(items: Array<SerializedMetric>): MetricContainerItem {
17+
export function createMetricContainerEnvelopeItem(
18+
items: Array<SerializedMetric>,
19+
inferUserData?: boolean,
20+
): MetricContainerItem {
21+
const inferSetting = inferUserData ? 'auto' : 'never';
1522
return [
1623
{
1724
type: 'trace_metric',
1825
item_count: items.length,
1926
content_type: 'application/vnd.sentry.items.trace-metric+json',
2027
} as MetricContainerItem[0],
2128
{
29+
version: 2,
30+
...(isBrowser() && {
31+
ingest_settings: { infer_ip: inferSetting, infer_user_agent: inferSetting },
32+
}),
2233
items,
2334
},
2435
];
@@ -33,13 +44,15 @@ export function createMetricContainerEnvelopeItem(items: Array<SerializedMetric>
3344
* @param metadata - The metadata to include in the envelope.
3445
* @param tunnel - The tunnel to include in the envelope.
3546
* @param dsn - The DSN to include in the envelope.
47+
* @param inferUserData - If true, tells Relay to infer the end-user IP and User-Agent from the incoming request.
3648
* @returns The created envelope.
3749
*/
3850
export function createMetricEnvelope(
3951
metrics: Array<SerializedMetric>,
4052
metadata?: SdkMetadata,
4153
tunnel?: string,
4254
dsn?: DsnComponents,
55+
inferUserData?: boolean,
4356
): MetricEnvelope {
4457
const headers: MetricEnvelope[0] = {};
4558

@@ -54,5 +67,5 @@ export function createMetricEnvelope(
5467
headers.dsn = dsnToString(dsn);
5568
}
5669

57-
return createEnvelope<MetricEnvelope>(headers, [createMetricContainerEnvelopeItem(metrics)]);
70+
return createEnvelope<MetricEnvelope>(headers, [createMetricContainerEnvelopeItem(metrics, inferUserData)]);
5871
}

packages/core/src/metrics/internal.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,13 @@ export function _INTERNAL_flushMetricsBuffer(client: Client, maybeMetricBuffer?:
225225
}
226226

227227
const clientOptions = client.getOptions();
228-
const envelope = createMetricEnvelope(metricBuffer, clientOptions._metadata, clientOptions.tunnel, client.getDsn());
228+
const envelope = createMetricEnvelope(
229+
metricBuffer,
230+
clientOptions._metadata,
231+
clientOptions.tunnel,
232+
client.getDsn(),
233+
clientOptions.sendDefaultPii,
234+
);
229235

230236
// Clear the metric buffer after envelopes have been constructed.
231237
_getBufferMap().set(client, []);

packages/core/src/types-hoist/metric.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,10 @@ export interface SerializedMetric {
7777
}
7878

7979
export type SerializedMetricContainer = {
80+
version?: number;
81+
ingest_settings?: {
82+
infer_ip?: 'auto' | 'never';
83+
infer_user_agent?: 'auto' | 'never';
84+
};
8085
items: Array<SerializedMetric>;
8186
};

packages/core/test/lib/metrics/envelope.test.ts

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ import type { SerializedMetric } from '../../../src/types-hoist/metric';
55
import type { SdkMetadata } from '../../../src/types-hoist/sdkmetadata';
66
import * as utilsDsn from '../../../src/utils/dsn';
77
import * as utilsEnvelope from '../../../src/utils/envelope';
8+
import { isBrowser } from '../../../src/utils/isBrowser';
89

910
vi.mock('../../../src/utils/dsn', () => ({
1011
dsnToString: vi.fn(dsn => `https://${dsn.publicKey}@${dsn.host}/`),
1112
}));
1213
vi.mock('../../../src/utils/envelope', () => ({
1314
createEnvelope: vi.fn((_headers, items) => [_headers, items]),
1415
}));
16+
vi.mock('../../../src/utils/isBrowser', () => ({
17+
isBrowser: vi.fn(() => false),
18+
}));
19+
20+
afterEach(() => {
21+
vi.mocked(isBrowser).mockReturnValue(false);
22+
});
1523

1624
describe('createMetricContainerEnvelopeItem', () => {
17-
it('creates an envelope item with correct structure', () => {
25+
it('emits version: 2 without ingest_settings when not in browser', () => {
1826
const mockMetric: SerializedMetric = {
1927
timestamp: 1713859200,
2028
trace_id: '3d9355f71e9c444b81161599adac6e29',
@@ -26,15 +34,63 @@ describe('createMetricContainerEnvelopeItem', () => {
2634
attributes: {},
2735
};
2836

29-
const result = createMetricContainerEnvelopeItem([mockMetric, mockMetric]);
37+
const result = createMetricContainerEnvelopeItem([mockMetric], true);
3038

31-
expect(result).toHaveLength(2);
3239
expect(result[0]).toEqual({
3340
type: 'trace_metric',
34-
item_count: 2,
41+
item_count: 1,
3542
content_type: 'application/vnd.sentry.items.trace-metric+json',
3643
});
37-
expect(result[1]).toEqual({ items: [mockMetric, mockMetric] });
44+
expect(result[1]).toEqual({
45+
version: 2,
46+
items: [mockMetric],
47+
});
48+
});
49+
50+
it("includes ingest_settings with 'auto' values when in browser and inferUserData is true", () => {
51+
vi.mocked(isBrowser).mockReturnValue(true);
52+
53+
const mockMetric: SerializedMetric = {
54+
timestamp: 1713859200,
55+
trace_id: '3d9355f71e9c444b81161599adac6e29',
56+
span_id: '8b5f5e5e5e5e5e5e',
57+
name: 'test.metric',
58+
type: 'counter',
59+
value: 1,
60+
unit: 'count',
61+
attributes: {},
62+
};
63+
64+
const result = createMetricContainerEnvelopeItem([mockMetric], true);
65+
66+
expect(result[1]).toEqual({
67+
version: 2,
68+
ingest_settings: { infer_ip: 'auto', infer_user_agent: 'auto' },
69+
items: [mockMetric],
70+
});
71+
});
72+
73+
it("includes ingest_settings with 'never' values when in browser and inferUserData is false", () => {
74+
vi.mocked(isBrowser).mockReturnValue(true);
75+
76+
const mockMetric: SerializedMetric = {
77+
timestamp: 1713859200,
78+
trace_id: '3d9355f71e9c444b81161599adac6e29',
79+
span_id: '8b5f5e5e5e5e5e5e',
80+
name: 'test.metric',
81+
type: 'counter',
82+
value: 1,
83+
unit: 'count',
84+
attributes: {},
85+
};
86+
87+
const result = createMetricContainerEnvelopeItem([mockMetric], false);
88+
89+
expect(result[1]).toEqual({
90+
version: 2,
91+
ingest_settings: { infer_ip: 'never', infer_user_agent: 'never' },
92+
items: [mockMetric],
93+
});
3894
});
3995
});
4096

@@ -165,7 +221,7 @@ describe('createMetricEnvelope', () => {
165221
expect.arrayContaining([
166222
expect.arrayContaining([
167223
{ type: 'trace_metric', item_count: 2, content_type: 'application/vnd.sentry.items.trace-metric+json' },
168-
{ items: mockMetrics },
224+
{ version: 2, items: mockMetrics },
169225
]),
170226
]),
171227
);

0 commit comments

Comments
 (0)