@@ -10,46 +10,14 @@ import { SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY, SENTRY_TRACE_PARENT_CONTEXT_KEY }
10
10
import { isSentryRequestSpan } from './utils/isSentryRequest' ;
11
11
import { mapOtelStatus } from './utils/mapOtelStatus' ;
12
12
import { parseSpanDescription } from './utils/parseOtelSpanDescription' ;
13
-
14
- interface SpanProcessorOptions {
15
- /**
16
- * By default, if a span is started and we cannot find a Sentry parent span for it,
17
- * even if the OTEL span has a parent reference, we will still create the Sentry span as a root span.
18
- *
19
- * While this is more tolerant of errors, it means that the generated Spans in Sentry may have an incorrect hierarchy.
20
- *
21
- * When opting into strict span parent handling, we will discard any Spans where we can't find the corresponding parent.
22
- * This also requires that we defer clearing of references to the point where the root span is finished -
23
- * as sometimes these are not fired in correct order, leading to spans being dropped.
24
- *
25
- * Note that enabling this is the more correct option
26
- * and will probably eventually become the default in a future version.
27
- */
28
- strictSpanParentHandling : boolean ;
29
- }
30
-
31
- export const SENTRY_SPAN_PROCESSOR_MAP : Map < string , SentrySpan > = new Map < string , SentrySpan > ( ) ;
32
-
33
- // A map of a sentry span ID to a list of otel span IDs
34
- // When the sentry span is finished, clear all references of the given otel spans
35
- export const SCHEDULE_TO_CLEAR : Map < string , string [ ] > = new Map < string , string [ ] > ( ) ;
36
-
37
- /** Get a Sentry span for an otel span ID. */
38
- export function getSentrySpan ( otelSpanId : string ) : SentrySpan | undefined {
39
- return SENTRY_SPAN_PROCESSOR_MAP . get ( otelSpanId ) ;
40
- }
13
+ import { clearSpan , getSentrySpan , setSentrySpan } from './utils/spanMap' ;
41
14
42
15
/**
43
16
* Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via
44
17
* the Sentry SDK.
45
18
*/
46
19
export class SentrySpanProcessor implements OtelSpanProcessor {
47
- private _strictSpanParentHandling : boolean ;
48
-
49
- public constructor ( { strictSpanParentHandling } : Partial < SpanProcessorOptions > = { } ) {
50
- // Default to false
51
- this . _strictSpanParentHandling = ! ! strictSpanParentHandling ;
52
-
20
+ public constructor ( ) {
53
21
addTracingExtensions ( ) ;
54
22
55
23
addGlobalEventProcessor ( event => {
@@ -83,7 +51,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
83
51
84
52
// Otel supports having multiple non-nested spans at the same time
85
53
// so we cannot use hub.getSpan(), as we cannot rely on this being on the current span
86
- const sentryParentSpan = otelParentSpanId && SENTRY_SPAN_PROCESSOR_MAP . get ( otelParentSpanId ) ;
54
+ const sentryParentSpan = otelParentSpanId && getSentrySpan ( otelParentSpanId ) ;
87
55
88
56
if ( sentryParentSpan ) {
89
57
const sentryChildSpan = sentryParentSpan . startChild ( {
@@ -93,7 +61,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
93
61
spanId : otelSpanId ,
94
62
} ) ;
95
63
96
- SENTRY_SPAN_PROCESSOR_MAP . set ( otelSpanId , sentryChildSpan ) ;
64
+ setSentrySpan ( otelSpanId , sentryChildSpan ) ;
97
65
} else {
98
66
const traceCtx = getTraceData ( otelSpan , parentContext ) ;
99
67
const transaction = getCurrentHub ( ) . startTransaction ( {
@@ -104,11 +72,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
104
72
spanId : otelSpanId ,
105
73
} ) ;
106
74
107
- SENTRY_SPAN_PROCESSOR_MAP . set ( otelSpanId , transaction ) ;
108
-
109
- if ( this . _strictSpanParentHandling ) {
110
- SCHEDULE_TO_CLEAR . set ( transaction . spanId , [ ] ) ;
111
- }
75
+ setSentrySpan ( otelSpanId , transaction ) ;
112
76
}
113
77
}
114
78
@@ -122,7 +86,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
122
86
if ( ! sentrySpan ) {
123
87
__DEBUG_BUILD__ &&
124
88
logger . error ( `SentrySpanProcessor could not find span with OTEL-spanId ${ otelSpanId } to finish.` ) ;
125
- this . _clearSpan ( otelSpanId ) ;
89
+ clearSpan ( otelSpanId ) ;
126
90
return ;
127
91
}
128
92
@@ -131,7 +95,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
131
95
// leading to an infinite loop.
132
96
// In this case, we do not want to finish the span, in order to avoid sending it to Sentry
133
97
if ( isSentryRequestSpan ( otelSpan ) ) {
134
- this . _clearSpan ( otelSpanId ) ;
98
+ clearSpan ( otelSpanId ) ;
135
99
return ;
136
100
}
137
101
@@ -141,7 +105,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
141
105
client && client . emit && client ?. emit ( 'otelSpanEnd' , otelSpan , mutableOptions ) ;
142
106
143
107
if ( mutableOptions . drop ) {
144
- this . _clearSpan ( otelSpanId ) ;
108
+ clearSpan ( otelSpanId ) ;
145
109
return ;
146
110
}
147
111
@@ -194,7 +158,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
194
158
195
159
sentrySpan . finish ( convertOtelTimeToSeconds ( otelSpan . endTime ) ) ;
196
160
197
- this . _clearSpan ( otelSpanId ) ;
161
+ clearSpan ( otelSpanId ) ;
198
162
}
199
163
200
164
/**
@@ -214,17 +178,6 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
214
178
}
215
179
return Promise . resolve ( ) ;
216
180
}
217
-
218
- /**
219
- * Clear all references for a given OTEL span.
220
- */
221
- private _clearSpan ( otelSpanId : string ) : void {
222
- if ( this . _strictSpanParentHandling ) {
223
- scheduleToClear ( otelSpanId ) ;
224
- } else {
225
- clearSpan ( otelSpanId ) ;
226
- }
227
- }
228
181
}
229
182
230
183
function getTraceData ( otelSpan : OtelSpan , parentContext : Context ) : Partial < TransactionContext > {
@@ -300,50 +253,3 @@ function updateTransactionWithOtelData(transaction: Transaction, otelSpan: OtelS
300
253
function convertOtelTimeToSeconds ( [ seconds , nano ] : [ number , number ] ) : number {
301
254
return seconds + nano / 1_000_000_000 ;
302
255
}
303
-
304
- function scheduleToClear ( otelSpanId : string ) : void {
305
- const span = SENTRY_SPAN_PROCESSOR_MAP . get ( otelSpanId ) ;
306
-
307
- if ( ! span ) {
308
- // hmm, something is fishy here, but abort...
309
- // But to be sure we still try to delete the SCHEDULE_TO_CLEAR, to avoid leaks
310
- SCHEDULE_TO_CLEAR . delete ( otelSpanId ) ;
311
- return ;
312
- }
313
-
314
- const sentrySpanId = span . spanId ;
315
-
316
- // This is the root, clear all that have been scheduled
317
- if ( spanIsRoot ( span ) || ! span . transaction ) {
318
- const toClear = SCHEDULE_TO_CLEAR . get ( sentrySpanId ) || [ ] ;
319
- toClear . push ( otelSpanId ) ;
320
-
321
- toClear . forEach ( otelSpanIdToClear => clearSpan ( otelSpanIdToClear ) ) ;
322
- SCHEDULE_TO_CLEAR . delete ( sentrySpanId ) ;
323
- return ;
324
- }
325
-
326
- // Clear when root span is cleared
327
- const root = span . transaction ;
328
- const rootSentrySpanId = root . spanId ;
329
-
330
- const toClear = SCHEDULE_TO_CLEAR . get ( rootSentrySpanId ) ;
331
-
332
- // If this does not exist, it means we prob. already cleaned it up before
333
- // So we ignore the parent and just clean this span up right now
334
- if ( ! toClear ) {
335
- clearSpan ( otelSpanId ) ;
336
- return ;
337
- }
338
-
339
- toClear . push ( otelSpanId ) ;
340
- }
341
-
342
- function spanIsRoot ( span : SentrySpan ) : span is Transaction {
343
- return span . transaction === span ;
344
- }
345
-
346
- // make sure to remove references in maps, to ensure this can be GCed
347
- function clearSpan ( otelSpanId : string ) : void {
348
- SENTRY_SPAN_PROCESSOR_MAP . delete ( otelSpanId ) ;
349
- }
0 commit comments