1
1
import type { Scope , Span , SpanTimeInput , StartSpanOptions , TransactionContext } from '@sentry/types' ;
2
2
3
3
import { addNonEnumerableProperty , dropUndefinedKeys , logger , tracingContextFromHeaders } from '@sentry/utils' ;
4
+ import { getDynamicSamplingContextFromSpan } from '.' ;
4
5
5
6
import { DEBUG_BUILD } from '../debug-build' ;
6
7
import { getCurrentScope , withScope } from '../exports' ;
7
8
import type { Hub } from '../hub' ;
8
9
import { runWithAsyncContext } from '../hub' ;
9
10
import { getIsolationScope } from '../hub' ;
10
11
import { getCurrentHub } from '../hub' ;
12
+ import type { Scope as ScopeClass } from '../scope' ;
11
13
import { handleCallbackErrors } from '../utils/handleCallbackErrors' ;
12
14
import { hasTracingEnabled } from '../utils/hasTracingEnabled' ;
13
- import { spanTimeInputToSeconds , spanToJSON } from '../utils/spanUtils' ;
15
+ import { spanIsSampled , spanTimeInputToSeconds , spanToJSON } from '../utils/spanUtils' ;
14
16
15
17
/**
16
18
* Wraps a function with a transaction/span and finishes the span after the function is done.
@@ -40,8 +42,13 @@ export function trace<T>(
40
42
// eslint-disable-next-line deprecation/deprecation
41
43
const parentSpan = scope . getSpan ( ) ;
42
44
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
+ } ) ;
45
52
46
53
// eslint-disable-next-line deprecation/deprecation
47
54
scope . setSpan ( activeSpan ) ;
@@ -73,7 +80,7 @@ export function trace<T>(
73
80
* and the `span` returned from the callback will be undefined.
74
81
*/
75
82
export function startSpan < T > ( context : StartSpanOptions , callback : ( span : Span | undefined ) => T ) : T {
76
- const ctx = normalizeContext ( context ) ;
83
+ const spanContext = normalizeContext ( context ) ;
77
84
78
85
return runWithAsyncContext ( ( ) => {
79
86
return withScope ( context . scope , scope => {
@@ -83,10 +90,14 @@ export function startSpan<T>(context: StartSpanOptions, callback: (span: Span |
83
90
const parentSpan = scope . getSpan ( ) ;
84
91
85
92
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
+ } ) ;
90
101
91
102
return handleCallbackErrors (
92
103
( ) => callback ( activeSpan ) ,
@@ -125,7 +136,7 @@ export function startSpanManual<T>(
125
136
context : StartSpanOptions ,
126
137
callback : ( span : Span | undefined , finish : ( ) => void ) => T ,
127
138
) : T {
128
- const ctx = normalizeContext ( context ) ;
139
+ const spanContext = normalizeContext ( context ) ;
129
140
130
141
return runWithAsyncContext ( ( ) => {
131
142
return withScope ( context . scope , scope => {
@@ -135,10 +146,14 @@ export function startSpanManual<T>(
135
146
const parentSpan = scope . getSpan ( ) ;
136
147
137
148
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
+ } ) ;
142
157
143
158
function finishAndSetSpan ( ) : void {
144
159
activeSpan && activeSpan . end ( ) ;
@@ -175,7 +190,7 @@ export function startInactiveSpan(context: StartSpanOptions): Span | undefined {
175
190
return undefined ;
176
191
}
177
192
178
- const ctx = normalizeContext ( context ) ;
193
+ const spanContext = normalizeContext ( context ) ;
179
194
// eslint-disable-next-line deprecation/deprecation
180
195
const hub = getCurrentHub ( ) ;
181
196
const parentSpan = context . scope
@@ -189,37 +204,19 @@ export function startInactiveSpan(context: StartSpanOptions): Span | undefined {
189
204
return undefined ;
190
205
}
191
206
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 ( ) ;
219
208
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 ( ) ;
221
213
222
- return span ;
214
+ return createChildSpanOrTransaction ( hub , {
215
+ parentSpan,
216
+ spanContext,
217
+ forceTransaction : context . forceTransaction ,
218
+ scope : temporaryScope ,
219
+ } ) ;
223
220
}
224
221
225
222
/**
@@ -334,20 +331,46 @@ export const continueTrace: ContinueTrace = <V>(
334
331
335
332
function createChildSpanOrTransaction (
336
333
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
+ } ,
339
345
) : Span | undefined {
340
346
if ( ! hasTracingEnabled ( ) ) {
341
347
return undefined ;
342
348
}
343
349
344
350
const isolationScope = getIsolationScope ( ) ;
345
- const scope = getCurrentScope ( ) ;
346
351
347
352
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
+
349
362
// 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
+ } ) ;
351
374
} else {
352
375
const { traceId, dsc, parentSpanId, sampled } = {
353
376
...isolationScope . getPropagationContext ( ) ,
@@ -359,15 +382,21 @@ function createChildSpanOrTransaction(
359
382
traceId,
360
383
parentSpanId,
361
384
parentSampled : sampled ,
362
- ...ctx ,
385
+ ...spanContext ,
363
386
metadata : {
364
387
dynamicSamplingContext : dsc ,
365
388
// eslint-disable-next-line deprecation/deprecation
366
- ...ctx . metadata ,
389
+ ...spanContext . metadata ,
367
390
} ,
368
391
} ) ;
369
392
}
370
393
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
+
371
400
setCapturedScopesOnSpan ( span , scope , isolationScope ) ;
372
401
373
402
return span ;
0 commit comments