Skip to content

Commit 5a084de

Browse files
authored
fix(sampling): Remove stray sampling data tags (#3197)
1 parent d118b85 commit 5a084de

File tree

6 files changed

+130
-95
lines changed

6 files changed

+130
-95
lines changed

packages/core/src/request.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,18 @@ export function sessionToSentryRequest(session: Session, api: API): SentryReques
5151

5252
/** Creates a SentryRequest from an event. */
5353
export function eventToSentryRequest(event: Event, api: API): SentryRequest {
54-
// since JS has no Object.prototype.pop()
55-
const { __sentry_samplingMethod: samplingMethod, __sentry_sampleRate: sampleRate, ...otherTags } = event.tags || {};
56-
event.tags = otherTags;
57-
5854
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
5955
const eventType = event.type || 'event';
6056
const useEnvelope = eventType === 'transaction';
6157

58+
const { transactionSampling, ...metadata } = event.debug_meta || {};
59+
const { method: samplingMethod, rate: sampleRate } = transactionSampling || {};
60+
if (Object.keys(metadata).length === 0) {
61+
delete event.debug_meta;
62+
} else {
63+
event.debug_meta = metadata;
64+
}
65+
6266
const req: SentryRequest = {
6367
body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
6468
type: eventType,

packages/core/test/lib/request.test.ts

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
import { Event, TransactionSamplingMethod } from '@sentry/types';
1+
import { DebugMeta, Event, SentryRequest, TransactionSamplingMethod } from '@sentry/types';
22

33
import { API } from '../../src/api';
44
import { eventToSentryRequest } from '../../src/request';
55

66
describe('eventToSentryRequest', () => {
7-
let api: API;
7+
function parseEnvelopeRequest(request: SentryRequest): any {
8+
const [envelopeHeaderString, itemHeaderString, eventString] = request.body.split('\n');
9+
10+
return {
11+
envelopeHeader: JSON.parse(envelopeHeaderString),
12+
itemHeader: JSON.parse(itemHeaderString),
13+
event: JSON.parse(eventString),
14+
};
15+
}
16+
17+
const api = new API('https://[email protected]/12312012', {
18+
sdk: {
19+
integrations: ['AWSLambda'],
20+
name: 'sentry.javascript.browser',
21+
version: `12.31.12`,
22+
packages: [{ name: 'npm:@sentry/browser', version: `12.31.12` }],
23+
},
24+
});
825
let event: Event;
926

1027
beforeEach(() => {
11-
api = new API('https://[email protected]/12312012', {
12-
sdk: {
13-
integrations: ['AWSLambda'],
14-
name: 'sentry.javascript.browser',
15-
version: `12.31.12`,
16-
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
17-
},
18-
});
1928
event = {
2029
contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } },
2130
environment: 'dogpark',
@@ -28,68 +37,63 @@ describe('eventToSentryRequest', () => {
2837
};
2938
});
3039

31-
[
32-
{ method: TransactionSamplingMethod.Rate, rate: '0.1121', dog: 'Charlie' },
33-
{ method: TransactionSamplingMethod.Sampler, rate: '0.1231', dog: 'Maisey' },
34-
{ method: TransactionSamplingMethod.Inheritance, dog: 'Cory' },
35-
{ method: TransactionSamplingMethod.Explicit, dog: 'Bodhi' },
36-
37-
// this shouldn't ever happen (at least the method should always be included in tags), but good to know that things
38-
// won't blow up if it does
39-
{ dog: 'Lucy' },
40-
].forEach(({ method, rate, dog }) => {
41-
it(`adds transaction sampling information to item header - ${method}, ${rate}, ${dog}`, () => {
42-
// TODO kmclb - once tag types are loosened, don't need to cast to string here
43-
event.tags = { __sentry_samplingMethod: String(method), __sentry_sampleRate: String(rate), dog };
44-
45-
const result = eventToSentryRequest(event, api);
46-
47-
const [envelopeHeaderString, itemHeaderString, eventString] = result.body.split('\n');
48-
49-
const envelope = {
50-
envelopeHeader: JSON.parse(envelopeHeaderString),
51-
itemHeader: JSON.parse(itemHeaderString),
52-
event: JSON.parse(eventString),
53-
};
54-
55-
// the right stuff is added to the item header
56-
expect(envelope.itemHeader).toEqual({
57-
type: 'transaction',
58-
// TODO kmclb - once tag types are loosened, don't need to cast to string here
59-
sample_rates: [{ id: String(method), rate: String(rate) }],
60-
});
61-
62-
// show that it pops the right tags and leaves the rest alone
63-
expect('__sentry_samplingMethod' in envelope.event.tags).toBe(false);
64-
expect('__sentry_sampleRate' in envelope.event.tags).toBe(false);
65-
expect('dog' in envelope.event.tags).toBe(true);
66-
});
40+
it(`adds transaction sampling information to item header`, () => {
41+
event.debug_meta = { transactionSampling: { method: TransactionSamplingMethod.Rate, rate: 0.1121 } };
42+
43+
const result = eventToSentryRequest(event, api);
44+
const envelope = parseEnvelopeRequest(result);
45+
46+
expect(envelope.itemHeader).toEqual(
47+
expect.objectContaining({
48+
sample_rates: [{ id: TransactionSamplingMethod.Rate, rate: 0.1121 }],
49+
}),
50+
);
6751
});
6852

69-
it('adds sdk info to envelope header', () => {
53+
it('removes transaction sampling information (and only that) from debug_meta', () => {
54+
event.debug_meta = {
55+
transactionSampling: { method: TransactionSamplingMethod.Sampler, rate: 0.1121 },
56+
dog: 'Charlie',
57+
} as DebugMeta;
58+
7059
const result = eventToSentryRequest(event, api);
60+
const envelope = parseEnvelopeRequest(result);
7161

72-
const envelopeHeaderString = result.body.split('\n')[0];
73-
const parsedHeader = JSON.parse(envelopeHeaderString);
62+
expect('transactionSampling' in envelope.event.debug_meta).toBe(false);
63+
expect('dog' in envelope.event.debug_meta).toBe(true);
64+
});
7465

75-
expect(parsedHeader).toEqual(
66+
it('removes debug_meta entirely if it ends up empty', () => {
67+
event.debug_meta = {
68+
transactionSampling: { method: TransactionSamplingMethod.Rate, rate: 0.1121 },
69+
} as DebugMeta;
70+
71+
const result = eventToSentryRequest(event, api);
72+
const envelope = parseEnvelopeRequest(result);
73+
74+
expect('debug_meta' in envelope.event).toBe(false);
75+
});
76+
77+
it('adds sdk info to envelope header', () => {
78+
const result = eventToSentryRequest(event, api);
79+
const envelope = parseEnvelopeRequest(result);
80+
81+
expect(envelope.envelopeHeader).toEqual(
7682
expect.objectContaining({ sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } }),
7783
);
7884
});
7985

8086
it('adds sdk info to event body', () => {
8187
const result = eventToSentryRequest(event, api);
88+
const envelope = parseEnvelopeRequest(result);
8289

83-
const eventString = result.body.split('\n')[2];
84-
const parsedEvent = JSON.parse(eventString);
85-
86-
expect(parsedEvent).toEqual(
90+
expect(envelope.event).toEqual(
8791
expect.objectContaining({
8892
sdk: {
8993
integrations: ['AWSLambda'],
9094
name: 'sentry.javascript.browser',
9195
version: `12.31.12`,
92-
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
96+
packages: [{ name: 'npm:@sentry/browser', version: `12.31.12` }],
9397
},
9498
}),
9599
);
@@ -99,22 +103,21 @@ describe('eventToSentryRequest', () => {
99103
event.sdk = {
100104
integrations: ['Clojure'],
101105
name: 'foo',
102-
packages: [{ name: 'npm:@sentry/clj', version: `6.6.6` }],
106+
packages: [{ name: 'npm:@sentry/clj', version: `12.31.12` }],
103107
version: '1337',
104108
};
105-
const result = eventToSentryRequest(event, api);
106109

107-
const eventString = result.body.split('\n')[2];
108-
const parsedEvent = JSON.parse(eventString);
110+
const result = eventToSentryRequest(event, api);
111+
const envelope = parseEnvelopeRequest(result);
109112

110-
expect(parsedEvent).toEqual(
113+
expect(envelope.event).toEqual(
111114
expect.objectContaining({
112115
sdk: {
113116
integrations: ['Clojure', 'AWSLambda'],
114117
name: 'foo',
115118
packages: [
116-
{ name: 'npm:@sentry/clj', version: `6.6.6` },
117-
{ name: 'npm:@sentry/browser', version: `6.6.6` },
119+
{ name: 'npm:@sentry/clj', version: `12.31.12` },
120+
{ name: 'npm:@sentry/browser', version: `12.31.12` },
118121
],
119122
version: '1337',
120123
},

packages/tracing/src/hubextensions.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ function sample<T extends Transaction>(hub: Hub, transaction: T, samplingContext
4545

4646
// if the user has forced a sampling decision by passing a `sampled` value in their transaction context, go with that
4747
if (transaction.sampled !== undefined) {
48-
transaction.tags = { ...transaction.tags, __sentry_samplingMethod: TransactionSamplingMethod.Explicit };
48+
transaction.setMetadata({
49+
transactionSampling: { method: TransactionSamplingMethod.Explicit },
50+
});
4951
return transaction;
5052
}
5153

@@ -54,25 +56,27 @@ function sample<T extends Transaction>(hub: Hub, transaction: T, samplingContext
5456
let sampleRate;
5557
if (typeof options.tracesSampler === 'function') {
5658
sampleRate = options.tracesSampler(samplingContext);
57-
// cast the rate to a number first in case it's a boolean
58-
transaction.tags = {
59-
...transaction.tags,
60-
__sentry_samplingMethod: TransactionSamplingMethod.Sampler,
61-
// TODO kmclb - once tag types are loosened, don't need to cast to string here
62-
__sentry_sampleRate: String(Number(sampleRate)),
63-
};
59+
transaction.setMetadata({
60+
transactionSampling: {
61+
method: TransactionSamplingMethod.Sampler,
62+
// cast to number in case it's a boolean
63+
rate: Number(sampleRate),
64+
},
65+
});
6466
} else if (samplingContext.parentSampled !== undefined) {
6567
sampleRate = samplingContext.parentSampled;
66-
transaction.tags = { ...transaction.tags, __sentry_samplingMethod: TransactionSamplingMethod.Inheritance };
68+
transaction.setMetadata({
69+
transactionSampling: { method: TransactionSamplingMethod.Inheritance },
70+
});
6771
} else {
6872
sampleRate = options.tracesSampleRate;
69-
// cast the rate to a number first in case it's a boolean
70-
transaction.tags = {
71-
...transaction.tags,
72-
__sentry_samplingMethod: TransactionSamplingMethod.Rate,
73-
// TODO kmclb - once tag types are loosened, don't need to cast to string here
74-
__sentry_sampleRate: String(Number(sampleRate)),
75-
};
73+
transaction.setMetadata({
74+
transactionSampling: {
75+
method: TransactionSamplingMethod.Rate,
76+
// cast to number in case it's a boolean
77+
rate: Number(sampleRate),
78+
},
79+
});
7680
}
7781

7882
// Since this is coming from the user (or from a function provided by the user), who knows what we might get. (The

packages/tracing/src/transaction.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@ import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils';
44

55
import { Span as SpanClass, SpanRecorder } from './span';
66

7+
interface TransactionMetadata {
8+
transactionSampling?: { [key: string]: string | number };
9+
}
10+
711
/** JSDoc */
812
export class Transaction extends SpanClass implements TransactionInterface {
913
public name: string;
14+
15+
private _metadata: TransactionMetadata = {};
16+
1017
private _measurements: Measurements = {};
1118

1219
/**
@@ -64,6 +71,14 @@ export class Transaction extends SpanClass implements TransactionInterface {
6471
this._measurements = { ...measurements };
6572
}
6673

74+
/**
75+
* Set metadata for this transaction.
76+
* @hidden
77+
*/
78+
public setMetadata(newMetadata: TransactionMetadata): void {
79+
this._metadata = { ...this._metadata, ...newMetadata };
80+
}
81+
6782
/**
6883
* @inheritDoc
6984
*/
@@ -108,6 +123,7 @@ export class Transaction extends SpanClass implements TransactionInterface {
108123
timestamp: this.endTimestamp,
109124
transaction: this.name,
110125
type: 'transaction',
126+
debug_meta: this._metadata,
111127
};
112128

113129
const hasMeasurements = Object.keys(this._measurements).length > 0;

packages/tracing/test/hub.test.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22
import { BrowserClient } from '@sentry/browser';
33
import { Hub } from '@sentry/hub';
44
import * as hubModule from '@sentry/hub';
5+
import { TransactionSamplingMethod } from '@sentry/types';
56
import * as utilsModule from '@sentry/utils'; // for mocking
67
import { logger } from '@sentry/utils';
78

89
import { BrowserTracing } from '../src/browser/browsertracing';
910
import { addExtensionMethods } from '../src/hubextensions';
11+
import { Transaction } from '../src/transaction';
1012
import { extractTraceparentData, TRACEPARENT_REGEXP } from '../src/utils';
1113
import { addDOMPropertiesToGlobal, getSymbolObjectKeyByName } from './testutils';
1214

1315
addExtensionMethods();
1416

1517
const mathRandom = jest.spyOn(Math, 'random');
18+
jest.spyOn(Transaction.prototype, 'setMetadata');
1619

1720
// we have to add things into the real global object (rather than mocking the return value of getGlobalObject) because
1821
// there are modules which call getGlobalObject as they load, which is seemingly too early for jest to intervene
@@ -26,7 +29,7 @@ describe('Hub', () => {
2629
});
2730

2831
afterEach(() => {
29-
jest.restoreAllMocks();
32+
jest.clearAllMocks();
3033
jest.useRealTimers();
3134
});
3235

@@ -184,35 +187,39 @@ describe('Hub', () => {
184187
it('should record sampling method when sampling decision is explicitly set', () => {
185188
const tracesSampler = jest.fn().mockReturnValue(0.1121);
186189
const hub = new Hub(new BrowserClient({ tracesSampler }));
187-
const transaction = hub.startTransaction({ name: 'dogpark', sampled: true });
190+
hub.startTransaction({ name: 'dogpark', sampled: true });
188191

189-
expect(transaction.tags).toEqual(expect.objectContaining({ __sentry_samplingMethod: 'explicitly_set' }));
192+
expect(Transaction.prototype.setMetadata).toHaveBeenCalledWith({
193+
transactionSampling: { method: TransactionSamplingMethod.Explicit },
194+
});
190195
});
191196

192197
it('should record sampling method and rate when sampling decision comes from tracesSampler', () => {
193198
const tracesSampler = jest.fn().mockReturnValue(0.1121);
194199
const hub = new Hub(new BrowserClient({ tracesSampler }));
195-
const transaction = hub.startTransaction({ name: 'dogpark' });
200+
hub.startTransaction({ name: 'dogpark' });
196201

197-
expect(transaction.tags).toEqual(
198-
expect.objectContaining({ __sentry_samplingMethod: 'client_sampler', __sentry_sampleRate: '0.1121' }),
199-
);
202+
expect(Transaction.prototype.setMetadata).toHaveBeenCalledWith({
203+
transactionSampling: { method: TransactionSamplingMethod.Sampler, rate: 0.1121 },
204+
});
200205
});
201206

202207
it('should record sampling method when sampling decision is inherited', () => {
203208
const hub = new Hub(new BrowserClient({ tracesSampleRate: 0.1121 }));
204-
const transaction = hub.startTransaction({ name: 'dogpark', parentSampled: true });
209+
hub.startTransaction({ name: 'dogpark', parentSampled: true });
205210

206-
expect(transaction.tags).toEqual(expect.objectContaining({ __sentry_samplingMethod: 'inheritance' }));
211+
expect(Transaction.prototype.setMetadata).toHaveBeenCalledWith({
212+
transactionSampling: { method: TransactionSamplingMethod.Inheritance },
213+
});
207214
});
208215

209216
it('should record sampling method and rate when sampling decision comes from traceSampleRate', () => {
210217
const hub = new Hub(new BrowserClient({ tracesSampleRate: 0.1121 }));
211-
const transaction = hub.startTransaction({ name: 'dogpark' });
218+
hub.startTransaction({ name: 'dogpark' });
212219

213-
expect(transaction.tags).toEqual(
214-
expect.objectContaining({ __sentry_samplingMethod: 'client_rate', __sentry_sampleRate: '0.1121' }),
215-
);
220+
expect(Transaction.prototype.setMetadata).toHaveBeenCalledWith({
221+
transactionSampling: { method: TransactionSamplingMethod.Rate, rate: 0.1121 },
222+
});
216223
});
217224
});
218225

packages/types/src/debugMeta.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
**/
44
export interface DebugMeta {
55
images?: Array<DebugImage>;
6+
transactionSampling?: { rate?: number; method?: string };
67
}
78

89
/**

0 commit comments

Comments
 (0)