File tree

5 files changed

+78
-12
lines changed

5 files changed

+78
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getAsyncContextStrategy } from '../asyncContext';
22
import { getMainCarrier } from '../carrier';
3+
import type { Client } from '../client';
34
import { getClient, getCurrentScope } from '../currentScopes';
45
import { isEnabled } from '../exports';
56
import type { Scope } from '../scope';
@@ -22,8 +23,8 @@ import { getActiveSpan, spanToTraceHeader } from './spanUtils';
2223
* @returns an object with the tracing data values. The object keys are the name of the tracing key to be used as header
2324
* or meta tag name.
2425
*/
25-
export function getTraceData(options: { span?: Span } = {}): SerializedTraceData {
26-
const client = getClient();
26+
export function getTraceData(options: { span?: Span; scope?: Scope; client?: Client } = {}): SerializedTraceData {
27+
const client = options.client || getClient();
2728
if (!isEnabled() || !client) {
2829
return {};
2930
}
@@ -34,7 +35,7 @@ export function getTraceData(options: { span?: Span } = {}): SerializedTraceData
3435
return acs.getTraceData(options);
3536
}
3637

37-
const scope = getCurrentScope();
38+
const scope = options.scope || getCurrentScope();
3839
const span = options.span || getActiveSpan();
3940
const sentryTrace = span ? spanToTraceHeader(span) : scopeToTraceHeader(scope);
4041
const dsc = span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromScope(client, scope);
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
getIsolationScope,
77
getMainCarrier,
88
getTraceData,
9+
Scope,
910
SentrySpan,
1011
setAsyncContextStrategy,
1112
setCurrentClient,
@@ -158,6 +159,35 @@ describe('getTraceData', () => {
158159
});
159160
});
160161

