1
- /* eslint-disable max-lines */
2
-
3
1
import type { ClientOptions , Scope , SentrySpanArguments , Span , SpanTimeInput , StartSpanOptions } from '@sentry/types' ;
4
2
import { generatePropagationContext , logger , propagationContextFromHeaders } from '@sentry/utils' ;
5
3
import type { AsyncContextStrategy } from '../asyncContext/types' ;
@@ -34,47 +32,40 @@ const SUPPRESS_TRACING_KEY = '__SENTRY_SUPPRESS_TRACING__';
34
32
* You'll always get a span passed to the callback,
35
33
* it may just be a non-recording span if the span is not sampled or if tracing is disabled.
36
34
*/
37
- export function startSpan < T > ( options : StartSpanOptions , callback : ( span : Span ) => T ) : T {
35
+ export function startSpan < T > ( context : StartSpanOptions , callback : ( span : Span ) => T ) : T {
38
36
const acs = getAcs ( ) ;
39
37
if ( acs . startSpan ) {
40
- return acs . startSpan ( options , callback ) ;
38
+ return acs . startSpan ( context , callback ) ;
41
39
}
42
40
43
- const spanArguments = parseSentrySpanArguments ( options ) ;
44
- const { forceTransaction, parentSpan : customParentSpan } = options ;
45
-
46
- return withScope ( options . scope , ( ) => {
47
- // If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan`
48
- const wrapper = getActiveSpanWrapper < T > ( customParentSpan ) ;
49
-
50
- return wrapper ( ( ) => {
51
- const scope = getCurrentScope ( ) ;
52
- const parentSpan = getParentSpan ( scope ) ;
53
-
54
- const shouldSkipSpan = options . onlyIfParent && ! parentSpan ;
55
- const activeSpan = shouldSkipSpan
56
- ? new SentryNonRecordingSpan ( )
57
- : createChildOrRootSpan ( {
58
- parentSpan,
59
- spanArguments,
60
- forceTransaction,
61
- scope,
62
- } ) ;
63
-
64
- _setSpanForScope ( scope , activeSpan ) ;
65
-
66
- return handleCallbackErrors (
67
- ( ) => callback ( activeSpan ) ,
68
- ( ) => {
69
- // Only update the span status if it hasn't been changed yet, and the span is not yet finished
70
- const { status } = spanToJSON ( activeSpan ) ;
71
- if ( activeSpan . isRecording ( ) && ( ! status || status === 'ok' ) ) {
72
- activeSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'internal_error' } ) ;
73
- }
74
- } ,
75
- ( ) => activeSpan . end ( ) ,
76
- ) ;
77
- } ) ;
41
+ const spanContext = normalizeContext ( context ) ;
42
+
43
+ return withScope ( context . scope , scope => {
44
+ const parentSpan = getParentSpan ( scope ) ;
45
+
46
+ const shouldSkipSpan = context . onlyIfParent && ! parentSpan ;
47
+ const activeSpan = shouldSkipSpan
48
+ ? new SentryNonRecordingSpan ( )
49
+ : createChildOrRootSpan ( {
50
+ parentSpan,
51
+ spanContext,
52
+ forceTransaction : context . forceTransaction ,
53
+ scope,
54
+ } ) ;
55
+
56
+ _setSpanForScope ( scope , activeSpan ) ;
57
+
58
+ return handleCallbackErrors (
59
+ ( ) => callback ( activeSpan ) ,
60
+ ( ) => {
61
+ // Only update the span status if it hasn't been changed yet, and the span is not yet finished
62
+ const { status } = spanToJSON ( activeSpan ) ;
63
+ if ( activeSpan . isRecording ( ) && ( ! status || status === 'ok' ) ) {
64
+ activeSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'internal_error' } ) ;
65
+ }
66
+ } ,
67
+ ( ) => activeSpan . end ( ) ,
68
+ ) ;
78
69
} ) ;
79
70
}
80
71
@@ -88,50 +79,43 @@ export function startSpan<T>(options: StartSpanOptions, callback: (span: Span) =
88
79
* You'll always get a span passed to the callback,
89
80
* it may just be a non-recording span if the span is not sampled or if tracing is disabled.
90
81
*/
91
- export function startSpanManual < T > ( options : StartSpanOptions , callback : ( span : Span , finish : ( ) => void ) => T ) : T {
82
+ export function startSpanManual < T > ( context : StartSpanOptions , callback : ( span : Span , finish : ( ) => void ) => T ) : T {
92
83
const acs = getAcs ( ) ;
93
84
if ( acs . startSpanManual ) {
94
- return acs . startSpanManual ( options , callback ) ;
85
+ return acs . startSpanManual ( context , callback ) ;
95
86
}
96
87
97
- const spanArguments = parseSentrySpanArguments ( options ) ;
98
- const { forceTransaction, parentSpan : customParentSpan } = options ;
99
-
100
- return withScope ( options . scope , ( ) => {
101
- // If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan`
102
- const wrapper = getActiveSpanWrapper < T > ( customParentSpan ) ;
103
-
104
- return wrapper ( ( ) => {
105
- const scope = getCurrentScope ( ) ;
106
- const parentSpan = getParentSpan ( scope ) ;
107
-
108
- const shouldSkipSpan = options . onlyIfParent && ! parentSpan ;
109
- const activeSpan = shouldSkipSpan
110
- ? new SentryNonRecordingSpan ( )
111
- : createChildOrRootSpan ( {
112
- parentSpan,
113
- spanArguments,
114
- forceTransaction,
115
- scope,
116
- } ) ;
117
-
118
- _setSpanForScope ( scope , activeSpan ) ;
119
-
120
- function finishAndSetSpan ( ) : void {
121
- activeSpan . end ( ) ;
122
- }
123
-
124
- return handleCallbackErrors (
125
- ( ) => callback ( activeSpan , finishAndSetSpan ) ,
126
- ( ) => {
127
- // Only update the span status if it hasn't been changed yet, and the span is not yet finished
128
- const { status } = spanToJSON ( activeSpan ) ;
129
- if ( activeSpan . isRecording ( ) && ( ! status || status === 'ok' ) ) {
130
- activeSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'internal_error' } ) ;
131
- }
132
- } ,
133
- ) ;
134
- } ) ;
88
+ const spanContext = normalizeContext ( context ) ;
89
+
90
+ return withScope ( context . scope , scope => {
91
+ const parentSpan = getParentSpan ( scope ) ;
92
+
93
+ const shouldSkipSpan = context . onlyIfParent && ! parentSpan ;
94
+ const activeSpan = shouldSkipSpan
95
+ ? new SentryNonRecordingSpan ( )
96
+ : createChildOrRootSpan ( {
97
+ parentSpan,
98
+ spanContext,
99
+ forceTransaction : context . forceTransaction ,
100
+ scope,
101
+ } ) ;
102
+
103
+ _setSpanForScope ( scope , activeSpan ) ;
104
+
105
+ function finishAndSetSpan ( ) : void {
106
+ activeSpan . end ( ) ;
107
+ }
108
+
109
+ return handleCallbackErrors (
110
+ ( ) => callback ( activeSpan , finishAndSetSpan ) ,
111
+ ( ) => {
112
+ // Only update the span status if it hasn't been changed yet, and the span is not yet finished
113
+ const { status } = spanToJSON ( activeSpan ) ;
114
+ if ( activeSpan . isRecording ( ) && ( ! status || status === 'ok' ) ) {
115
+ activeSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'internal_error' } ) ;
116
+ }
117
+ } ,
118
+ ) ;
135
119
} ) ;
136
120
}
137
121
@@ -144,39 +128,28 @@ export function startSpanManual<T>(options: StartSpanOptions, callback: (span: S
144
128
* This function will always return a span,
145
129
* it may just be a non-recording span if the span is not sampled or if tracing is disabled.
146
130
*/
147
- export function startInactiveSpan ( options : StartSpanOptions ) : Span {
131
+ export function startInactiveSpan ( context : StartSpanOptions ) : Span {
148
132
const acs = getAcs ( ) ;
149
133
if ( acs . startInactiveSpan ) {
150
- return acs . startInactiveSpan ( options ) ;
134
+ return acs . startInactiveSpan ( context ) ;
151
135
}
152
136
153
- const spanArguments = parseSentrySpanArguments ( options ) ;
154
- const { forceTransaction, parentSpan : customParentSpan } = options ;
137
+ const spanContext = normalizeContext ( context ) ;
155
138
156
- // If `options.scope` is defined, we use this as as a wrapper,
157
- // If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan`
158
- const wrapper = options . scope
159
- ? ( callback : ( ) => Span ) => withScope ( options . scope , callback )
160
- : customParentSpan
161
- ? ( callback : ( ) => Span ) => withActiveSpan ( customParentSpan , callback )
162
- : ( callback : ( ) => Span ) => callback ( ) ;
139
+ const scope = context . scope || getCurrentScope ( ) ;
140
+ const parentSpan = getParentSpan ( scope ) ;
163
141
164
- return wrapper ( ( ) => {
165
- const scope = getCurrentScope ( ) ;
166
- const parentSpan = getParentSpan ( scope ) ;
142
+ const shouldSkipSpan = context . onlyIfParent && ! parentSpan ;
167
143
168
- const shouldSkipSpan = options . onlyIfParent && ! parentSpan ;
169
-
170
- if ( shouldSkipSpan ) {
171
- return new SentryNonRecordingSpan ( ) ;
172
- }
144
+ if ( shouldSkipSpan ) {
145
+ return new SentryNonRecordingSpan ( ) ;
146
+ }
173
147
174
- return createChildOrRootSpan ( {
175
- parentSpan,
176
- spanArguments,
177
- forceTransaction,
178
- scope,
179
- } ) ;
148
+ return createChildOrRootSpan ( {
149
+ parentSpan,
150
+ spanContext,
151
+ forceTransaction : context . forceTransaction ,
152
+ scope,
180
153
} ) ;
181
154
}
182
155
@@ -266,12 +239,12 @@ export function startNewTrace<T>(callback: () => T): T {
266
239
267
240
function createChildOrRootSpan ( {
268
241
parentSpan,
269
- spanArguments ,
242
+ spanContext ,
270
243
forceTransaction,
271
244
scope,
272
245
} : {
273
246
parentSpan : SentrySpan | undefined ;
274
- spanArguments : SentrySpanArguments ;
247
+ spanContext : SentrySpanArguments ;
275
248
forceTransaction ?: boolean ;
276
249
scope : Scope ;
277
250
} ) : Span {
@@ -283,7 +256,7 @@ function createChildOrRootSpan({
283
256
284
257
let span : Span ;
285
258
if ( parentSpan && ! forceTransaction ) {
286
- span = _startChildSpan ( parentSpan , scope , spanArguments ) ;
259
+ span = _startChildSpan ( parentSpan , scope , spanContext ) ;
287
260
addChildSpanToSpan ( parentSpan , span ) ;
288
261
} else if ( parentSpan ) {
289
262
// If we forced a transaction but have a parent span, make sure to continue from the parent span, not the scope
@@ -295,7 +268,7 @@ function createChildOrRootSpan({
295
268
{
296
269
traceId,
297
270
parentSpanId,
298
- ...spanArguments ,
271
+ ...spanContext ,
299
272
} ,
300
273
scope ,
301
274
parentSampled ,
@@ -317,7 +290,7 @@ function createChildOrRootSpan({
317
290
{
318
291
traceId,
319
292
parentSpanId,
320
- ...spanArguments ,
293
+ ...spanContext ,
321
294
} ,
322
295
scope ,
323
296
parentSampled ,
@@ -339,17 +312,19 @@ function createChildOrRootSpan({
339
312
* This converts StartSpanOptions to SentrySpanArguments.
340
313
* For the most part (for now) we accept the same options,
341
314
* but some of them need to be transformed.
315
+ *
316
+ * Eventually the StartSpanOptions will be more aligned with OpenTelemetry.
342
317
*/
343
- function parseSentrySpanArguments ( options : StartSpanOptions ) : SentrySpanArguments {
344
- const exp = options . experimental || { } ;
318
+ function normalizeContext ( context : StartSpanOptions ) : SentrySpanArguments {
319
+ const exp = context . experimental || { } ;
345
320
const initialCtx : SentrySpanArguments = {
346
321
isStandalone : exp . standalone ,
347
- ...options ,
322
+ ...context ,
348
323
} ;
349
324
350
- if ( options . startTime ) {
325
+ if ( context . startTime ) {
351
326
const ctx : SentrySpanArguments & { startTime ?: SpanTimeInput } = { ...initialCtx } ;
352
- ctx . startTimestamp = spanTimeInputToSeconds ( options . startTime ) ;
327
+ ctx . startTimestamp = spanTimeInputToSeconds ( context . startTime ) ;
353
328
delete ctx . startTime ;
354
329
return ctx ;
355
330
}
@@ -444,11 +419,3 @@ function getParentSpan(scope: Scope): SentrySpan | undefined {
444
419
445
420
return span ;
446
421
}
447
-
448
- function getActiveSpanWrapper < T > ( parentSpan ?: Span ) : ( callback : ( ) => T ) => T {
449
- return parentSpan
450
- ? ( callback : ( ) => T ) => {
451
- return withActiveSpan ( parentSpan , callback ) ;
452
- }
453
- : ( callback : ( ) => T ) => callback ( ) ;
454
- }
0 commit comments