* Internal helper for starting spans and manual spans. See startSpan and startSpanManual for the public APIs. * @param options - The span context options * @param callback - The callback to execute with the span * @param autoEnd - Whether to automatically end the span after the ca
(options: OpenTelemetrySpanContext, callback: (span: Span) => T, autoEnd: boolean)
| 39 | * @param autoEnd - Whether to automatically end the span after the callback completes |
| 40 | */ |
| 41 | function _startSpan<T>(options: OpenTelemetrySpanContext, callback: (span: Span) => T, autoEnd: boolean): T { |
| 42 | const tracer = getTracer(); |
| 43 | |
| 44 | const { name, parentSpan: customParentSpan } = options; |
| 45 | |
| 46 | // If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan` |
| 47 | const wrapper = getActiveSpanWrapper<T>(customParentSpan); |
| 48 | |
| 49 | return wrapper(() => { |
| 50 | const activeCtx = getContext(options.scope, options.forceTransaction); |
| 51 | const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx); |
| 52 | const ctx = missingRequiredParent ? suppressTracing(activeCtx) : activeCtx; |
| 53 | |
| 54 | if (missingRequiredParent) { |
| 55 | getClient()?.recordDroppedEvent('no_parent_span', 'span'); |
| 56 | } |
| 57 | |
| 58 | const spanOptions = getSpanOptions(options); |
| 59 | |
| 60 | // If spans are not enabled, ensure we suppress tracing for the span creation |
| 61 | // but preserve the original context for the callback execution |
| 62 | // This ensures that we don't create spans when tracing is disabled which |
| 63 | // would otherwise be a problem for users that don't enable tracing but use |
| 64 | // custom OpenTelemetry setups. |
| 65 | if (!hasSpansEnabled()) { |
| 66 | const suppressedCtx = isTracingSuppressed(ctx) ? ctx : suppressTracing(ctx); |
| 67 | |
| 68 | return context.with(suppressedCtx, () => { |
| 69 | return tracer.startActiveSpan(name, spanOptions, suppressedCtx, span => { |
| 70 | patchSpanEnd(span); |
| 71 | // Restore the original unsuppressed context for the callback execution |
| 72 | // so that custom OpenTelemetry spans maintain the correct context. |
| 73 | // We use activeCtx (not ctx) because ctx may be suppressed when onlyIfParent is true |
| 74 | // and no parent span exists. Using activeCtx ensures custom OTel spans are never |
| 75 | // inadvertently suppressed. |
| 76 | return context.with(activeCtx, () => { |
| 77 | return handleCallbackErrors( |
| 78 | () => callback(span), |
| 79 | () => { |
| 80 | // Only set the span status to ERROR when there wasn't any status set before, in order to avoid stomping useful span statuses |
| 81 | if (spanToJSON(span).status === undefined) { |
| 82 | span.setStatus({ code: SpanStatusCode.ERROR }); |
| 83 | } |
| 84 | }, |
| 85 | autoEnd ? () => span.end() : undefined, |
| 86 | ); |
| 87 | }); |
| 88 | }); |
| 89 | }); |
| 90 | } |
| 91 | |
| 92 | return tracer.startActiveSpan(name, spanOptions, ctx, span => { |
| 93 | patchSpanEnd(span); |
| 94 | return handleCallbackErrors( |
| 95 | () => callback(span), |
| 96 | () => { |
| 97 | // Only set the span status to ERROR when there wasn't any status set before, in order to avoid stomping useful span statuses |
| 98 | if (spanToJSON(span).status === undefined) { |
no test coverage detected