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' ;
23import { context } from '@opentelemetry/api' ;
34import { SpanStatusCode , trace } from '@opentelemetry/api' ;
4- import { suppressTracing } from '@opentelemetry/core' ;
5+ import { TraceState , suppressTracing } from '@opentelemetry/core' ;
56import { SDK_VERSION , getClient , getCurrentScope , handleCallbackErrors } from '@sentry/core' ;
67import type { Client , Scope } from '@sentry/types' ;
8+ import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils' ;
9+ import { SENTRY_TRACE_STATE_DSC } from './constants' ;
710
811import { InternalSentrySemanticAttributes } from './semanticAttributes' ;
912import type { OpenTelemetryClient , OpenTelemetrySpanContext } from './types' ;
1013import { getContextFromScope } from './utils/contextData' ;
14+ import { getDynamicSamplingContextFromSpan } from './utils/dynamicSamplingContext' ;
15+ import { getRootSpan } from './utils/getActiveSpan' ;
1116import { setSpanMetadata } from './utils/spanData' ;
1217
1318/**
@@ -24,7 +29,7 @@ export function startSpan<T>(options: OpenTelemetrySpanContext, callback: (span:
2429
2530 const { name } = options ;
2631
27- const activeCtx = getContext ( options . scope ) ;
32+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
2833 const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
2934 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
3035
@@ -57,7 +62,7 @@ export function startSpanManual<T>(options: OpenTelemetrySpanContext, callback:
5762
5863 const { name } = options ;
5964
60- const activeCtx = getContext ( options . scope ) ;
65+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
6166 const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
6267 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
6368
@@ -95,7 +100,7 @@ export function startInactiveSpan(options: OpenTelemetrySpanContext): Span {
95100
96101 const { name } = options ;
97102
98- const activeCtx = getContext ( options . scope ) ;
103+ const activeCtx = getContext ( options . scope , options . forceTransaction ) ;
99104 const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
100105 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
101106
@@ -166,7 +171,50 @@ function ensureTimestampInMilliseconds(timestamp: number): number {
166171 return isMs ? timestamp * 1000 : timestamp ;
167172}
168173
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 {
170218 if ( scope ) {
171219 const ctx = getContextFromScope ( scope ) ;
172220 if ( ctx ) {
0 commit comments