1- import type { Span , Tracer } from '@opentelemetry/api' ;
1+ import type { Context , Span , SpanOptions , Tracer } from '@opentelemetry/api' ;
22import { context } from '@opentelemetry/api' ;
33import { SpanStatusCode , trace } from '@opentelemetry/api' ;
44import { suppressTracing } from '@opentelemetry/core' ;
@@ -7,6 +7,7 @@ import type { Client, Scope } from '@sentry/types';
77
88import { InternalSentrySemanticAttributes } from './semanticAttributes' ;
99import type { OpenTelemetryClient , OpenTelemetrySpanContext } from './types' ;
10+ import { getContextFromScope } from './utils/contextData' ;
1011import { setSpanMetadata } from './utils/spanData' ;
1112
1213/**
@@ -18,17 +19,19 @@ import { setSpanMetadata } from './utils/spanData';
1819 *
1920 * Note that you'll always get a span passed to the callback, it may just be a NonRecordingSpan if the span is not sampled.
2021 */
21- export function startSpan < T > ( spanContext : OpenTelemetrySpanContext , callback : ( span : Span ) => T ) : T {
22+ export function startSpan < T > ( options : OpenTelemetrySpanContext , callback : ( span : Span ) => T ) : T {
2223 const tracer = getTracer ( ) ;
2324
24- const { name } = spanContext ;
25+ const { name } = options ;
2526
26- const activeCtx = context . active ( ) ;
27- const shouldSkipSpan = spanContext . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
27+ const activeCtx = getContext ( options . scope ) ;
28+ const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
2829 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
2930
31+ const spanContext = getSpanContext ( options ) ;
32+
3033 return tracer . startActiveSpan ( name , spanContext , ctx , span => {
31- _applySentryAttributesToSpan ( span , spanContext ) ;
34+ _applySentryAttributesToSpan ( span , options ) ;
3235
3336 return handleCallbackErrors (
3437 ( ) => callback ( span ) ,
@@ -49,17 +52,19 @@ export function startSpan<T>(spanContext: OpenTelemetrySpanContext, callback: (s
4952 *
5053 * Note that you'll always get a span passed to the callback, it may just be a NonRecordingSpan if the span is not sampled.
5154 */
52- export function startSpanManual < T > ( spanContext : OpenTelemetrySpanContext , callback : ( span : Span ) => T ) : T {
55+ export function startSpanManual < T > ( options : OpenTelemetrySpanContext , callback : ( span : Span ) => T ) : T {
5356 const tracer = getTracer ( ) ;
5457
55- const { name } = spanContext ;
58+ const { name } = options ;
5659
57- const activeCtx = context . active ( ) ;
58- const shouldSkipSpan = spanContext . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
60+ const activeCtx = getContext ( options . scope ) ;
61+ const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
5962 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
6063
64+ const spanContext = getSpanContext ( options ) ;
65+
6166 return tracer . startActiveSpan ( name , spanContext , ctx , span => {
62- _applySentryAttributesToSpan ( span , spanContext ) ;
67+ _applySentryAttributesToSpan ( span , options ) ;
6368
6469 return handleCallbackErrors (
6570 ( ) => callback ( span ) ,
@@ -85,18 +90,20 @@ export const startActiveSpan = startSpan;
8590 * or you didn't set `tracesSampleRate` or `tracesSampler`, this function will not generate spans
8691 * and the `span` returned from the callback will be undefined.
8792 */
88- export function startInactiveSpan ( spanContext : OpenTelemetrySpanContext ) : Span {
93+ export function startInactiveSpan ( options : OpenTelemetrySpanContext ) : Span {
8994 const tracer = getTracer ( ) ;
9095
91- const { name } = spanContext ;
96+ const { name } = options ;
9297
93- const activeCtx = context . active ( ) ;
94- const shouldSkipSpan = spanContext . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
98+ const activeCtx = getContext ( options . scope ) ;
99+ const shouldSkipSpan = options . onlyIfParent && ! trace . getSpan ( activeCtx ) ;
95100 const ctx = shouldSkipSpan ? suppressTracing ( activeCtx ) : activeCtx ;
96101
102+ const spanContext = getSpanContext ( options ) ;
103+
97104 const span = tracer . startSpan ( name , spanContext , ctx ) ;
98105
99- _applySentryAttributesToSpan ( span , spanContext ) ;
106+ _applySentryAttributesToSpan ( span , options ) ;
100107
101108 return span ;
102109}
@@ -120,8 +127,9 @@ function getTracer(): Tracer {
120127 return ( client && client . tracer ) || trace . getTracer ( '@sentry/opentelemetry' , SDK_VERSION ) ;
121128}
122129
123- function _applySentryAttributesToSpan ( span : Span , spanContext : OpenTelemetrySpanContext ) : void {
124- const { origin, op, source, metadata } = spanContext ;
130+ function _applySentryAttributesToSpan ( span : Span , options : OpenTelemetrySpanContext ) : void {
131+ // eslint-disable-next-line deprecation/deprecation
132+ const { origin, op, source, metadata } = options ;
125133
126134 if ( origin ) {
127135 span . setAttribute ( InternalSentrySemanticAttributes . ORIGIN , origin ) ;
@@ -139,3 +147,32 @@ function _applySentryAttributesToSpan(span: Span, spanContext: OpenTelemetrySpan
139147 setSpanMetadata ( span , metadata ) ;
140148 }
141149}
150+
151+ function getSpanContext ( options : OpenTelemetrySpanContext ) : SpanOptions {
152+ const { startTime, attributes, kind } = options ;
153+
154+ // OTEL expects timestamps in ms, not seconds
155+ const fixedStartTime = typeof startTime === 'number' ? ensureTimestampInMilliseconds ( startTime ) : startTime ;
156+
157+ return {
158+ attributes,
159+ kind,
160+ startTime : fixedStartTime ,
161+ } ;
162+ }
163+
164+ function ensureTimestampInMilliseconds ( timestamp : number ) : number {
165+ const isMs = timestamp < 9999999999 ;
166+ return isMs ? timestamp * 1000 : timestamp ;
167+ }
168+
169+ function getContext ( scope ?: Scope ) : Context {
170+ if ( scope ) {
171+ const ctx = getContextFromScope ( scope ) ;
172+ if ( ctx ) {
173+ return ctx ;
174+ }
175+ }
176+
177+ return context . active ( ) ;
178+ }
0 commit comments