Skip to content

Commit d0881ad

Browse files
committed
ref: Streamline sentry-trace, baggage and DSC handling
1 parent acb5688 commit d0881ad

File tree

15 files changed

+205
-169
lines changed

15 files changed

+205
-169
lines changed

packages/browser/src/tracing/request.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,11 @@ import {
1010
SentryNonRecordingSpan,
1111
getActiveSpan,
1212
getClient,
13-
getCurrentScope,
14-
getDynamicSamplingContextFromClient,
15-
getDynamicSamplingContextFromSpan,
16-
getIsolationScope,
13+
getSentryHeaders,
1714
hasTracingEnabled,
1815
instrumentFetchRequest,
1916
setHttpStatus,
2017
spanToJSON,
21-
spanToTraceHeader,
2218
startInactiveSpan,
2319
} from '@sentry/core';
2420
import type { Client, HandlerDataXhr, SentryWrappedXMLHttpRequest, Span } from '@sentry/types';
@@ -27,8 +23,6 @@ import {
2723
addFetchEndInstrumentationHandler,
2824
addFetchInstrumentationHandler,
2925
browserPerformanceTimeOrigin,
30-
dynamicSamplingContextToSentryBaggageHeader,
31-
generateSentryTraceHeader,
3226
parseUrl,
3327
stringMatchesSomePattern,
3428
} from '@sentry/utils';
@@ -419,21 +413,9 @@ export function xhrCallback(
419413
}
420414

421415
function addTracingHeadersToXhrRequest(xhr: SentryWrappedXMLHttpRequest, client: Client, span?: Span): void {
422-
const scope = getCurrentScope();
423-
const isolationScope = getIsolationScope();
424-
const { traceId, spanId, sampled, dsc } = {
425-
...isolationScope.getPropagationContext(),
426-
...scope.getPropagationContext(),
427-
};
428-
429-
const sentryTraceHeader =
430-
span && hasTracingEnabled() ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, spanId, sampled);
431-
432-
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(
433-
dsc || (span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromClient(traceId, client)),
434-
);
416+
const { sentryTrace, baggage } = getSentryHeaders({ span, client });
435417

436-
setHeaderOnXhr(xhr, sentryTraceHeader, sentryBaggageHeader);
418+
setHeaderOnXhr(xhr, sentryTrace, baggage);
437419
}
438420

439421
function setHeaderOnXhr(

packages/core/src/baseclient.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import {
3737
checkOrSetAlreadyCaught,
3838
createAttachmentEnvelopeItem,
3939
createClientReportEnvelope,
40-
dropUndefinedKeys,
4140
dsnToString,
4241
isParameterizedString,
4342
isPlainObject,
@@ -57,9 +56,9 @@ import { createEventEnvelope, createSessionEnvelope } from './envelope';
5756
import type { IntegrationIndex } from './integration';
5857
import { afterSetupIntegrations } from './integration';
5958
import { setupIntegration, setupIntegrations } from './integration';
59+
import { getDynamicSamplingContextFromScopes, getTraceContextFromScopes } from './propagationContext';
6060
import type { Scope } from './scope';
6161
import { updateSession } from './session';
62-
import { getDynamicSamplingContextFromClient } from './tracing/dynamicSamplingContext';
6362
import { parseSampleRate } from './utils/parseSampleRate';
6463
import { prepareEvent } from './utils/prepareEvent';
6564

@@ -689,30 +688,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
689688
return evt;
690689
}
691690

692-
const propagationContext = {
693-
...isolationScope.getPropagationContext(),
694-
...(currentScope ? currentScope.getPropagationContext() : undefined),
691+
evt.contexts = {
692+
trace: getTraceContextFromScopes(currentScope, isolationScope),
693+
...evt.contexts,
695694
};
696695

697-
const trace = evt.contexts && evt.contexts.trace;
698-
if (!trace && propagationContext) {
699-
const { traceId: trace_id, spanId, parentSpanId, dsc } = propagationContext;
700-
evt.contexts = {
701-
trace: dropUndefinedKeys({
702-
trace_id,
703-
span_id: spanId,
704-
parent_span_id: parentSpanId,
705-
}),
706-
...evt.contexts,
707-
};
696+
const dynamicSamplingContext = getDynamicSamplingContextFromScopes(this, currentScope, isolationScope);
708697

709-
const dynamicSamplingContext = dsc ? dsc : getDynamicSamplingContextFromClient(trace_id, this);
698+
evt.sdkProcessingMetadata = {
699+
dynamicSamplingContext,
700+
...evt.sdkProcessingMetadata,
701+
};
710702

711-
evt.sdkProcessingMetadata = {
712-
dynamicSamplingContext,
713-
...evt.sdkProcessingMetadata,
714-
};
715-
}
716703
return evt;
717704
});
718705
}

