1
- import type { Context , Span , SpanOptions , Tracer } from '@opentelemetry/api' ;
1
+ import type { Context , Span , SpanContext , SpanOptions , Tracer } from '@opentelemetry/api' ;
2
+ import { TraceFlags } from '@opentelemetry/api' ;
2
3
import { context } from '@opentelemetry/api' ;
3
4
import { SpanStatusCode , trace } from '@opentelemetry/api' ;
4
- import { suppressTracing } from '@opentelemetry/core' ;
5
+ import { TraceState , suppressTracing } from '@opentelemetry/core' ;
5
6
import { SDK_VERSION , getClient , getCurrentScope , handleCallbackErrors } from '@sentry/core' ;
6
7
import type { Client , Scope } from '@sentry/types' ;
8
+ import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils' ;
9
+ import { SENTRY_TRACE_STATE_DSC } from './constants' ;
7
10
8
11
import { InternalSentrySemanticAttributes } from './semanticAttributes' ;
9
12
import type { OpenTelemetryClient , OpenTelemetrySpanContext } from './types' ;
10
13
import { getContextFromScope } from './utils/contextData' ;
14
+ import { getDynamicSamplingContextFromSpan } from './utils/dynamicSamplingContext' ;
15
+ import { getRootSpan } from './utils/getActiveSpan' ;
11
16
import { setSpanMetadata } from './utils/spanData' ;
12
17
13
18
/**
@@ -24,7 +29,7 @@ export function startSpan<T>(options: OpenTelemetrySpanContext, callback: (span:
24
29
25
30
const { name } = options ;
26
31
27
- const activeCtx = getContext ( options . scope ) ;
32
+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
28
33
const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
29
34
const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
30
35
@@ -57,7 +62,7 @@ export function startSpanManual<T>(options: OpenTelemetrySpanContext, callback:
57
62
58
63
const { name } = options ;
59
64
60
- const activeCtx = getContext ( options . scope ) ;
65
+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
61
66
const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
62
67
const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
63
68
@@ -95,7 +100,7 @@ export function startInactiveSpan(options: OpenTelemetrySpanContext): Span {
95
100
96
101
const { name } = options ;
97
102
98
- const activeCtx = getContext ( options . scope ) ;
103
+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
99
104
const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
100
105
const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
101
106
@@ -166,7 +171,50 @@ function ensureTimestampInMilliseconds(timestamp: number): number {
166
171
return isMs ? timestamp * 1000 : timestamp ;
167
172
}
168
173
169
- function getContext ( scope ?: Scope ) : Context {
174
+ function getContext ( scope : Scope | undefined , forceTransaction : boolean | undefined ) : Context {
175
+ const ctx = getContextForScope ( scope ) ;
176
+
177
+ if ( ! forceTransaction ) {
178
+ return ctx ;
179
+ }
180
+
181
+ // Else we need to "fix" the context to have no parent span
182
+ const parentSpan = trace . getSpan ( ctx ) ;
183
+
184
+ // If there is no parent span, all good, nothing to do!
185
+ if ( ! parentSpan ) {
186
+ return ctx ;
187
+ }
188
+
189
+ // Else, we need to do two things:
190
+ // 1. Unset the parent span from the context, so we'll create a new root span
191
+ // 2. Ensure the propagation context is correct, so we'll continue from the parent span
192
+ const ctxWithoutSpan = trace . deleteSpan ( ctx ) ;
193
+
194
+ const { spanId, traceId, traceFlags } = parentSpan . spanContext ( ) ;
195
+ // eslint-disable-next-line no-bitwise
196
+ const sampled = Boolean ( traceFlags & TraceFlags . SAMPLED ) ;
197
+
198
+ const rootSpan = getRootSpan ( parentSpan ) ;
199
+ const dsc = getDynamicSamplingContextFromSpan ( rootSpan ) ;
200
+ const dscString = dynamicSamplingContextToSentryBaggageHeader ( dsc ) ;
201
+
202
+ const traceState = dscString ? new TraceState ( ) . set ( SENTRY_TRACE_STATE_DSC , dscString ) : undefined ;
203
+
204
+ const spanContext : SpanContext = {
205
+ traceId,
206
+ spanId,
207
+ isRemote : true ,
208
+ traceFlags : sampled ? TraceFlags . SAMPLED : TraceFlags . NONE ,
209
+ traceState,
210
+ } ;
211
+
212
+ const ctxWithSpanContext = trace . setSpanContext ( ctxWithoutSpan , spanContext ) ;
213
+
214
+ return ctxWithSpanContext ;
215
+ }
216
+
217
+ function getContextForScope ( scope ?: Scope ) : Context {
170
218
if ( scope ) {
171
219
const ctx = getContextFromScope ( scope ) ;
172
220
if ( ctx ) {
0 commit comments