Skip to content

Commit a550b9d

Browse files
committed
feat(tracing): Add additional dynamic sampling context items to baggage and envelope headers
1 parent 06d573f commit a550b9d

File tree

12 files changed

+84
-56
lines changed

12 files changed

+84
-56
lines changed

packages/core/src/envelope.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
BaggageObj,
32
DsnComponents,
43
Event,
54
EventEnvelope,
@@ -13,7 +12,7 @@ import {
1312
SessionEnvelope,
1413
SessionItem,
1514
} from '@sentry/types';
16-
import { createEnvelope, dropUndefinedKeys, dsnToString } from '@sentry/utils';
15+
import { createEnvelope, dropUndefinedKeys, dsnToString, getSentryBaggageItems } from '@sentry/utils';
1716

1817
/** Extract sdk info from from the API metadata */
1918
function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined {
@@ -77,14 +76,14 @@ export function createEventEnvelope(
7776

7877
enhanceEventWithSdkInfo(event, metadata && metadata.sdk);
7978

79+
const envelopeHeaders = createEventEnvelopeHeaders(event, sdkInfo, tunnel, dsn);
80+
8081
// Prevent this data (which, if it exists, was used in earlier steps in the processing pipeline) from being sent to
8182
// sentry. (Note: Our use of this property comes and goes with whatever we might be debugging, whatever hacks we may
8283
// have temporarily added, etc. Even if we don't happen to be using it at some point in the future, let's not get rid
8384
// of this `delete`, lest we miss putting it back in the next time the property is in use.)
8485
delete event.sdkProcessingMetadata;
8586

86-
const envelopeHeaders = createEventEnvelopeHeaders(event, sdkInfo, tunnel, dsn);
87-
8887
const eventItem: EventItem = [
8988
{
9089
type: eventType,
@@ -101,8 +100,9 @@ function createEventEnvelopeHeaders(
101100
tunnel: string | undefined,
102101
dsn: DsnComponents,
103102
): EventEnvelopeHeaders {
104-
const baggage = event.contexts && (event.contexts.baggage as BaggageObj);
105-
const { environment, release, transaction, userid, usersegment } = baggage || {};
103+
const baggage = event.sdkProcessingMetadata && event.sdkProcessingMetadata.baggage;
104+
const { environment, release, transaction, userid, usersegment, samplerate } =
105+
(baggage && getSentryBaggageItems(baggage)) || {};
106106

107107
return {
108108
event_id: event.event_id as string,
@@ -119,9 +119,10 @@ function createEventEnvelopeHeaders(
119119
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
120120
trace_id: (event.contexts!.trace as Record<string, unknown>).trace_id as string,
121121
public_key: dsn.publicKey,
122-
environment: environment,
123-
release: release,
124-
transaction: transaction,
122+
sample_rate: samplerate,
123+
environment,
124+
release,
125+
transaction,
125126
...((userid || usersegment) && {
126127
user: {
127128
id: userid,

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ describe('createEventEnvelope', () => {
3838
trace: {
3939
trace_id: '1234',
4040
},
41-
baggage: {
42-
release: '1.0.0',
43-
},
41+
},
42+
sdkProcessingMetadata: {
43+
baggage: [{ release: '1.0.0' }, '', false],
4444
},
4545
},
4646
{ release: '1.0.0', trace_id: '1234', public_key: 'pubKey123' },
@@ -53,10 +53,9 @@ describe('createEventEnvelope', () => {
5353
trace: {
5454
trace_id: '1234',
5555
},
56-
baggage: {
57-
environment: 'prod',
58-
release: '1.0.0',
59-
},
56+
},
57+
sdkProcessingMetadata: {
58+
baggage: [{ environment: 'prod', release: '1.0.0' }, '', false],
6059
},
6160
},
6261
{ release: '1.0.0', environment: 'prod', trace_id: '1234', public_key: 'pubKey123' },
@@ -69,13 +68,20 @@ describe('createEventEnvelope', () => {
6968
trace: {
7069
trace_id: '1234',
7170
},
72-
baggage: {
73-
environment: 'prod',
74-
release: '1.0.0',
75-
userid: 'bob',
76-
usersegment: 'segmentA',
77-
transaction: 'TX',
78-
},
71+
},
72+
sdkProcessingMetadata: {
73+
baggage: [
74+
{
75+
environment: 'prod',
76+
release: '1.0.0',
77+
userid: 'bob',
78+
usersegment: 'segmentA',
79+
transaction: 'TX',
80+
samplerate: '0.95',
81+
},
82+
'',
83+
false,
84+
],
7985
},
8086
},
8187
{
@@ -85,6 +91,7 @@ describe('createEventEnvelope', () => {
8591
transaction: 'TX',
8692
trace_id: '1234',
8793
public_key: 'pubKey123',
94+
sample_rate: '0.95',
8895
},
8996
],
9097
];

packages/hub/src/scope.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ export class Scope implements ScopeInterface {
454454
if (this._transactionName) {
455455
event.transaction = this._transactionName;
456456
}
457+
457458
// We want to set the trace context for normal events only if there isn't already
458459
// a trace context on the event. There is a product feature in place where we link
459460
// errors with transaction and it relies on that.
@@ -470,7 +471,12 @@ export class Scope implements ScopeInterface {
470471
event.breadcrumbs = [...(event.breadcrumbs || []), ...this._breadcrumbs];
471472
event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined;
472473

473-
event.sdkProcessingMetadata = this._sdkProcessingMetadata;
474+
// Since we're storing dynamic sampling context data in the event.sdkProcessingMetadata
475+
// field We have to re-apply it after we applied the Scope's field.
476+
// (This is because we're storing this data on the span and not on the scope)
477+
const baggage = event.sdkProcessingMetadata && event.sdkProcessingMetadata.baggage;
478+
event.sdkProcessingMetadata = this._sdkProcessingMetadata || {};
479+
event.sdkProcessingMetadata.baggage = baggage;
474480

475481
return this._notifyEventProcessors([...getGlobalEventProcessors(), ...this._eventProcessors], event, hint);
476482
}

packages/integration-tests/suites/tracing/baggage/test.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

packages/integration-tests/suites/tracing/baggage/init.js renamed to packages/integration-tests/suites/tracing/envelope-header/init.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Sentry.init({
88
integrations: [new Integrations.BrowserTracing({ tracingOrigins: [/.*/] })],
99
environment: 'production',
1010
tracesSampleRate: 1,
11+
debug: true,
1112
});
1213

1314
Sentry.configureScope(scope => {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { expect } from '@playwright/test';
2+
import { EventEnvelopeHeaders } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest(
8+
'should send dynamic sampling context data in transaction envelope header',
9+
async ({ getLocalTestPath, page }) => {
10+
const url = await getLocalTestPath({ testDir: __dirname });
11+
12+
const envHeader = await getFirstSentryEnvelopeRequest<EventEnvelopeHeaders>(page, url, envelopeHeaderRequestParser);
13+
14+
expect(envHeader.trace).toBeDefined();
15+
expect(envHeader.trace).toEqual({
16+
environment: 'production',
17+
// TODO comment back in once we properly support transaction and user data in Baggage
18+
// transaction: 'testTransactionBaggage',
19+
// user: {
20+
// id: 'user123',
21+
// segment: 'segmentB',
22+
// },
23+
sample_rate: '1',
24+
public_key: 'public',
25+
trace_id: expect.any(String),
26+
});
27+
},
28+
);

packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ test('Should populate and propagate sentry baggage if sentry-trace header does n
7777
expect(response).toMatchObject({
7878
test_data: {
7979
host: 'somewhere.not.sentry',
80-
baggage: 'sentry-environment=prod,sentry-release=1.0',
80+
baggage: 'sentry-environment=prod,sentry-release=1.0,sentry-samplerate=1',
8181
},
8282
});
8383
});
@@ -93,7 +93,7 @@ test('Should populate Sentry and propagate 3rd party content if sentry-trace hea
9393
expect(response).toMatchObject({
9494
test_data: {
9595
host: 'somewhere.not.sentry',
96-
baggage: 'foo=bar,bar=baz,sentry-environment=prod,sentry-release=1.0',
96+
baggage: 'foo=bar,bar=baz,sentry-environment=prod,sentry-release=1.0,sentry-samplerate=1',
9797
},
9898
});
9999
});

packages/node/test/integrations/http.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('tracing', () => {
9898
const baggageHeader = request.getHeader('baggage') as string;
9999

100100
expect(baggageHeader).toBeDefined();
101-
expect(baggageHeader).toEqual('sentry-environment=production,sentry-release=1.0.0');
101+
expect(baggageHeader).toEqual('sentry-environment=production,sentry-release=1.0.0,sentry-samplerate=1');
102102
});
103103

104104
it('propagates 3rd party baggage header data to outgoing non-sentry requests', async () => {
@@ -110,7 +110,7 @@ describe('tracing', () => {
110110
const baggageHeader = request.getHeader('baggage') as string;
111111

112112
expect(baggageHeader).toBeDefined();
113-
expect(baggageHeader).toEqual('dog=great,sentry-environment=production,sentry-release=1.0.0');
113+
expect(baggageHeader).toEqual('dog=great,sentry-environment=production,sentry-release=1.0.0,sentry-samplerate=1');
114114
});
115115

116116
it("doesn't attach the sentry-trace header to outgoing sentry requests", () => {

packages/tracing/src/span.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ export class Span implements SpanInterface {
316316

317317
// Only add Sentry baggage items to baggage, if baggage does not exist yet or it is still
318318
// empty and mutable
319+
// TODO: we might want to ditch the isSentryBaggageEmpty condition because it prevents
320+
// custom sentry-values in DSC (added by users in the future)
319321
const finalBaggage =
320322
!existingBaggage || (isBaggageMutable(existingBaggage) && isSentryBaggageEmpty(existingBaggage))
321323
? this._getBaggageWithSentryValues(existingBaggage)
@@ -374,8 +376,12 @@ export class Span implements SpanInterface {
374376

375377
const { environment, release } = (client && client.getOptions()) || {};
376378

379+
const metadata = this.transaction && this.transaction.metadata;
380+
const sampelRate = metadata && metadata.transactionSampling && metadata.transactionSampling.rate;
381+
377382
environment && setBaggageValue(baggage, 'environment', environment);
378383
release && setBaggageValue(baggage, 'release', release);
384+
sampelRate && setBaggageValue(baggage, 'samplerate', sampelRate.toString());
379385

380386
setBaggageImmutable(baggage);
381387

packages/tracing/src/transaction.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
TransactionContext,
77
TransactionMetadata,
88
} from '@sentry/types';
9-
import { dropUndefinedKeys, getSentryBaggageItems, logger } from '@sentry/utils';
9+
import { dropUndefinedKeys, logger } from '@sentry/utils';
1010

1111
import { Span as SpanClass, SpanRecorder } from './span';
1212

@@ -122,15 +122,17 @@ export class Transaction extends SpanClass implements TransactionInterface {
122122
const transaction: Event = {
123123
contexts: {
124124
trace: this.getTraceContext(),
125-
baggage: getSentryBaggageItems(this.getBaggage()),
126125
},
127126
spans: finishedSpans,
128127
start_timestamp: this.startTimestamp,
129128
tags: this.tags,
130129
timestamp: this.endTimestamp,
131130
transaction: this.name,
132131
type: 'transaction',
133-
sdkProcessingMetadata: this.metadata,
132+
sdkProcessingMetadata: {
133+
...this.metadata,
134+
baggage: this.getBaggage(),
135+
},
134136
};
135137

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

packages/types/src/baggage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment';
1+
export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment' | 'samplerate';
22
export type BaggageObj = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;
33

44
/**

packages/types/src/envelope.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { UserFeedback } from './user';
1212
export type EventTraceContext = {
1313
trace_id: Transaction['traceId'];
1414
public_key: DsnComponents['publicKey'];
15+
sample_rate?: string;
1516
release?: string;
1617
user?: {
1718
id?: string;

0 commit comments

Comments
 (0)