packages/core/src/fetch.ts

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
import type { Client, HandlerDataFetch, Scope, Span, SpanOrigin } from '@sentry/types';
2-
import {
3-
BAGGAGE_HEADER_NAME,
4-
SENTRY_BAGGAGE_KEY_PREFIX,
5-
dynamicSamplingContextToSentryBaggageHeader,
6-
generateSentryTraceHeader,
7-
isInstanceOf,
8-
parseUrl,
9-
} from '@sentry/utils';
10-
import { getClient, getCurrentScope, getIsolationScope } from './currentScopes';
2+
import { BAGGAGE_HEADER_NAME, SENTRY_BAGGAGE_KEY_PREFIX, isInstanceOf, parseUrl } from '@sentry/utils';
3+
import { getClient, getCurrentScope } from './currentScopes';
114
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes';
12-
import {
13-
SPAN_STATUS_ERROR,
14-
getDynamicSamplingContextFromClient,
15-
getDynamicSamplingContextFromSpan,
16-
setHttpStatus,
17-
startInactiveSpan,
18-
} from './tracing';
5+
import { SPAN_STATUS_ERROR, getSentryHeaders, setHttpStatus, startInactiveSpan } from './tracing';
196
import { SentryNonRecordingSpan } from './tracing/sentryNonRecordingSpan';
207
import { hasTracingEnabled } from './utils/hasTracingEnabled';
21-
import { getActiveSpan, spanToTraceHeader } from './utils/spanUtils';
8+
import { getActiveSpan } from './utils/spanUtils';
229

2310
type PolymorphicRequestHeaders =
2411
| Record<string, string | undefined>
@@ -132,44 +119,31 @@ export function addTracingHeadersToFetchRequest(
132119
},
133120
span?: Span,
134121
): PolymorphicRequestHeaders | undefined {
135-
const isolationScope = getIsolationScope();
136-
137-
const { traceId, spanId, sampled, dsc } = {
138-
...isolationScope.getPropagationContext(),
139-
...scope.getPropagationContext(),
140-
};
141-
142-
const sentryTraceHeader = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, spanId, sampled);
143-
144-
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(
145-
dsc || (span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromClient(traceId, client)),
146-
);
122+
const { sentryTrace, baggage } = getSentryHeaders({ span, client, scope });
147123

148124
const headers =
149125
fetchOptionsObj.headers ||
150126
(typeof Request !== 'undefined' && isInstanceOf(request, Request) ? (request as Request).headers : undefined);
151127

152128
if (!headers) {
153-
return { 'sentry-trace': sentryTraceHeader, baggage: sentryBaggageHeader };
129+
return { 'sentry-trace': sentryTrace, baggage };
154130
} else if (typeof Headers !== 'undefined' && isInstanceOf(headers, Headers)) {
155131
const newHeaders = new Headers(headers as Headers);
156132

157-
newHeaders.set('sentry-trace', sentryTraceHeader);
133+
newHeaders.set('sentry-trace', sentryTrace);
158134

159-
if (sentryBaggageHeader) {
135+
if (baggage) {
160136
const prevBaggageHeader = newHeaders.get(BAGGAGE_HEADER_NAME);
161137
if (prevBaggageHeader) {
162138
const prevHeaderStrippedFromSentryBaggage = stripBaggageHeaderOfSentryBaggageValues(prevBaggageHeader);
163139
newHeaders.set(
164140
BAGGAGE_HEADER_NAME,
165141
// If there are non-sentry entries (i.e. if the stripped string is non-empty/truthy) combine the stripped header and sentry baggage header
166142
// otherwise just set the sentry baggage header
167-
prevHeaderStrippedFromSentryBaggage
168-
? `${prevHeaderStrippedFromSentryBaggage},${sentryBaggageHeader}`
169-
: sentryBaggageHeader,
143+
prevHeaderStrippedFromSentryBaggage ? `${prevHeaderStrippedFromSentryBaggage},${baggage}` : baggage,
170144
);
171145
} else {
172-
newHeaders.set(BAGGAGE_HEADER_NAME, sentryBaggageHeader);
146+
newHeaders.set(BAGGAGE_HEADER_NAME, baggage);
173147
}
174148
}
175149

@@ -191,13 +165,13 @@ export function addTracingHeadersToFetchRequest(
191165
}
192166
}),
193167
// Attach the new sentry-trace header
194-
['sentry-trace', sentryTraceHeader],
168+
['sentry-trace', sentryTrace],
195169
];
196170

