({
parentSpan,
spanArguments,
forceTransaction,
scope,
}: {
parentSpan: SentrySpan | undefined;
spanArguments: SentrySpanArguments;
forceTransaction?: boolean;
scope: Scope;
})
| 348 | } |
| 349 | |
| 350 | function createChildOrRootSpan({ |
| 351 | parentSpan, |
| 352 | spanArguments, |
| 353 | forceTransaction, |
| 354 | scope, |
| 355 | }: { |
| 356 | parentSpan: SentrySpan | undefined; |
| 357 | spanArguments: SentrySpanArguments; |
| 358 | forceTransaction?: boolean; |
| 359 | scope: Scope; |
| 360 | }): Span { |
| 361 | const isolationScope = getIsolationScope(); |
| 362 | |
| 363 | if (!hasSpansEnabled()) { |
| 364 | const scopePropagationContext = { ...isolationScope.getPropagationContext(), ...scope.getPropagationContext() }; |
| 365 | const traceId = parentSpan ? parentSpan.spanContext().traceId : scopePropagationContext.traceId; |
| 366 | |
| 367 | // The placeholder is a thin marker; it carries no sampling decision or DSC. Both are read from |
| 368 | // the scope: the sampling decision in `getTraceData`, the DSC in `getDynamicSamplingContextFromSpan`. |
| 369 | const span = new SentryNonRecordingSpan({ traceId }); |
| 370 | |
| 371 | // Nested placeholders link to their parent so `getRootSpan` resolves to the root placeholder, |
| 372 | // whose captured scope is the source of truth. Root/forced placeholders are their own root. |
| 373 | if (parentSpan && !forceTransaction) { |
| 374 | addChildSpanToSpan(parentSpan, span); |
| 375 | } |
| 376 | |
| 377 | // Capture scopes so consumers (e.g. SentryTraceProvider) can read them and so the DSC can be |
| 378 | // resolved from the scope by `getDynamicSamplingContextFromSpan`. Consistent with `startIdleSpan`. |
| 379 | setCapturedScopesOnSpan(span, scope, isolationScope); |
| 380 | |
| 381 | return span; |
| 382 | } |
| 383 | |
| 384 | const client = getClient(); |
| 385 | if (_shouldIgnoreStreamedSpan(client, spanArguments)) { |
| 386 | if (!isTracingSuppressed(scope)) { |
| 387 | // if tracing is actively suppressed (Sentry.suppressTracing(...)), |
| 388 | // we don't want to record a client outcome for the ignored span |
| 389 | client?.recordDroppedEvent('ignored', 'span'); |
| 390 | } |
| 391 | |
| 392 | const ignoredSpan = new SentryNonRecordingSpan({ |
| 393 | dropReason: 'ignored', |
| 394 | traceId: parentSpan?.spanContext().traceId ?? scope.getPropagationContext().traceId, |
| 395 | }); |
| 396 | setCapturedScopesOnSpan(ignoredSpan, scope, isolationScope); |
| 397 | |
| 398 | return ignoredSpan; |
| 399 | } |
| 400 | |
| 401 | let span: Span; |
| 402 | if (parentSpan && !forceTransaction) { |
| 403 | span = _startChildSpan(parentSpan, scope, spanArguments, isolationScope); |
| 404 | addChildSpanToSpan(parentSpan, span); |
| 405 | } else if (parentSpan) { |
| 406 | // If we forced a transaction but have a parent span, make sure to continue from the parent span, not the scope |
| 407 | const dsc = getDynamicSamplingContextFromSpan(parentSpan); |
no test coverage detected