11import type { Scope , Span , SpanTimeInput , StartSpanOptions , TransactionContext } from '@sentry/types' ;
22
33import { addNonEnumerableProperty , dropUndefinedKeys , logger , tracingContextFromHeaders } from '@sentry/utils' ;
4+ import { getDynamicSamplingContextFromSpan } from '.' ;
45
56import { DEBUG_BUILD } from '../debug-build' ;
67import { getCurrentScope , withScope } from '../exports' ;
78import type { Hub } from '../hub' ;
89import { runWithAsyncContext } from '../hub' ;
910import { getIsolationScope } from '../hub' ;
1011import { getCurrentHub } from '../hub' ;
12+ import type { Scope as ScopeClass } from '../scope' ;
1113import { handleCallbackErrors } from '../utils/handleCallbackErrors' ;
1214import { hasTracingEnabled } from '../utils/hasTracingEnabled' ;
13- import { spanTimeInputToSeconds , spanToJSON } from '../utils/spanUtils' ;
15+ import { spanIsSampled , spanTimeInputToSeconds , spanToJSON } from '../utils/spanUtils' ;
1416
1517/**
1618 * Wraps a function with a transaction/span and finishes the span after the function is done.
@@ -40,8 +42,13 @@ export function trace<T>(
4042 // eslint-disable-next-line deprecation/deprecation
4143 const parentSpan = scope . getSpan ( ) ;
4244
43- const ctx = normalizeContext ( context ) ;
44- const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
45+ const spanContext = normalizeContext ( context ) ;
46+ const activeSpan = createChildSpanOrTransaction ( hub , {
47+ parentSpan,
48+ spanContext,
49+ forceTransaction : false ,
50+ scope,
51+ } ) ;
4552
4653 // eslint-disable-next-line deprecation/deprecation
4754 scope . setSpan ( activeSpan ) ;
@@ -73,7 +80,7 @@ export function trace<T>(
7380 * and the `span` returned from the callback will be undefined.
7481 */
7582export function startSpan < T > ( context : StartSpanOptions , callback : ( span : Span | undefined ) => T ) : T {
76- const ctx = normalizeContext ( context ) ;
83+ const spanContext = normalizeContext ( context ) ;
7784
7885 return runWithAsyncContext ( ( ) => {
7986 return withScope ( context . scope , scope => {
@@ -83,10 +90,14 @@ export function startSpan<T>(context: StartSpanOptions, callback: (span: Span |
8390 const parentSpan = scope . getSpan ( ) ;
8491
8592 const shouldSkipSpan = context . onlyIfParent && ! parentSpan ;
86- const activeSpan = shouldSkipSpan ? undefined : createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
87-
88- // eslint-disable-next-line deprecation/deprecation
89- scope . setSpan ( activeSpan ) ;
93+ const activeSpan = shouldSkipSpan
94+ ? undefined
95+ : createChildSpanOrTransaction ( hub , {
96+ parentSpan,
97+ spanContext,
98+ forceTransaction : context . forceTransaction ,
99+ scope,
100+ } ) ;
90101
91102 return handleCallbackErrors (
92103 ( ) => callback ( activeSpan ) ,
@@ -125,7 +136,7 @@ export function startSpanManual<T>(
125136 context : StartSpanOptions ,
126137 callback : ( span : Span | undefined , finish : ( ) => void ) => T ,
127138) : T {
128- const ctx = normalizeContext ( context ) ;
139+ const spanContext = normalizeContext ( context ) ;
129140
130141 return runWithAsyncContext ( ( ) => {
131142 return withScope ( context . scope , scope => {
@@ -135,10 +146,14 @@ export function startSpanManual<T>(
135146 const parentSpan = scope . getSpan ( ) ;
136147
137148 const shouldSkipSpan = context . onlyIfParent && ! parentSpan ;
138- const activeSpan = shouldSkipSpan ? undefined : createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
139-
140- // eslint-disable-next-line deprecation/deprecation
141- scope . setSpan ( activeSpan ) ;
149+ const activeSpan = shouldSkipSpan
150+ ? undefined
151+ : createChildSpanOrTransaction ( hub , {
152+ parentSpan,
153+ spanContext,
154+ forceTransaction : context . forceTransaction ,
155+ scope,
156+ } ) ;
142157
143158 function finishAndSetSpan ( ) : void {
144159 activeSpan && activeSpan . end ( ) ;
@@ -175,7 +190,7 @@ export function startInactiveSpan(context: StartSpanOptions): Span | undefined {
175190 return undefined ;
176191 }
177192
178- const ctx = normalizeContext ( context ) ;
193+ const spanContext = normalizeContext ( context ) ;
179194 // eslint-disable-next-line deprecation/deprecation
180195 const hub = getCurrentHub ( ) ;
181196 const parentSpan = context . scope
@@ -189,37 +204,19 @@ export function startInactiveSpan(context: StartSpanOptions): Span | undefined {
189204 return undefined ;
190205 }
191206
192- const isolationScope = getIsolationScope ( ) ;
193- const scope = getCurrentScope ( ) ;
194-
195- let span : Span | undefined ;
196-
197- if ( parentSpan ) {
198- // eslint-disable-next-line deprecation/deprecation
199- span = parentSpan . startChild ( ctx ) ;
200- } else {
201- const { traceId, dsc, parentSpanId, sampled } = {
202- ...isolationScope . getPropagationContext ( ) ,
203- ...scope . getPropagationContext ( ) ,
204- } ;
205-
206- // eslint-disable-next-line deprecation/deprecation
207- span = hub . startTransaction ( {
208- traceId,
209- parentSpanId,
210- parentSampled : sampled ,
211- ...ctx ,
212- metadata : {
213- dynamicSamplingContext : dsc ,
214- // eslint-disable-next-line deprecation/deprecation
215- ...ctx . metadata ,
216- } ,
217- } ) ;
218- }
207+ const scope = context . scope || getCurrentScope ( ) ;
219208
220- setCapturedScopesOnSpan ( span , scope , isolationScope ) ;
209+ // Even though we don't actually want to make this span active on the current scope,
210+ // we need to make it active on a temporary scope that we use for event processing
211+ // as otherwise, it won't pick the correct span for the event when processing it
212+ const temporaryScope = ( scope as ScopeClass ) . clone ( ) ;
221213
222- return span ;
214+ return createChildSpanOrTransaction ( hub , {
215+ parentSpan,
216+ spanContext,
217+ forceTransaction : context . forceTransaction ,
218+ scope : temporaryScope ,
219+ } ) ;
223220}
224221
225222/**
@@ -334,20 +331,46 @@ export const continueTrace: ContinueTrace = <V>(
334331
335332function createChildSpanOrTransaction (
336333 hub : Hub ,
337- parentSpan : Span | undefined ,
338- ctx : TransactionContext ,
334+ {
335+ parentSpan,
336+ spanContext,
337+ forceTransaction,
338+ scope,
339+ } : {
340+ parentSpan : Span | undefined ;
341+ spanContext : TransactionContext ;
342+ forceTransaction ?: boolean ;
343+ scope : Scope ;
344+ } ,
339345) : Span | undefined {
340346 if ( ! hasTracingEnabled ( ) ) {
341347 return undefined ;
342348 }
343349
344350 const isolationScope = getIsolationScope ( ) ;
345- const scope = getCurrentScope ( ) ;
346351
347352 let span : Span | undefined ;
348- if ( parentSpan ) {
353+ if ( parentSpan && ! forceTransaction ) {
354+ // eslint-disable-next-line deprecation/deprecation
355+ span = parentSpan . startChild ( spanContext ) ;
356+ } else if ( parentSpan ) {
357+ // If we forced a transaction but have a parent span, make sure to continue from the parent span, not the scope
358+ const dsc = getDynamicSamplingContextFromSpan ( parentSpan ) ;
359+ const { traceId, spanId : parentSpanId } = parentSpan . spanContext ( ) ;
360+ const sampled = spanIsSampled ( parentSpan ) ;
361+
349362 // eslint-disable-next-line deprecation/deprecation
350- span = parentSpan . startChild ( ctx ) ;
363+ span = hub . startTransaction ( {
364+ traceId,
365+ parentSpanId,
366+ parentSampled : sampled ,
367+ ...spanContext ,
368+ metadata : {
369+ dynamicSamplingContext : dsc ,
370+ // eslint-disable-next-line deprecation/deprecation
371+ ...spanContext . metadata ,
372+ } ,
373+ } ) ;
351374 } else {
352375 const { traceId, dsc, parentSpanId, sampled } = {
353376 ...isolationScope . getPropagationContext ( ) ,
@@ -359,15 +382,21 @@ function createChildSpanOrTransaction(
359382 traceId,
360383 parentSpanId,
361384 parentSampled : sampled ,
362- ...ctx ,
385+ ...spanContext ,
363386 metadata : {
364387 dynamicSamplingContext : dsc ,
365388 // eslint-disable-next-line deprecation/deprecation
366- ...ctx . metadata ,
389+ ...spanContext . metadata ,
367390 } ,
368391 } ) ;
369392 }
370393
394+ // We always set this as active span on the scope
395+ // In the case of this being an inactive span, we ensure to pass a detached scope in here in the first place
396+ // But by having this here, we can ensure that the lookup through `getCapturedScopesOnSpan` results in the correct scope & span combo
397+ // eslint-disable-next-line deprecation/deprecation
398+ scope . setSpan ( span ) ;
399+
371400 setCapturedScopesOnSpan ( span , scope , isolationScope ) ;
372401
373402 return span ;
0 commit comments