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