197-
if (sentryBaggageHeader) {
171+
if (baggage) {
198172
// If there are multiple entries with the same key, the browser will merge the values into a single request header.
199173
// Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
200-
newHeaders.push([BAGGAGE_HEADER_NAME, sentryBaggageHeader]);
174+
newHeaders.push([BAGGAGE_HEADER_NAME, baggage]);
201175
}
202176

203177
return newHeaders as PolymorphicRequestHeaders;
@@ -215,13 +189,13 @@ export function addTracingHeadersToFetchRequest(
215189
newBaggageHeaders.push(stripBaggageHeaderOfSentryBaggageValues(existingBaggageHeader));
216190
}
217191

218-
if (sentryBaggageHeader) {
219-
newBaggageHeaders.push(sentryBaggageHeader);
192+
if (baggage) {
193+
newBaggageHeaders.push(baggage);
220194
}
221195

222196
return {
223197
...(headers as Exclude<typeof headers, Headers>),
224-
'sentry-trace': sentryTraceHeader,
198+
'sentry-trace': sentryTrace,
225199
baggage: newBaggageHeaders.length > 0 ? newBaggageHeaders.join(',') : undefined,
226200
};
227201
}

packages/core/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export {
4343
getDefaultCurrentScope,
4444
getDefaultIsolationScope,
4545
} from './defaultScopes';
46+
export {
47+
getDynamicSamplingContextFromScopes,
48+
getTraceContextFromScopes,
49+
} from './propagationContext';
4650
export { setAsyncContextStrategy } from './asyncContext';
4751
export { getMainCarrier } from './carrier';
4852
export { makeSession, closeSession, updateSession } from './session';
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { Client, DynamicSamplingContext, PropagationContext, TraceContext } from '@sentry/types';
2+
import { dropUndefinedKeys, generateSentryTraceHeader } from '@sentry/utils';
3+
import { getCurrentScope, getGlobalScope, getIsolationScope } from './currentScopes';
4+
import { getDynamicSamplingContextFromClient } from './tracing';
5+
6+
/**
7+
* Get a trace context for the currently active scopes.
8+
*/
9+
export function getTraceContextFromScopes(
10+
scope = getCurrentScope(),
11+
isolationScope = getIsolationScope(),
12+
globalScope = getGlobalScope(),
13+
): TraceContext {
14+
const propagationContext = mergePropagationContexts(scope, isolationScope, globalScope);
15+
16+
const { traceId, spanId, parentSpanId } = propagationContext;
17+
18+
const traceContext: TraceContext = dropUndefinedKeys({
19+
trace_id: traceId,
20+
span_id: spanId,
21+
parent_span_id: parentSpanId,
22+
});
23+
24+
return traceContext;
25+
}
26+
27+
/**
28+
* Get a sentry-trace header value for the currently active scopes.
29+
*/
30+
export function scopesToTraceHeader(
31+
scope = getCurrentScope(),
32+
isolationScope = getIsolationScope(),
33+
globalScope = getGlobalScope(),
34+
): string {
35+
const { traceId, sampled, spanId } = mergePropagationContexts(scope, isolationScope, globalScope);
36+
return generateSentryTraceHeader(traceId, spanId, sampled);
37+
}
38+
39+
/**
40+
* Get the dynamic sampling context for the currently active scopes.
41+
*/
42+
export function getDynamicSamplingContextFromScopes(
43+
client: Client,
44+
scope = getCurrentScope(),
45+
isolationScope = getIsolationScope(),
46+
globalScope = getGlobalScope(),
47+
): Partial<DynamicSamplingContext> {
48+
const propagationContext = mergePropagationContexts(scope, isolationScope, globalScope);
49+
return propagationContext.dsc || getDynamicSamplingContextFromClient(propagationContext.traceId, client);
50+
}
51+
52+
function mergePropagationContexts(
53+
scope = getCurrentScope(),
54+
isolationScope = getIsolationScope(),
55+
globalScope = getGlobalScope(),
56+
): PropagationContext {
57+
return {
58+
...globalScope.getPropagationContext(),
59+
...isolationScope.getPropagationContext(),
60+
...scope.getPropagationContext(),
61+
};
62+
}

packages/core/src/server-runtime-client.ts

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@ import { BaseClient } from './baseclient';
1717
import { createCheckInEnvelope } from './checkin';
1818
import { getIsolationScope } from './currentScopes';
1919
import { DEBUG_BUILD } from './debug-build';
20+
import { getDynamicSamplingContextFromScopes, getTraceContextFromScopes } from './propagationContext';
2021
import type { Scope } from './scope';
2122
import { SessionFlusher } from './sessionflusher';
22-
import {
23-
getDynamicSamplingContextFromClient,
24-
getDynamicSamplingContextFromSpan,
25-
registerSpanErrorInstrumentation,
26-
} from './tracing';
23+
import { getDynamicSamplingContextFromSpan, registerSpanErrorInstrumentation } from './tracing';
2724
import { _getSpanForScope } from './utils/spanOnScope';
28-
import { getRootSpan, spanToTraceContext } from './utils/spanUtils';
25+
import { spanToTraceContext } from './utils/spanUtils';
2926

3027
export interface ServerRuntimeClientOptions extends ClientOptions<BaseTransportOptions> {
3128
platform?: string;
@@ -248,30 +245,19 @@ export class ServerRuntimeClient<
248245
}
249246

250247
/** Extract trace information from scope */
251-
private _getTraceInfoFromScope(
248+
protected _getTraceInfoFromScope(
252249
scope: Scope | undefined,
253250
): [dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined, traceContext: TraceContext | undefined] {
254251
if (!scope) {
255252
return [undefined, undefined];
256253
}
257254

258255
const span = _getSpanForScope(scope);
259-
if (span) {
260-
const rootSpan = getRootSpan(span);
261-
const samplingContext = getDynamicSamplingContextFromSpan(rootSpan);
262-
return [samplingContext, spanToTraceContext(rootSpan)];
263-
}
264-
265-
const { traceId, spanId, parentSpanId, dsc } = scope.getPropagationContext();
266-
const traceContext: TraceContext = {
267-
trace_id: traceId,
268-
span_id: spanId,
269-
parent_span_id: parentSpanId,
270-
};
271-
if (dsc) {
272-
return [dsc, traceContext];
273-
}
274256

275-
return [getDynamicSamplingContextFromClient(traceId, this), traceContext];
257+
const traceContext = span ? spanToTraceContext(span) : getTraceContextFromScopes(scope);
258+
const dynamicSamplingContext = span
259+
? getDynamicSamplingContextFromSpan(span)
260+
: getDynamicSamplingContextFromScopes(this, scope);
261+
return [dynamicSamplingContext, traceContext];
276262
}
277263
}

packages/core/src/tracing/dynamicSamplingContext.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ export function getDynamicSamplingContextFromSpan(span: Span): Readonly<Partial<
6565
return {};
6666
}
6767

