From 61be37b6aad87d1a71d924208f9c23b7b42e0ba5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 11:56:46 +0100 Subject: [PATCH 1/7] wip --- packages/core/src/tracing/span.ts | 1 + packages/core/src/utils/spanUtils.ts | 12 +++++++ .../core/test/lib/utils/spanUtils.test.ts | 35 ++++++++++++++++++- packages/types/src/span.ts | 1 + 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/span.ts b/packages/core/src/tracing/span.ts index c3cd71e87699..c22f6a06bd5f 100644 --- a/packages/core/src/tracing/span.ts +++ b/packages/core/src/tracing/span.ts @@ -105,6 +105,7 @@ export class Span implements SpanInterface { /** * @inheritDoc + * @deprecated Use top level `Sentry.getRootSpan()` instead */ public transaction?: Transaction; diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index cbf8ce6d7f5b..0561d7dde5a8 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -107,3 +107,15 @@ export function spanIsSampled(span: Span): boolean { // eslint-disable-next-line no-bitwise return Boolean(traceFlags & TRACE_FLAG_SAMPLED); } + +/** + * Returns the root span of a given span. + * + * As long as we use `Transaction`s internally, the returned root span + * will be a `Transaction` but be aware that this might change in the future. + * + * If the given span has no root span or transaction, `undefined` is returned. + */ +export function getRootSpan(span: Span): Span | undefined { + return span.transaction; +} diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts index e521df7c2dc9..bddfe11fdcf4 100644 --- a/packages/core/test/lib/utils/spanUtils.test.ts +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -1,6 +1,7 @@ import { TRACEPARENT_REGEXP, timestampInSeconds } from '@sentry/utils'; +import { Transaction } from '../../../build/types'; import { Span, spanToTraceHeader } from '../../../src'; -import { spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../../../src/utils/spanUtils'; +import { getRootSpan, spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../../../src/utils/spanUtils'; describe('spanToTraceHeader', () => { test('simple', () => { @@ -127,3 +128,35 @@ describe('spanIsSampled', () => { expect(spanIsSampled(span)).toBe(false); }); }); + +describe('getRootSpan', () => { + it('returns the root span of a span (Span)', () => { + const root = new Span({ name: 'test' }); + // @ts-expect-error this is highly illegal and shouldn't happen IRL + root.transaction = root; + + // eslint-disable-next-line deprecation/deprecation + const childSpan = root.startChild({ name: 'child' }); + expect(getRootSpan(childSpan)).toBe(root); + }); + + it('returns the root span of a span (Transaction)', () => { + const root = new Transaction({ name: 'test' }); + + // eslint-disable-next-line deprecation/deprecation + const childSpan = root.startChild({ name: 'child' }); + expect(getRootSpan(childSpan)).toBe(root); + }); + + it('returns the span itself if it is a root span', () => { + const span = new Transaction({ name: 'test' }); + + expect(getRootSpan(span)).toBe(span); + }); + + it('returns undefined if span has no root span', () => { + const span = new Span({ name: 'test' }); + + expect(getRootSpan(span)).toBe(undefined); + }); +}); diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index 662cfb57a155..164e7c47f741 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -216,6 +216,7 @@ export interface Span extends SpanContext { /** * The transaction containing this span + * @deprecated Use top level `Sentry.getRootSpan()` instead */ transaction?: Transaction; From a0efb9bd2ff7d6e653252b7871876465ca34c86a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 13:15:32 +0100 Subject: [PATCH 2/7] replace usages/add eslint ignores in core --- packages/core/src/scope.ts | 3 +++ packages/core/src/server-runtime-client.ts | 4 ++-- .../core/src/tracing/dynamicSamplingContext.ts | 7 +++---- packages/core/src/tracing/span.ts | 16 +++++++++++----- packages/core/src/tracing/transaction.ts | 2 ++ packages/core/src/utils/applyScopeDataToEvent.ts | 8 ++++---- packages/core/src/utils/spanUtils.ts | 2 ++ .../lib/tracing/dynamicSamplingContext.test.ts | 11 +++++------ packages/core/test/lib/utils/spanUtils.test.ts | 6 ++++-- 9 files changed, 36 insertions(+), 23 deletions(-) diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 255af68dd48e..fe682bf02661 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -334,6 +334,9 @@ export class Scope implements ScopeInterface { // Often, this span (if it exists at all) will be a transaction, but it's not guaranteed to be. Regardless, it will // have a pointer to the currently-active transaction. const span = this._span; + // Cannot replace with getRootSpan because getRootSpan returns a span, not a transaction + // Also, this method will be removed anyway. + // eslint-disable-next-line deprecation/deprecation return span && span.transaction; } diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index 00f23be2dc0a..785ad0c4f94b 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -26,7 +26,7 @@ import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, } from './tracing'; -import { spanToTraceContext } from './utils/spanUtils'; +import { getRootSpan, spanToTraceContext } from './utils/spanUtils'; export interface ServerRuntimeClientOptions extends ClientOptions { platform?: string; @@ -262,7 +262,7 @@ export class ServerRuntimeClient< // eslint-disable-next-line deprecation/deprecation const span = scope.getSpan(); if (span) { - const samplingContext = span.transaction ? getDynamicSamplingContextFromSpan(span) : undefined; + const samplingContext = getRootSpan(span) ? getDynamicSamplingContextFromSpan(span) : undefined; return [samplingContext, spanToTraceContext(span)]; } diff --git a/packages/core/src/tracing/dynamicSamplingContext.ts b/packages/core/src/tracing/dynamicSamplingContext.ts index 318c232eb43b..e9b93ef96ac8 100644 --- a/packages/core/src/tracing/dynamicSamplingContext.ts +++ b/packages/core/src/tracing/dynamicSamplingContext.ts @@ -3,7 +3,7 @@ import { dropUndefinedKeys } from '@sentry/utils'; import { DEFAULT_ENVIRONMENT } from '../constants'; import { getClient, getCurrentScope } from '../exports'; -import { spanIsSampled, spanToJSON } from '../utils/spanUtils'; +import { getRootSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils'; /** * Creates a dynamic sampling context from a client. @@ -54,9 +54,8 @@ export function getDynamicSamplingContextFromSpan(span: Span): Readonly'; const nameStr = spanToJSON(childSpan).description || '< unknown name >'; - const idStr = childSpan.transaction.spanContext().spanId; + const idStr = rootSpan.spanContext().spanId; const logMessage = `[Tracing] Starting '${opStr}' span on transaction '${nameStr}' (${idStr}).`; logger.log(logMessage); @@ -417,11 +422,12 @@ export class Span implements SpanInterface { /** @inheritdoc */ public end(endTimestamp?: SpanTimeInput): void { + const rootSpan = getRootSpan(this); if ( DEBUG_BUILD && // Don't call this for transactions - this.transaction && - this.transaction.spanContext().spanId !== this._spanId + rootSpan && + rootSpan.spanContext().spanId !== this._spanId ) { const logMessage = this._logMessage; if (logMessage) { diff --git a/packages/core/src/tracing/transaction.ts b/packages/core/src/tracing/transaction.ts index 738e98e418a3..90d2c1ad4a92 100644 --- a/packages/core/src/tracing/transaction.ts +++ b/packages/core/src/tracing/transaction.ts @@ -66,6 +66,8 @@ export class Transaction extends SpanClass implements TransactionInterface { this._trimEnd = transactionContext.trimEnd; // this is because transactions are also spans, and spans have a transaction pointer + // TODO (v8): Replace this with another way to set the root span + // eslint-disable-next-line deprecation/deprecation this.transaction = this; // If Dynamic Sampling Context is provided during the creation of the transaction, we freeze it as it usually means diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index f834776ac9ba..57cc00053be9 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -1,7 +1,7 @@ import type { Breadcrumb, Event, PropagationContext, ScopeData, Span } from '@sentry/types'; import { arrayify } from '@sentry/utils'; import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext'; -import { spanToJSON, spanToTraceContext } from './spanUtils'; +import { getRootSpan, spanToJSON, spanToTraceContext } from './spanUtils'; /** * Applies data from the scope to the event and runs all event processors on it. @@ -174,13 +174,13 @@ function applySdkMetadataToEvent( function applySpanToEvent(event: Event, span: Span): void { event.contexts = { trace: spanToTraceContext(span), ...event.contexts }; - const transaction = span.transaction; - if (transaction) { + const rootSpan = getRootSpan(span); + if (rootSpan) { event.sdkProcessingMetadata = { dynamicSamplingContext: getDynamicSamplingContextFromSpan(span), ...event.sdkProcessingMetadata, }; - const transactionName = spanToJSON(transaction).description; + const transactionName = spanToJSON(rootSpan).description; if (transactionName) { event.tags = { transaction: transactionName, ...event.tags }; } diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index 0561d7dde5a8..1a80f3d29397 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -117,5 +117,7 @@ export function spanIsSampled(span: Span): boolean { * If the given span has no root span or transaction, `undefined` is returned. */ export function getRootSpan(span: Span): Span | undefined { + // TODO (v8): Remove this check and just return span + // eslint-disable-next-line deprecation/deprecation return span.transaction; } diff --git a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts index da8bf1595e21..91158d36ff01 100644 --- a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts +++ b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts @@ -20,7 +20,7 @@ describe('getDynamicSamplingContextFromSpan', () => { }); test('returns the DSC provided during transaction creation', () => { - // eslint-disable-next-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation -- using old API on purpose const transaction = new Transaction({ name: 'tx', metadata: { dynamicSamplingContext: { environment: 'myEnv' } }, @@ -68,7 +68,7 @@ describe('getDynamicSamplingContextFromSpan', () => { }); test('returns a new DSC, if no DSC was provided during transaction creation (via new Txn and deprecated metadata)', () => { - // eslint-disable-next-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation -- using old API on purpose const transaction = new Transaction({ name: 'tx', metadata: { @@ -92,7 +92,7 @@ describe('getDynamicSamplingContextFromSpan', () => { describe('Including transaction name in DSC', () => { test('is not included if transaction source is url', () => { - // eslint-disable-next-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation -- using old API on purpose const transaction = new Transaction({ name: 'tx', metadata: { @@ -109,8 +109,7 @@ describe('getDynamicSamplingContextFromSpan', () => { ['is included if transaction source is parameterized route/url', 'route'], ['is included if transaction source is a custom name', 'custom'], ])('%s', (_: string, source) => { - // eslint-disable-next-line deprecation/deprecation - const transaction = new Transaction({ + const transaction = startInactiveSpan({ name: 'tx', metadata: { ...(source && { source: source as TransactionSource }), @@ -120,7 +119,7 @@ describe('getDynamicSamplingContextFromSpan', () => { // Only setting the attribute manually because we're directly calling new Transaction() transaction?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source); - const dsc = getDynamicSamplingContextFromSpan(transaction); + const dsc = getDynamicSamplingContextFromSpan(transaction!); expect(dsc.transaction).toEqual('tx'); }); diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts index bddfe11fdcf4..116124130cf0 100644 --- a/packages/core/test/lib/utils/spanUtils.test.ts +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -1,6 +1,5 @@ import { TRACEPARENT_REGEXP, timestampInSeconds } from '@sentry/utils'; -import { Transaction } from '../../../build/types'; -import { Span, spanToTraceHeader } from '../../../src'; +import { Span, Transaction, spanToTraceHeader } from '../../../src'; import { getRootSpan, spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../../../src/utils/spanUtils'; describe('spanToTraceHeader', () => { @@ -133,6 +132,7 @@ describe('getRootSpan', () => { it('returns the root span of a span (Span)', () => { const root = new Span({ name: 'test' }); // @ts-expect-error this is highly illegal and shouldn't happen IRL + // eslint-disable-next-line deprecation/deprecation root.transaction = root; // eslint-disable-next-line deprecation/deprecation @@ -141,6 +141,7 @@ describe('getRootSpan', () => { }); it('returns the root span of a span (Transaction)', () => { + // eslint-disable-next-line deprecation/deprecation const root = new Transaction({ name: 'test' }); // eslint-disable-next-line deprecation/deprecation @@ -149,6 +150,7 @@ describe('getRootSpan', () => { }); it('returns the span itself if it is a root span', () => { + // eslint-disable-next-line deprecation/deprecation const span = new Transaction({ name: 'test' }); expect(getRootSpan(span)).toBe(span); From 31126f3c726b4e2e6aa4031f01d404c49dea4615 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 13:23:28 +0100 Subject: [PATCH 3/7] move to dedicated file --- packages/core/src/index.ts | 1 + packages/core/src/server-runtime-client.ts | 3 +- .../src/tracing/dynamicSamplingContext.ts | 3 +- packages/core/src/tracing/span.ts | 2 +- .../core/src/utils/applyScopeDataToEvent.ts | 3 +- packages/core/src/utils/getRootSpan.ts | 15 +++++++ packages/core/src/utils/spanUtils.ts | 14 ------- packages/core/test/lib/utils/getRootSpan.ts | 36 +++++++++++++++++ .../core/test/lib/utils/spanUtils.test.ts | 39 +------------------ .../tracing-internal/src/browser/request.ts | 3 +- packages/tracing-internal/src/common/fetch.ts | 2 +- 11 files changed, 64 insertions(+), 57 deletions(-) create mode 100644 packages/core/src/utils/getRootSpan.ts create mode 100644 packages/core/test/lib/utils/getRootSpan.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e277c01f2dbe..67ce93ce33fa 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -81,6 +81,7 @@ export { spanToJSON, spanIsSampled, } from './utils/spanUtils'; +export { getRootSpan } from './utils/getRootSpan'; export { DEFAULT_ENVIRONMENT } from './constants'; export { ModuleMetadata } from './integrations/metadata'; export { RequestData } from './integrations/requestdata'; diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index 785ad0c4f94b..aed69369fc5d 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -26,7 +26,8 @@ import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, } from './tracing'; -import { getRootSpan, spanToTraceContext } from './utils/spanUtils'; +import { getRootSpan } from './utils/getRootSpan'; +import { spanToTraceContext } from './utils/spanUtils'; export interface ServerRuntimeClientOptions extends ClientOptions { platform?: string; diff --git a/packages/core/src/tracing/dynamicSamplingContext.ts b/packages/core/src/tracing/dynamicSamplingContext.ts index e9b93ef96ac8..f27052c8cfbc 100644 --- a/packages/core/src/tracing/dynamicSamplingContext.ts +++ b/packages/core/src/tracing/dynamicSamplingContext.ts @@ -3,7 +3,8 @@ import { dropUndefinedKeys } from '@sentry/utils'; import { DEFAULT_ENVIRONMENT } from '../constants'; import { getClient, getCurrentScope } from '../exports'; -import { getRootSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils'; +import { getRootSpan } from '../utils/getRootSpan'; +import { spanIsSampled, spanToJSON } from '../utils/spanUtils'; /** * Creates a dynamic sampling context from a client. diff --git a/packages/core/src/tracing/span.ts b/packages/core/src/tracing/span.ts index 5fc4beedc682..170f2d4b60be 100644 --- a/packages/core/src/tracing/span.ts +++ b/packages/core/src/tracing/span.ts @@ -16,10 +16,10 @@ import type { import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; +import { getRootSpan } from '../utils/getRootSpan'; import { TRACE_FLAG_NONE, TRACE_FLAG_SAMPLED, - getRootSpan, spanTimeInputToSeconds, spanToJSON, spanToTraceContext, diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index 57cc00053be9..426f2295fbb0 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -1,7 +1,8 @@ import type { Breadcrumb, Event, PropagationContext, ScopeData, Span } from '@sentry/types'; import { arrayify } from '@sentry/utils'; import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext'; -import { getRootSpan, spanToJSON, spanToTraceContext } from './spanUtils'; +import { getRootSpan } from './getRootSpan'; +import { spanToJSON, spanToTraceContext } from './spanUtils'; /** * Applies data from the scope to the event and runs all event processors on it. diff --git a/packages/core/src/utils/getRootSpan.ts b/packages/core/src/utils/getRootSpan.ts new file mode 100644 index 000000000000..9a0f5d642a77 --- /dev/null +++ b/packages/core/src/utils/getRootSpan.ts @@ -0,0 +1,15 @@ +import type { Span } from '@sentry/types'; + +/** + * Returns the root span of a given span. + * + * As long as we use `Transaction`s internally, the returned root span + * will be a `Transaction` but be aware that this might change in the future. + * + * If the given span has no root span or transaction, `undefined` is returned. + */ +export function getRootSpan(span: Span): Span | undefined { + // TODO (v8): Remove this check and just return span + // eslint-disable-next-line deprecation/deprecation + return span.transaction; +} diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index 1a80f3d29397..cbf8ce6d7f5b 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -107,17 +107,3 @@ export function spanIsSampled(span: Span): boolean { // eslint-disable-next-line no-bitwise return Boolean(traceFlags & TRACE_FLAG_SAMPLED); } - -/** - * Returns the root span of a given span. - * - * As long as we use `Transaction`s internally, the returned root span - * will be a `Transaction` but be aware that this might change in the future. - * - * If the given span has no root span or transaction, `undefined` is returned. - */ -export function getRootSpan(span: Span): Span | undefined { - // TODO (v8): Remove this check and just return span - // eslint-disable-next-line deprecation/deprecation - return span.transaction; -} diff --git a/packages/core/test/lib/utils/getRootSpan.ts b/packages/core/test/lib/utils/getRootSpan.ts new file mode 100644 index 000000000000..eba622a2d884 --- /dev/null +++ b/packages/core/test/lib/utils/getRootSpan.ts @@ -0,0 +1,36 @@ +import { Span, Transaction, getRootSpan } from '../../../src'; + +describe('getRootSpan', () => { + it('returns the root span of a span (Span)', () => { + const root = new Span({ name: 'test' }); + // @ts-expect-error this is highly illegal and shouldn't happen IRL + // eslint-disable-next-line deprecation/deprecation + root.transaction = root; + + // eslint-disable-next-line deprecation/deprecation + const childSpan = root.startChild({ name: 'child' }); + expect(getRootSpan(childSpan)).toBe(root); + }); + + it('returns the root span of a span (Transaction)', () => { + // eslint-disable-next-line deprecation/deprecation + const root = new Transaction({ name: 'test' }); + + // eslint-disable-next-line deprecation/deprecation + const childSpan = root.startChild({ name: 'child' }); + expect(getRootSpan(childSpan)).toBe(root); + }); + + it('returns the span itself if it is a root span', () => { + // eslint-disable-next-line deprecation/deprecation + const span = new Transaction({ name: 'test' }); + + expect(getRootSpan(span)).toBe(span); + }); + + it('returns undefined if span has no root span', () => { + const span = new Span({ name: 'test' }); + + expect(getRootSpan(span)).toBe(undefined); + }); +}); diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts index 116124130cf0..e521df7c2dc9 100644 --- a/packages/core/test/lib/utils/spanUtils.test.ts +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -1,6 +1,6 @@ import { TRACEPARENT_REGEXP, timestampInSeconds } from '@sentry/utils'; -import { Span, Transaction, spanToTraceHeader } from '../../../src'; -import { getRootSpan, spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../../../src/utils/spanUtils'; +import { Span, spanToTraceHeader } from '../../../src'; +import { spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../../../src/utils/spanUtils'; describe('spanToTraceHeader', () => { test('simple', () => { @@ -127,38 +127,3 @@ describe('spanIsSampled', () => { expect(spanIsSampled(span)).toBe(false); }); }); - -describe('getRootSpan', () => { - it('returns the root span of a span (Span)', () => { - const root = new Span({ name: 'test' }); - // @ts-expect-error this is highly illegal and shouldn't happen IRL - // eslint-disable-next-line deprecation/deprecation - root.transaction = root; - - // eslint-disable-next-line deprecation/deprecation - const childSpan = root.startChild({ name: 'child' }); - expect(getRootSpan(childSpan)).toBe(root); - }); - - it('returns the root span of a span (Transaction)', () => { - // eslint-disable-next-line deprecation/deprecation - const root = new Transaction({ name: 'test' }); - - // eslint-disable-next-line deprecation/deprecation - const childSpan = root.startChild({ name: 'child' }); - expect(getRootSpan(childSpan)).toBe(root); - }); - - it('returns the span itself if it is a root span', () => { - // eslint-disable-next-line deprecation/deprecation - const span = new Transaction({ name: 'test' }); - - expect(getRootSpan(span)).toBe(span); - }); - - it('returns undefined if span has no root span', () => { - const span = new Span({ name: 'test' }); - - expect(getRootSpan(span)).toBe(undefined); - }); -}); diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index abde36a88488..5d37c7ed49be 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -21,6 +21,7 @@ import { stringMatchesSomePattern, } from '@sentry/utils'; +import { getRootSpan } from '@sentry/core/build/types/utils/spanUtils'; import { instrumentFetchRequest } from '../common/fetch'; import { addPerformanceInstrumentationHandler } from './instrument'; @@ -298,7 +299,7 @@ export function xhrCallback( if (xhr.setRequestHeader && shouldAttachHeaders(sentryXhrData.url)) { if (span) { - const transaction = span && span.transaction; + const transaction = span && getRootSpan(span); const dynamicSamplingContext = transaction && getDynamicSamplingContextFromSpan(transaction); const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); setHeaderOnXhr(xhr, spanToTraceHeader(span), sentryBaggageHeader); diff --git a/packages/tracing-internal/src/common/fetch.ts b/packages/tracing-internal/src/common/fetch.ts index 14cf242cbf51..b5b104f6f3c6 100644 --- a/packages/tracing-internal/src/common/fetch.ts +++ b/packages/tracing-internal/src/common/fetch.ts @@ -134,7 +134,7 @@ export function addTracingHeadersToFetchRequest( // eslint-disable-next-line deprecation/deprecation const span = requestSpan || scope.getSpan(); - const transaction = span && span.transaction; + const transaction = span && getRootSpan(span); const { traceId, sampled, dsc } = scope.getPropagationContext(); From 9298b763d6db809b9455d29a7df76fd23355cf33 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 13:31:27 +0100 Subject: [PATCH 4/7] replace span.transaction usages in other packages --- packages/astro/src/server/meta.ts | 7 ++++--- packages/opentelemetry-node/src/propagator.ts | 4 ++-- packages/opentelemetry-node/src/utils/spanMap.ts | 3 ++- packages/svelte/src/performance.ts | 3 ++- packages/tracing-internal/src/browser/request.ts | 2 +- packages/tracing-internal/src/common/fetch.ts | 1 + 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/astro/src/server/meta.ts b/packages/astro/src/server/meta.ts index 1a908acc9ceb..dd591c15d966 100644 --- a/packages/astro/src/server/meta.ts +++ b/packages/astro/src/server/meta.ts @@ -1,6 +1,7 @@ import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, + getRootSpan, spanToTraceHeader, } from '@sentry/core'; import type { Client, Scope, Span } from '@sentry/types'; @@ -32,12 +33,12 @@ export function getTracingMetaTags( client: Client | undefined, ): { sentryTrace: string; baggage?: string } { const { dsc, sampled, traceId } = scope.getPropagationContext(); - const transaction = span?.transaction; + const rootSpan = span && getRootSpan(span); const sentryTrace = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); - const dynamicSamplingContext = transaction - ? getDynamicSamplingContextFromSpan(transaction) + const dynamicSamplingContext = rootSpan + ? getDynamicSamplingContextFromSpan(rootSpan) : dsc ? dsc : client diff --git a/packages/opentelemetry-node/src/propagator.ts b/packages/opentelemetry-node/src/propagator.ts index 9052ede7e966..ead43afdb8f5 100644 --- a/packages/opentelemetry-node/src/propagator.ts +++ b/packages/opentelemetry-node/src/propagator.ts @@ -1,7 +1,7 @@ import type { Baggage, Context, TextMapGetter, TextMapSetter } from '@opentelemetry/api'; import { TraceFlags, isSpanContextValid, propagation, trace } from '@opentelemetry/api'; import { W3CBaggagePropagator, isTracingSuppressed } from '@opentelemetry/core'; -import { getDynamicSamplingContextFromSpan, spanToTraceHeader } from '@sentry/core'; +import { getDynamicSamplingContextFromSpan, getRootSpan, spanToTraceHeader } from '@sentry/core'; import { SENTRY_BAGGAGE_KEY_PREFIX, baggageHeaderToDynamicSamplingContext, @@ -35,7 +35,7 @@ export class SentryPropagator extends W3CBaggagePropagator { if (span) { setter.set(carrier, SENTRY_TRACE_HEADER, spanToTraceHeader(span)); - if (span.transaction) { + if (getRootSpan(span)) { const dynamicSamplingContext = getDynamicSamplingContextFromSpan(span); baggage = Object.entries(dynamicSamplingContext).reduce((b, [dscKey, dscValue]) => { if (dscValue) { diff --git a/packages/opentelemetry-node/src/utils/spanMap.ts b/packages/opentelemetry-node/src/utils/spanMap.ts index eee8e923ccbf..9cbdba4460ab 100644 --- a/packages/opentelemetry-node/src/utils/spanMap.ts +++ b/packages/opentelemetry-node/src/utils/spanMap.ts @@ -1,3 +1,4 @@ +import { getRootSpan } from '@sentry/core'; import type { Span as SentrySpan } from '@sentry/types'; interface SpanMapEntry { @@ -31,7 +32,7 @@ export function getSentrySpan(spanId: string): SentrySpan | undefined { export function setSentrySpan(spanId: string, sentrySpan: SentrySpan): void { let ref: SpanRefType = SPAN_REF_ROOT; - const rootSpanId = sentrySpan.transaction?.spanContext().spanId; + const rootSpanId = getRootSpan(sentrySpan)?.spanContext().spanId; if (rootSpanId && rootSpanId !== spanId) { const root = SPAN_MAP.get(rootSpanId); diff --git a/packages/svelte/src/performance.ts b/packages/svelte/src/performance.ts index f0aa16d0c961..38d68468ffd6 100644 --- a/packages/svelte/src/performance.ts +++ b/packages/svelte/src/performance.ts @@ -3,6 +3,7 @@ import type { Span, Transaction } from '@sentry/types'; import { afterUpdate, beforeUpdate, onMount } from 'svelte'; import { current_component } from 'svelte/internal'; +import { getRootSpan } from '@sentry/core'; import { DEFAULT_COMPONENT_NAME, UI_SVELTE_INIT, UI_SVELTE_UPDATE } from './constants'; import type { TrackComponentOptions } from './types'; @@ -74,7 +75,7 @@ function recordUpdateSpans(componentName: string, initSpan?: Span): void { // If we are initializing the component when the update span is started, we start it as child // of the init span. Else, we start it as a child of the transaction. const parentSpan = - initSpan && !initSpan.endTimestamp && initSpan.transaction === transaction ? initSpan : transaction; + initSpan && !initSpan.endTimestamp && getRootSpan(initSpan) === transaction ? initSpan : transaction; // eslint-disable-next-line deprecation/deprecation updateSpan = parentSpan.startChild({ diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index 5d37c7ed49be..461171ec1e25 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -5,6 +5,7 @@ import { getCurrentScope, getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, + getRootSpan, hasTracingEnabled, spanToJSON, spanToTraceHeader, @@ -21,7 +22,6 @@ import { stringMatchesSomePattern, } from '@sentry/utils'; -import { getRootSpan } from '@sentry/core/build/types/utils/spanUtils'; import { instrumentFetchRequest } from '../common/fetch'; import { addPerformanceInstrumentationHandler } from './instrument'; diff --git a/packages/tracing-internal/src/common/fetch.ts b/packages/tracing-internal/src/common/fetch.ts index b5b104f6f3c6..a6f7d68ac920 100644 --- a/packages/tracing-internal/src/common/fetch.ts +++ b/packages/tracing-internal/src/common/fetch.ts @@ -4,6 +4,7 @@ import { getCurrentScope, getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, + getRootSpan, hasTracingEnabled, spanToTraceHeader, } from '@sentry/core'; From 9b721b1cc27d43eb6ecf7966cde14286b6647819 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 13:33:01 +0100 Subject: [PATCH 5/7] migration --- MIGRATION.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 0fb51fd6c4c7..382a0d142f91 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -151,7 +151,11 @@ In v8, the Span class is heavily reworked. The following properties & methods ar - `span.data`: Use `spanToJSON(span).data` instead. - `span.setTag()`: Use `span.setAttribute()` instead or set tags on the surrounding scope. - `span.setData()`: Use `span.setAttribute()` instead. +<<<<<<< HEAD - `span.instrumenter` This field was removed and will be replaced internally. +======= +- `span.transaction`: Use `getRootSpan` utility function instead. +>>>>>>> 167fa756a (migration) - `transaction.setContext()`: Set context on the surrounding scope instead. ## Deprecate `pushScope` & `popScope` in favor of `withScope` From c2c20b1dfffc7e2b9fd1935dd5dd73e1a64f6170 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 10 Jan 2024 13:55:13 +0100 Subject: [PATCH 6/7] add core as a dep of @sentry/svelte --- packages/svelte/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0add4e5758ee..aff3a68ab70a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@sentry/browser": "7.93.0", + "@sentry/core": "7.93.0", "@sentry/types": "7.93.0", "@sentry/utils": "7.93.0", "magic-string": "^0.30.0" From 5131d0267031ad0ba25fa940b3deae19a3d416ad Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 11 Jan 2024 12:50:20 +0100 Subject: [PATCH 7/7] fix rebase ?? --- MIGRATION.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 382a0d142f91..330601046734 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -151,11 +151,8 @@ In v8, the Span class is heavily reworked. The following properties & methods ar - `span.data`: Use `spanToJSON(span).data` instead. - `span.setTag()`: Use `span.setAttribute()` instead or set tags on the surrounding scope. - `span.setData()`: Use `span.setAttribute()` instead. -<<<<<<< HEAD - `span.instrumenter` This field was removed and will be replaced internally. -======= - `span.transaction`: Use `getRootSpan` utility function instead. ->>>>>>> 167fa756a (migration) - `transaction.setContext()`: Set context on the surrounding scope instead. ## Deprecate `pushScope` & `popScope` in favor of `withScope`