162+
it('allows to pass a scope & client directly', () => {
163+
// this default client & scope should not be used!
164+
setupClient();
165+
getCurrentScope().setPropagationContext({
166+
traceId: '12345678901234567890123456789099',
167+
sampleRand: 0.44,
168+
});
169+
170+
const options = getDefaultTestClientOptions({
171+
dsn: 'https://[email protected]/42',
172+
tracesSampleRate: 1,
173+
});
174+
const customClient = new TestClient(options);
175+
176+
const scope = new Scope();
177+
scope.setPropagationContext({
178+
traceId: '12345678901234567890123456789012',
179+
sampleRand: 0.42,
180+
});
181+
scope.setClient(customClient);
182+
183+
const traceData = getTraceData({ client: customClient, scope });
184+
185+
expect(traceData['sentry-trace']).toMatch(/^12345678901234567890123456789012-[a-f0-9]{16}$/);
186+
expect(traceData.baggage).toEqual(
187+
'sentry-environment=production,sentry-public_key=567,sentry-trace_id=12345678901234567890123456789012',
188+
);
189+
});
190+
161191
it('returns propagationContext DSC data if no span is available', () => {
162192
setupClient();
163193

Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Baggage, Context, Span, SpanContext, TextMapGetter, TextMapSetter
22
import { context, INVALID_TRACEID, propagation, trace, TraceFlags } from '@opentelemetry/api';
33
import { isTracingSuppressed, W3CBaggagePropagator } from '@opentelemetry/core';
44
import { ATTR_URL_FULL, SEMATTRS_HTTP_URL } from '@opentelemetry/semantic-conventions';
5-
import type { continueTrace, DynamicSamplingContext, Options } from '@sentry/core';
5+
import type { Client, continueTrace, DynamicSamplingContext, Options, Scope } from '@sentry/core';
66
import {
77
generateSentryTraceHeader,
88
getClient,
@@ -152,8 +152,12 @@ export function shouldPropagateTraceForUrl(
152152

153153
/**
154154
* Get propagation injection data for the given context.
155+
* The additional options can be passed to override the scope and client that is otherwise derived from the context.
155156
*/
156-
export function getInjectionData(context: Context): {
157+
export function getInjectionData(
158+
context: Context,
159+
options: { scope?: Scope; client?: Client } = {},
160+
): {
157161
dynamicSamplingContext: Partial<DynamicSamplingContext> | undefined;
158162
traceId: string | undefined;
159163
spanId: string | undefined;
@@ -190,8 +194,8 @@ export function getInjectionData(context: Context): {
190194

191195
// Else we try to use the propagation context from the scope
192196
// The only scenario where this should happen is when we neither have a span, nor an incoming trace
193-
const scope = getScopesFromContext(context)?.scope || getCurrentScope();
194-
const client = getClient();
197+
const scope = options.scope || getScopesFromContext(context)?.scope || getCurrentScope();
198+
const client = options.client || getClient();
195199

196200
const propagationContext = scope.getPropagationContext();
197201
const dynamicSamplingContext = client ? getDynamicSamplingContextFromScope(client, scope) : undefined;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as api from '@opentelemetry/api';
2-
import type { SerializedTraceData, Span } from '@sentry/core';
2+
import type { Client, Scope, SerializedTraceData, Span } from '@sentry/core';
33
import {
44
dynamicSamplingContextToSentryBaggageHeader,
55
generateSentryTraceHeader,
@@ -12,16 +12,20 @@ import { getContextFromScope } from './contextData';
1212
* Otel-specific implementation of `getTraceData`.
1313
* @see `@sentry/core` version of `getTraceData` for more information
1414
*/
15-
export function getTraceData({ span }: { span?: Span } = {}): SerializedTraceData {
16-
let ctx = api.context.active();
15+
export function getTraceData({
16+
span,
17+
scope,
18+
client,
19+
}: { span?: Span; scope?: Scope; client?: Client } = {}): SerializedTraceData {
20+
let ctx = (scope && getContextFromScope(scope)) ?? api.context.active();
1721

1822
if (span) {
1923
const { scope } = getCapturedScopesOnSpan(span);
2024
// fall back to current context if for whatever reason we can't find the one of the span
2125
ctx = (scope && getContextFromScope(scope)) || api.trace.setSpan(api.context.active(), span);
2226
}
2327

24-
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx);
28+
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx, { scope, client });
2529

2630
return {
2731
'sentry-trace': generateSentryTraceHeader(traceId, spanId, sampled),
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { context, trace } from '@opentelemetry/api';
2-
import { getCurrentScope, setAsyncContextStrategy } from '@sentry/core';
2+
import { getCurrentScope, Scope, setAsyncContextStrategy } from '@sentry/core';
33
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
44
import { getTraceData } from '../../src/utils/getTraceData';
55
import { makeTraceState } from '../../src/utils/makeTraceState';
66
import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit';
7+
import { getDefaultTestClientOptions, TestClient } from '../helpers/TestClient';
78

89
describe('getTraceData', () => {
910
beforeEach(() => {
@@ -52,6 +53,32 @@ describe('getTraceData', () => {
5253
});
5354
});
5455

56+
it('allows to pass a scope & client directly', () => {
57+
getCurrentScope().setPropagationContext({
58+
traceId: '12345678901234567890123456789099',
59+
sampleRand: 0.44,
60+
});
61+
62+
const customClient = new TestClient(
63+
getDefaultTestClientOptions({ tracesSampleRate: 1, dsn: 'https://[email protected]/42' }),
64+
);
65+
66+
// note: Right now, this only works properly if the scope is linked to a context
67+
const scope = new Scope();
68+
scope.setPropagationContext({
69+
traceId: '12345678901234567890123456789012',
70+
sampleRand: 0.42,
71+
});
72+
scope.setClient(customClient);
73+
74+
const traceData = getTraceData({ client: customClient, scope });
75+
76+
expect(traceData['sentry-trace']).toMatch(/^12345678901234567890123456789012-[a-f0-9]{16}$/);
77+
expect(traceData.baggage).toEqual(
78+
'sentry-environment=production,sentry-public_key=123,sentry-trace_id=12345678901234567890123456789012',
79+
);
80+
});
81+
5582
it('returns propagationContext DSC data if no span is available', () => {
5683
getCurrentScope().setPropagationContext({
5784
traceId: '12345678901234567890123456789012',

0 commit comments

Comments
 (0)