68-
const dsc = getDynamicSamplingContextFromClient(spanToJSON(span).trace_id || '', client);
69-
7068
const rootSpan = getRootSpan(span);
7169

7270
// For core implementation, we freeze the DSC onto the span as a non-enumerable property
@@ -87,6 +85,7 @@ export function getDynamicSamplingContextFromSpan(span: Span): Readonly<Partial<
8785
}
8886

8987
// Else, we generate it from the span
88+
const dsc = getDynamicSamplingContextFromClient(span.spanContext().traceId, client);
9089
const jsonSpan = spanToJSON(rootSpan);
9190
const attributes = jsonSpan.data || {};
9291
const maybeSampleRate = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];

packages/core/src/tracing/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export {
2727
export { setMeasurement, timedEventsToMeasurements } from './measurement';
2828
export { sampleSpan } from './sampling';
2929
export { logSpanEnd, logSpanStart } from './logSpans';
30+
export { getSentryHeaders } from './sentryHeaders';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { Client, Scope, Span } from '@sentry/types';
2+
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
3+
import { getDynamicSamplingContextFromScopes, scopesToTraceHeader } from '../propagationContext';
4+
import { spanToTraceHeader } from '../utils/spanUtils';
5+
import { getDynamicSamplingContextFromSpan } from './dynamicSamplingContext';
6+
7+
/**
8+
* Get the sentry-trace and baggage headers for a given span or scope.
9+
* If no scope is defined, it will use the current scope.
10+
*/
11+
export function getSentryHeaders({ span, client, scope }: { span?: Span; client: Client; scope?: Scope }): {
12+
sentryTrace: string;
13+
baggage: string | undefined;
14+
} {
15+
const sentryTrace = span ? spanToTraceHeader(span) : scopesToTraceHeader(scope);
16+
const dsc = span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromScopes(client, scope);
17+
const baggage = dynamicSamplingContextToSentryBaggageHeader(dsc);
18+
19+
return {
20+
sentryTrace,
21+
baggage,
22+
};
23+
}

0 commit comments

Comments
 (0)