From 4c13b0c9dee92200f5e471c93d689ca7ee1019e3 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 29 Feb 2024 09:37:04 +0000 Subject: [PATCH] feat(core): Remove deprecated props from `Span` interface Instead, in places we need it we cast to a `SentrySpan` which still has the things in place, for now. --- packages/astro/test/server/meta.test.ts | 28 +- packages/core/src/scope.ts | 8 +- packages/core/src/tracing/errors.ts | 5 + packages/core/src/tracing/sentrySpan.ts | 6 +- packages/core/src/tracing/trace.ts | 12 +- packages/core/src/utils/getRootSpan.ts | 3 +- packages/core/src/utils/prepareEvent.ts | 3 +- packages/core/src/utils/spanUtils.ts | 11 +- packages/core/test/lib/hint.test.ts | 2 +- .../test/lib/integrations/requestdata.test.ts | 5 +- packages/core/test/lib/scope.test.ts | 14 +- packages/core/test/lib/sdk.test.ts | 2 +- packages/core/test/lib/tracing/errors.test.ts | 6 +- .../test/lib/tracing/idletransaction.test.ts | 13 +- packages/core/test/lib/tracing/trace.test.ts | 7 - packages/node/src/integrations/http.ts | 4 +- .../node/src/integrations/undici/index.ts | 5 +- .../opentelemetry-node/src/spanprocessor.ts | 7 +- .../opentelemetry-node/src/utils/spanMap.ts | 2 +- .../test/propagator.test.ts | 3 +- packages/opentelemetry/src/spanExporter.ts | 6 +- packages/react/test/profiler.test.tsx | 6 +- packages/svelte/src/performance.ts | 13 +- packages/svelte/test/performance.test.ts | 316 +++++++++++------- .../src/node/integrations/apollo.ts | 4 +- .../src/node/integrations/express.ts | 3 +- .../src/node/integrations/graphql.ts | 4 +- .../src/node/integrations/mongo.ts | 4 +- .../src/node/integrations/mysql.ts | 4 +- .../src/node/integrations/postgres.ts | 4 +- packages/types/src/span.ts | 120 +------ packages/types/src/transaction.ts | 25 +- packages/utils/src/requestdata.ts | 15 +- 33 files changed, 335 insertions(+), 335 deletions(-) diff --git a/packages/astro/test/server/meta.test.ts b/packages/astro/test/server/meta.test.ts index f235ad34d7ca..37506cb118b7 100644 --- a/packages/astro/test/server/meta.test.ts +++ b/packages/astro/test/server/meta.test.ts @@ -1,25 +1,23 @@ import * as SentryCore from '@sentry/core'; +import { SentrySpan } from '@sentry/core'; +import type { Transaction } from '@sentry/types'; import { vi } from 'vitest'; import { getTracingMetaTags, isValidBaggageString } from '../../src/server/meta'; const TRACE_FLAG_SAMPLED = 0x1; -const mockedSpan = { - isRecording: () => true, - spanContext: () => { - return { - traceId: '12345678901234567890123456789012', - spanId: '1234567890123456', - traceFlags: TRACE_FLAG_SAMPLED, - }; - }, - transaction: { - getDynamicSamplingContext: () => ({ - environment: 'production', - }), - }, -} as any; +const mockedSpan = new SentrySpan({ + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', + sampled: true, +}); +// eslint-disable-next-line deprecation/deprecation +mockedSpan.transaction = { + getDynamicSamplingContext: () => ({ + environment: 'production', + }), +} as Transaction; const mockedClient = {} as any; diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 4646e5e5b015..820e41858135 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -26,6 +26,7 @@ import type { import { dateTimestampInSeconds, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { updateSession } from './session'; +import type { SentrySpan } from './tracing/sentrySpan'; /** * Default value for maximum number of breadcrumbs added to an event. @@ -329,10 +330,15 @@ 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; + if (span && (span as SentrySpan).transaction) { + // eslint-disable-next-line deprecation/deprecation + return (span as SentrySpan).transaction; + } + return undefined; } /** diff --git a/packages/core/src/tracing/errors.ts b/packages/core/src/tracing/errors.ts index 229695afc58c..f93486129c35 100644 --- a/packages/core/src/tracing/errors.ts +++ b/packages/core/src/tracing/errors.ts @@ -10,6 +10,11 @@ import { getActiveTransaction } from './utils'; let errorsInstrumented = false; +/** Only exposed for testing */ +export function _resetErrorsInstrumented(): void { + errorsInstrumented = false; +} + /** * Configures global error listeners */ diff --git a/packages/core/src/tracing/sentrySpan.ts b/packages/core/src/tracing/sentrySpan.ts index a478574b1c4e..9d1587d5fb60 100644 --- a/packages/core/src/tracing/sentrySpan.ts +++ b/packages/core/src/tracing/sentrySpan.ts @@ -1,6 +1,6 @@ import type { Primitive, - Span as SpanInterface, + Span, SpanAttributeValue, SpanAttributes, SpanContext, @@ -62,7 +62,7 @@ export class SpanRecorder { /** * Span contains all data about a span */ -export class SentrySpan implements SpanInterface { +export class SentrySpan implements Span { /** * Tags for the span. * @deprecated Use `spanToJSON(span).atttributes` instead. @@ -277,7 +277,7 @@ export class SentrySpan implements SpanInterface { */ public startChild( spanContext?: Pick>, - ): SpanInterface { + ): Span { const childSpan = new SentrySpan({ ...spanContext, parentSpanId: this._spanId, diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index a2030986616d..561f61ce9265 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -1,7 +1,6 @@ import type { Hub, Scope, Span, SpanTimeInput, StartSpanOptions, TransactionContext } from '@sentry/types'; import { dropUndefinedKeys, logger, tracingContextFromHeaders } from '@sentry/utils'; - import { getCurrentScope, getIsolationScope, withScope } from '../currentScopes'; import { DEBUG_BUILD } from '../debug-build'; @@ -10,6 +9,7 @@ import { handleCallbackErrors } from '../utils/handleCallbackErrors'; import { hasTracingEnabled } from '../utils/hasTracingEnabled'; import { spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../utils/spanUtils'; import { getDynamicSamplingContextFromSpan } from './dynamicSamplingContext'; +import type { SentrySpan } from './sentrySpan'; import { addChildSpanToSpan, getActiveSpan, setCapturedScopesOnSpan } from './utils'; /** @@ -30,7 +30,7 @@ export function startSpan(context: StartSpanOptions, callback: (span: Span | // eslint-disable-next-line deprecation/deprecation const hub = getCurrentHub(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; const shouldSkipSpan = context.onlyIfParent && !parentSpan; const activeSpan = shouldSkipSpan @@ -79,7 +79,7 @@ export function startSpanManual( // eslint-disable-next-line deprecation/deprecation const hub = getCurrentHub(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; const shouldSkipSpan = context.onlyIfParent && !parentSpan; const activeSpan = shouldSkipSpan @@ -130,8 +130,8 @@ export function startInactiveSpan(context: StartSpanOptions): Span | undefined { const hub = getCurrentHub(); const parentSpan = context.scope ? // eslint-disable-next-line deprecation/deprecation - context.scope.getSpan() - : getActiveSpan(); + (context.scope.getSpan() as SentrySpan | undefined) + : (getActiveSpan() as SentrySpan | undefined); const shouldSkipSpan = context.onlyIfParent && !parentSpan; @@ -264,7 +264,7 @@ function createChildSpanOrTransaction( forceTransaction, scope, }: { - parentSpan: Span | undefined; + parentSpan: SentrySpan | undefined; spanContext: TransactionContext; forceTransaction?: boolean; scope: Scope; diff --git a/packages/core/src/utils/getRootSpan.ts b/packages/core/src/utils/getRootSpan.ts index 9a0f5d642a77..fe6274c60670 100644 --- a/packages/core/src/utils/getRootSpan.ts +++ b/packages/core/src/utils/getRootSpan.ts @@ -1,4 +1,5 @@ import type { Span } from '@sentry/types'; +import type { SentrySpan } from './../tracing/sentrySpan'; /** * Returns the root span of a given span. @@ -11,5 +12,5 @@ import type { Span } from '@sentry/types'; 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; + return (span as SentrySpan).transaction ? (span as SentrySpan).transaction : undefined; } diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 8b0cc90df2fb..006ef75a5f13 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -332,8 +332,7 @@ function normalizeEvent(event: Event | null, depth: number, maxBreadth: number): if (data) { // This is a bit weird, as we generally have `Span` instances here, but to be safe we do not assume so - // eslint-disable-next-line deprecation/deprecation - span.data = normalize(data, depth, maxBreadth); + span.setAttributes(normalize(data, depth, maxBreadth)); } return span; diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index a23559576b9e..436d383b8a7c 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -62,6 +62,8 @@ function ensureTimestampInSeconds(timestamp: number): number { return isMs ? timestamp / 1000 : timestamp; } +type SpanWithToJSON = Span & { toJSON: () => SpanJSON }; + /** * Convert a span to a JSON representation. * Note that all fields returned here are optional and need to be guarded against. @@ -69,7 +71,6 @@ function ensureTimestampInSeconds(timestamp: number): number { * Note: Because of this, we currently have a circular type dependency (which we opted out of in package.json). * This is not avoidable as we need `spanToJSON` in `spanUtils.ts`, which in turn is needed by `span.ts` for backwards compatibility. * And `spanToJSON` needs the Span class from `span.ts` to check here. - * TODO v8: When we remove the deprecated stuff from `span.ts`, we can remove the circular dependency again. */ export function spanToJSON(span: Span): Partial { if (spanIsSentrySpan(span)) { @@ -77,12 +78,12 @@ export function spanToJSON(span: Span): Partial { } // Fallback: We also check for `.toJSON()` here... - // eslint-disable-next-line deprecation/deprecation - if (typeof span.toJSON === 'function') { - // eslint-disable-next-line deprecation/deprecation - return span.toJSON(); + if (typeof (span as SpanWithToJSON).toJSON === 'function') { + return (span as SpanWithToJSON).toJSON(); } + // TODO: Also handle OTEL spans here! + return {}; } diff --git a/packages/core/test/lib/hint.test.ts b/packages/core/test/lib/hint.test.ts index 5fb69ce39fff..25671b45262f 100644 --- a/packages/core/test/lib/hint.test.ts +++ b/packages/core/test/lib/hint.test.ts @@ -1,6 +1,6 @@ -import { captureEvent, getCurrentScope } from '@sentry/core'; import { GLOBAL_OBJ } from '@sentry/utils'; +import { captureEvent, getCurrentScope } from '../../src'; import { initAndBind } from '../../src/sdk'; import { TestClient, getDefaultTestClientOptions } from '../mocks/client'; import { AddAttachmentTestIntegration } from '../mocks/integration'; diff --git a/packages/core/test/lib/integrations/requestdata.test.ts b/packages/core/test/lib/integrations/requestdata.test.ts index e82638fd2e2e..723e9fa18260 100644 --- a/packages/core/test/lib/integrations/requestdata.test.ts +++ b/packages/core/test/lib/integrations/requestdata.test.ts @@ -1,9 +1,8 @@ import type { IncomingMessage } from 'http'; -import type { RequestDataIntegrationOptions } from '@sentry/core'; -import { setCurrentClient } from '@sentry/core'; -import { RequestData } from '@sentry/core'; import type { Event, EventProcessor } from '@sentry/types'; import * as sentryUtils from '@sentry/utils'; +import type { RequestDataIntegrationOptions } from '../../../src'; +import { RequestData, setCurrentClient } from '../../../src'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 30697275db2f..48a39d7f09b0 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -961,18 +961,16 @@ describe('withActiveSpan()', () => { }); }); - it('should create child spans when calling startSpan within the callback', done => { - expect.assertions(2); + it('should create child spans when calling startSpan within the callback', () => { const inactiveSpan = startInactiveSpan({ name: 'inactive-span' }); - withActiveSpan(inactiveSpan!, () => { - startSpan({ name: 'child-span' }, childSpan => { - // eslint-disable-next-line deprecation/deprecation - expect(childSpan?.parentSpanId).toBe(inactiveSpan?.spanContext().spanId); - expect(spanToJSON(childSpan!).parent_span_id).toBe(inactiveSpan?.spanContext().spanId); - done(); + const parentSpanId = withActiveSpan(inactiveSpan!, () => { + return startSpan({ name: 'child-span' }, childSpan => { + return spanToJSON(childSpan!).parent_span_id; }); }); + + expect(parentSpanId).toBe(inactiveSpan?.spanContext().spanId); }); it('when `null` is passed, no span should be active within the callback', () => { diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index ad3551b783da..0117585d05ab 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -1,5 +1,5 @@ -import { captureCheckIn, getCurrentScope, setCurrentClient } from '@sentry/core'; import type { Client, Integration, IntegrationFnResult } from '@sentry/types'; +import { captureCheckIn, getCurrentScope, setCurrentClient } from '../../src'; import { installedIntegrations } from '../../src/integration'; import { initAndBind } from '../../src/sdk'; diff --git a/packages/core/test/lib/tracing/errors.test.ts b/packages/core/test/lib/tracing/errors.test.ts index 29377d3fed09..24c19a24121d 100644 --- a/packages/core/test/lib/tracing/errors.test.ts +++ b/packages/core/test/lib/tracing/errors.test.ts @@ -1,13 +1,14 @@ -import { addTracingExtensions, setCurrentClient, spanToJSON, startInactiveSpan, startSpan } from '@sentry/core'; import type { HandlerDataError, HandlerDataUnhandledRejection } from '@sentry/types'; +import { addTracingExtensions, setCurrentClient, spanToJSON, startInactiveSpan, startSpan } from '../../../src'; -import { registerErrorInstrumentation } from '../../../src/tracing/errors'; +import { _resetErrorsInstrumented, registerErrorInstrumentation } from '../../../src/tracing/errors'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; const mockAddGlobalErrorInstrumentationHandler = jest.fn(); const mockAddGlobalUnhandledRejectionInstrumentationHandler = jest.fn(); let mockErrorCallback: (data: HandlerDataError) => void = () => {}; let mockUnhandledRejectionCallback: (data: HandlerDataUnhandledRejection) => void = () => {}; + jest.mock('@sentry/utils', () => { const actual = jest.requireActual('@sentry/utils'); return { @@ -36,6 +37,7 @@ describe('registerErrorHandlers()', () => { const client = new TestClient(options); setCurrentClient(client); client.init(); + _resetErrorsInstrumented(); }); it('registers error instrumentation', () => { diff --git a/packages/core/test/lib/tracing/idletransaction.test.ts b/packages/core/test/lib/tracing/idletransaction.test.ts index fae249227bb0..56ed93abe5d9 100644 --- a/packages/core/test/lib/tracing/idletransaction.test.ts +++ b/packages/core/test/lib/tracing/idletransaction.test.ts @@ -1,6 +1,12 @@ +/* eslint-disable deprecation/deprecation */ +import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; + import { + IdleTransaction, + SentrySpan, TRACING_DEFAULTS, Transaction, + getClient, getCurrentHub, getCurrentScope, getGlobalScope, @@ -10,11 +16,7 @@ import { startInactiveSpan, startSpan, startSpanManual, -} from '@sentry/core'; -/* eslint-disable deprecation/deprecation */ -import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; - -import { IdleTransaction, SentrySpan, getClient } from '../../../src'; +} from '../../../src'; import { IdleTransactionSpanRecorder } from '../../../src/tracing/idletransaction'; const dsn = 'https://123@sentry.io/42'; @@ -47,6 +49,7 @@ describe('IdleTransaction', () => { transaction.initSpanRecorder(10); const scope = getCurrentScope(); + // eslint-disable-next-line deprecation/deprecation expect(scope.getTransaction()).toBe(transaction); }); diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts index 56f15b6dcce4..bd298fd4d5ff 100644 --- a/packages/core/test/lib/tracing/trace.test.ts +++ b/packages/core/test/lib/tracing/trace.test.ts @@ -287,10 +287,7 @@ describe('startSpan', () => { expect(getCurrentScope()).not.toBe(initialScope); expect(getCurrentScope()).toBe(manualScope); expect(getActiveSpan()).toBe(span); - expect(spanToJSON(span!).parent_span_id).toBe('parent-span-id'); - // eslint-disable-next-line deprecation/deprecation - expect(span?.parentSpanId).toBe('parent-span-id'); }); expect(getCurrentScope()).toBe(initialScope); @@ -565,8 +562,6 @@ describe('startSpanManual', () => { expect(getCurrentScope()).toBe(manualScope); expect(getActiveSpan()).toBe(span); expect(spanToJSON(span!).parent_span_id).toBe('parent-span-id'); - // eslint-disable-next-line deprecation/deprecation - expect(span?.parentSpanId).toBe('parent-span-id'); finish(); @@ -789,8 +784,6 @@ describe('startInactiveSpan', () => { expect(span).toBeDefined(); expect(spanToJSON(span!).parent_span_id).toBe('parent-span-id'); - // eslint-disable-next-line deprecation/deprecation - expect(span?.parentSpanId).toBe('parent-span-id'); expect(getActiveSpan()).toBeUndefined(); span?.end(); diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 22407ca77e91..00858d6b15cf 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ import type * as http from 'http'; import type * as https from 'https'; -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import { defineIntegration, getIsolationScope, hasTracingEnabled } from '@sentry/core'; import { addBreadcrumb, @@ -319,7 +319,7 @@ function _createWrappedRequestMethodFactory( const scope = getCurrentScope(); const isolationScope = getIsolationScope(); - const parentSpan = getActiveSpan(); + const parentSpan = getActiveSpan() as SentrySpan; const data = getRequestSpanData(requestUrl, requestOptions); diff --git a/packages/node/src/integrations/undici/index.ts b/packages/node/src/integrations/undici/index.ts index b76caf647fae..222c75f852d8 100644 --- a/packages/node/src/integrations/undici/index.ts +++ b/packages/node/src/integrations/undici/index.ts @@ -1,3 +1,4 @@ +import type { SentrySpan } from '@sentry/core'; import { addBreadcrumb, defineIntegration, @@ -183,7 +184,7 @@ export class Undici implements Integration { const clientOptions = client.getOptions(); const scope = getCurrentScope(); const isolationScope = getIsolationScope(); - const parentSpan = getActiveSpan(); + const parentSpan = getActiveSpan() as SentrySpan; const span = this._shouldCreateSpan(stringUrl) ? createRequestSpan(parentSpan, request, stringUrl) : undefined; if (span) { @@ -320,7 +321,7 @@ function setHeadersOnRequest( } function createRequestSpan( - activeSpan: Span | undefined, + activeSpan: SentrySpan | undefined, request: RequestWithSentry, stringUrl: string, ): Span | undefined { diff --git a/packages/opentelemetry-node/src/spanprocessor.ts b/packages/opentelemetry-node/src/spanprocessor.ts index 4ef437a6e88e..7788858c586d 100644 --- a/packages/opentelemetry-node/src/spanprocessor.ts +++ b/packages/opentelemetry-node/src/spanprocessor.ts @@ -2,6 +2,7 @@ import type { Context } from '@opentelemetry/api'; import { SpanKind, context, trace } from '@opentelemetry/api'; import { suppressTracing } from '@opentelemetry/core'; import type { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import type { SentrySpan } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, @@ -11,7 +12,7 @@ import { getClient, getCurrentHub, } from '@sentry/core'; -import type { DynamicSamplingContext, Span as SentrySpan, TraceparentData, TransactionContext } from '@sentry/types'; +import type { DynamicSamplingContext, TraceparentData, TransactionContext } from '@sentry/types'; import { logger } from '@sentry/utils'; import { SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY, SENTRY_TRACE_PARENT_CONTEXT_KEY } from './constants'; @@ -69,7 +70,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor { name: otelSpan.name, startTimestamp: convertOtelTimeToSeconds(otelSpan.startTime), spanId: otelSpanId, - }); + }) as SentrySpan; setSentrySpan(otelSpanId, sentryChildSpan); } else { @@ -83,7 +84,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor { spanId: otelSpanId, }); - setSentrySpan(otelSpanId, transaction); + setSentrySpan(otelSpanId, transaction as unknown as SentrySpan); } } diff --git a/packages/opentelemetry-node/src/utils/spanMap.ts b/packages/opentelemetry-node/src/utils/spanMap.ts index 9cbdba4460ab..49e4c033403e 100644 --- a/packages/opentelemetry-node/src/utils/spanMap.ts +++ b/packages/opentelemetry-node/src/utils/spanMap.ts @@ -1,5 +1,5 @@ +import type { SentrySpan } from '@sentry/core'; import { getRootSpan } from '@sentry/core'; -import type { Span as SentrySpan } from '@sentry/types'; interface SpanMapEntry { sentrySpan: SentrySpan; diff --git a/packages/opentelemetry-node/test/propagator.test.ts b/packages/opentelemetry-node/test/propagator.test.ts index 6067b5d7e90d..550ec2633843 100644 --- a/packages/opentelemetry-node/test/propagator.test.ts +++ b/packages/opentelemetry-node/test/propagator.test.ts @@ -7,6 +7,7 @@ import { trace, } from '@opentelemetry/api'; import { suppressTracing } from '@opentelemetry/core'; +import type { SentrySpan } from '@sentry/core'; import { Transaction, addTracingExtensions, getCurrentHub, setCurrentClient } from '@sentry/core'; import type { Client, TransactionContext } from '@sentry/types'; @@ -67,7 +68,7 @@ describe('SentryPropagator', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { spanId, ...ctx } = transactionContext; // eslint-disable-next-line deprecation/deprecation - const span = transaction.startChild({ ...ctx, name: transactionContext.name }); + const span = transaction.startChild({ ...ctx, name: transactionContext.name }) as SentrySpan; setSentrySpan(span.spanContext().spanId, span); } } diff --git a/packages/opentelemetry/src/spanExporter.ts b/packages/opentelemetry/src/spanExporter.ts index 14f5fdc9fef8..b673930be9a4 100644 --- a/packages/opentelemetry/src/spanExporter.ts +++ b/packages/opentelemetry/src/spanExporter.ts @@ -3,7 +3,7 @@ import type { ExportResult } from '@opentelemetry/core'; import { ExportResultCode } from '@opentelemetry/core'; import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import type { Transaction } from '@sentry/core'; +import type { SentrySpan, Transaction } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -11,7 +11,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getCurrentHub, } from '@sentry/core'; -import type { Scope, Span as SentrySpan, SpanOrigin, TransactionSource } from '@sentry/types'; +import type { Scope, SpanOrigin, TransactionSource } from '@sentry/types'; import { addNonEnumerableProperty, dropUndefinedKeys, logger } from '@sentry/utils'; import { startTransaction } from './custom/transaction'; @@ -241,7 +241,7 @@ function createAndFinishSpanForOtelSpan(node: SpanNode, sentryParentSpan: Sentry startTimestamp: convertOtelTimeToSeconds(span.startTime), spanId, origin, - }); + }) as SentrySpan; sentrySpan.setStatus(mapStatus(span)); node.children.forEach(child => { diff --git a/packages/react/test/profiler.test.tsx b/packages/react/test/profiler.test.tsx index 5d399f342535..de770b362473 100644 --- a/packages/react/test/profiler.test.tsx +++ b/packages/react/test/profiler.test.tsx @@ -1,3 +1,4 @@ +import { SentrySpan } from '@sentry/core'; import type { SpanContext } from '@sentry/types'; import { render } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; @@ -10,10 +11,7 @@ import { UNKNOWN_COMPONENT, useProfiler, withProfiler } from '../src/profiler'; const mockStartInactiveSpan = jest.fn((spanArgs: SpanContext) => ({ ...spanArgs })); const mockFinish = jest.fn(); -// @sent -class MockSpan { - public constructor(public readonly ctx: SpanContext) {} - +class MockSpan extends SentrySpan { public end(): void { mockFinish(); } diff --git a/packages/svelte/src/performance.ts b/packages/svelte/src/performance.ts index 8cc3e86017ed..302f346e32b9 100644 --- a/packages/svelte/src/performance.ts +++ b/packages/svelte/src/performance.ts @@ -3,7 +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 { getRootSpan, startInactiveSpan, withActiveSpan } from '@sentry/core'; import { DEFAULT_COMPONENT_NAME, UI_SVELTE_INIT, UI_SVELTE_UPDATE } from './constants'; import type { TrackComponentOptions } from './types'; @@ -77,11 +77,12 @@ function recordUpdateSpans(componentName: string, initSpan?: Span): void { const parentSpan = initSpan && initSpan.isRecording() && getRootSpan(initSpan) === transaction ? initSpan : transaction; - // eslint-disable-next-line deprecation/deprecation - updateSpan = parentSpan.startChild({ - op: UI_SVELTE_UPDATE, - name: componentName, - origin: 'auto.ui.svelte', + updateSpan = withActiveSpan(parentSpan, () => { + return startInactiveSpan({ + op: UI_SVELTE_UPDATE, + name: componentName, + origin: 'auto.ui.svelte', + }); }); }); diff --git a/packages/svelte/test/performance.test.ts b/packages/svelte/test/performance.test.ts index 1d90b0b9ab79..8e7357da044e 100644 --- a/packages/svelte/test/performance.test.ts +++ b/packages/svelte/test/performance.test.ts @@ -1,197 +1,259 @@ -import type { Scope } from '@sentry/core'; import { act, render } from '@testing-library/svelte'; - +import { + addTracingExtensions, + getClient, + getCurrentScope, + getIsolationScope, + init, + spanToJSON, + startSpan, +} from '../src'; + +import type { TransactionEvent } from '@sentry/types'; import { vi } from 'vitest'; // linter doesn't like Svelte component imports import DummyComponent from './components/Dummy.svelte'; -let returnUndefinedTransaction = false; - -const testTransaction: { spans: any[]; startChild: jest.Mock; end: jest.Mock; isRecording: () => boolean } = { - spans: [], - startChild: vi.fn(), - end: vi.fn(), - isRecording: () => true, -}; -const testUpdateSpan = { end: vi.fn() }; -const testInitSpan: any = { - transaction: testTransaction, - end: vi.fn(), - startChild: vi.fn(), - isRecording: () => true, -}; - -vi.mock('@sentry/core', async () => { - const original = await vi.importActual('@sentry/core'); - return { - ...original, - getCurrentScope(): Scope { - return { - getTransaction: () => { - return returnUndefinedTransaction ? undefined : testTransaction; - }, - } as Scope; - }, - }; -}); +const PUBLIC_DSN = 'https://username@domain/123'; describe('Sentry.trackComponent()', () => { + const transactions: TransactionEvent[] = []; + beforeEach(() => { + transactions.splice(0, transactions.length); + vi.resetAllMocks(); - testTransaction.spans = []; - testTransaction.startChild.mockImplementation(spanCtx => { - testTransaction.spans.push(spanCtx); - return testInitSpan; - }); + getCurrentScope().clear(); + getIsolationScope().clear(); + + addTracingExtensions(); - testInitSpan.startChild.mockImplementation((spanCtx: any) => { - testTransaction.spans.push(spanCtx); - return testUpdateSpan; + const beforeSendTransaction = vi.fn(event => { + transactions.push(event); + return null; }); - testInitSpan.end = vi.fn(); - testInitSpan.isRecording = () => true; - returnUndefinedTransaction = false; + init({ + dsn: PUBLIC_DSN, + enableTracing: true, + beforeSendTransaction, + }); }); - it('creates nested init and update spans on component initialization', () => { - render(DummyComponent, { props: { options: {} } }); + it('creates nested init and update spans on component initialization', async () => { + startSpan({ name: 'outer' }, span => { + expect(span).toBeDefined(); + render(DummyComponent, { props: { options: {} } }); + }); + + await getClient()?.flush(); + + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(2); + + const rootSpanId = transaction.contexts?.trace?.span_id; + expect(rootSpanId).toBeDefined(); - expect(testTransaction.startChild).toHaveBeenCalledWith({ - name: '', + const initSpanId = transaction.spans![0].spanContext().spanId; + + expect(spanToJSON(transaction.spans![0])).toEqual({ + data: { + 'sentry.op': 'ui.svelte.init', + 'sentry.origin': 'auto.ui.svelte', + }, + description: '', op: 'ui.svelte.init', origin: 'auto.ui.svelte', + parent_span_id: rootSpanId, + span_id: initSpanId, + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), }); - expect(testInitSpan.startChild).toHaveBeenCalledWith({ - name: '', + expect(spanToJSON(transaction.spans![1])).toEqual({ + data: { + 'sentry.op': 'ui.svelte.update', + 'sentry.origin': 'auto.ui.svelte', + }, + description: '', op: 'ui.svelte.update', origin: 'auto.ui.svelte', + parent_span_id: initSpanId, + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), }); - - expect(testInitSpan.end).toHaveBeenCalledTimes(1); - expect(testUpdateSpan.end).toHaveBeenCalledTimes(1); - expect(testTransaction.spans.length).toEqual(2); }); it('creates an update span, when the component is updated', async () => { - // Make the end() function actually end the initSpan - testInitSpan.end.mockImplementation(() => { - testInitSpan.isRecording = () => false; + startSpan({ name: 'outer' }, async span => { + expect(span).toBeDefined(); + + // first we create the component + const { component } = render(DummyComponent, { props: { options: {} } }); + + // then trigger an update + // (just changing the trackUpdates prop so that we trigger an update. # + // The value doesn't do anything here) + await act(() => component.$set({ options: { trackUpdates: true } })); }); - // first we create the component - const { component } = render(DummyComponent, { props: { options: {} } }); + await getClient()?.flush(); - // then trigger an update - // (just changing the trackUpdates prop so that we trigger an update. # - // The value doesn't do anything here) - await act(() => component.$set({ options: { trackUpdates: true } })); + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(3); + + const rootSpanId = transaction.contexts?.trace?.span_id; + expect(rootSpanId).toBeDefined(); + + const initSpanId = transaction.spans![0].spanContext().spanId; - // once for init (unimportant here), once for starting the update span - expect(testTransaction.startChild).toHaveBeenCalledTimes(2); - expect(testTransaction.startChild).toHaveBeenLastCalledWith({ - name: '', + expect(spanToJSON(transaction.spans![0])).toEqual({ + data: { + 'sentry.op': 'ui.svelte.init', + 'sentry.origin': 'auto.ui.svelte', + }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + parent_span_id: rootSpanId, + span_id: initSpanId, + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spanToJSON(transaction.spans![1])).toEqual({ + data: { + 'sentry.op': 'ui.svelte.update', + 'sentry.origin': 'auto.ui.svelte', + }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + parent_span_id: initSpanId, + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spanToJSON(transaction.spans![2])).toEqual({ + data: { + 'sentry.op': 'ui.svelte.update', + 'sentry.origin': 'auto.ui.svelte', + }, + description: '', op: 'ui.svelte.update', origin: 'auto.ui.svelte', + parent_span_id: rootSpanId, + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), }); - expect(testTransaction.spans.length).toEqual(3); }); - it('only creates init spans if trackUpdates is deactivated', () => { - render(DummyComponent, { props: { options: { trackUpdates: false } } }); + it('only creates init spans if trackUpdates is deactivated', async () => { + startSpan({ name: 'outer' }, async span => { + expect(span).toBeDefined(); - expect(testTransaction.startChild).toHaveBeenCalledWith({ - name: '', - op: 'ui.svelte.init', - origin: 'auto.ui.svelte', + render(DummyComponent, { props: { options: { trackUpdates: false } } }); }); - expect(testInitSpan.startChild).not.toHaveBeenCalled(); + await getClient()?.flush(); + + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(1); - expect(testInitSpan.end).toHaveBeenCalledTimes(1); - expect(testTransaction.spans.length).toEqual(1); + expect(spanToJSON(transaction.spans![0]).op).toEqual('ui.svelte.init'); }); - it('only creates update spans if trackInit is deactivated', () => { - render(DummyComponent, { props: { options: { trackInit: false } } }); + it('only creates update spans if trackInit is deactivated', async () => { + startSpan({ name: 'outer' }, span => { + expect(span).toBeDefined(); - expect(testTransaction.startChild).toHaveBeenCalledWith({ - name: '', - op: 'ui.svelte.update', - origin: 'auto.ui.svelte', + render(DummyComponent, { props: { options: { trackInit: false } } }); }); - expect(testInitSpan.startChild).not.toHaveBeenCalled(); + await getClient()?.flush(); - expect(testInitSpan.end).toHaveBeenCalledTimes(1); - expect(testTransaction.spans.length).toEqual(1); + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(1); + + expect(spanToJSON(transaction.spans![0]).op).toEqual('ui.svelte.update'); }); - it('creates no spans if trackInit and trackUpdates are deactivated', () => { - render(DummyComponent, { props: { options: { trackInit: false, trackUpdates: false } } }); + it('creates no spans if trackInit and trackUpdates are deactivated', async () => { + startSpan({ name: 'outer' }, span => { + expect(span).toBeDefined(); + + render(DummyComponent, { props: { options: { trackInit: false, trackUpdates: false } } }); + }); + + await getClient()?.flush(); - expect(testTransaction.startChild).not.toHaveBeenCalled(); - expect(testInitSpan.startChild).not.toHaveBeenCalled(); - expect(testTransaction.spans.length).toEqual(0); + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(0); }); it('sets a custom component name as a span name if `componentName` is provided', async () => { - render(DummyComponent, { - props: { options: { componentName: 'CustomComponentName' } }, - }); + startSpan({ name: 'outer' }, span => { + expect(span).toBeDefined(); - expect(testTransaction.startChild).toHaveBeenCalledWith({ - name: '', - op: 'ui.svelte.init', - origin: 'auto.ui.svelte', + render(DummyComponent, { + props: { options: { componentName: 'CustomComponentName' } }, + }); }); - expect(testInitSpan.startChild).toHaveBeenCalledWith({ - name: '', - op: 'ui.svelte.update', - origin: 'auto.ui.svelte', - }); + await getClient()?.flush(); - expect(testInitSpan.end).toHaveBeenCalledTimes(1); - expect(testUpdateSpan.end).toHaveBeenCalledTimes(1); - expect(testTransaction.spans.length).toEqual(2); + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + expect(transaction.spans).toHaveLength(2); + + expect(spanToJSON(transaction.spans![0]).description).toEqual(''); + expect(spanToJSON(transaction.spans![1]).description).toEqual(''); }); it("doesn't do anything, if there's no ongoing transaction", async () => { - returnUndefinedTransaction = true; - render(DummyComponent, { props: { options: { componentName: 'CustomComponentName' } }, }); - expect(testInitSpan.end).toHaveBeenCalledTimes(0); - expect(testUpdateSpan.end).toHaveBeenCalledTimes(0); - expect(testTransaction.spans.length).toEqual(0); + await getClient()?.flush(); + + expect(transactions).toHaveLength(0); }); - it("doesn't record update spans, if there's no ongoing transaction at that time", async () => { - // Make the end() function actually end the initSpan - testInitSpan.end.mockImplementation(() => { - testInitSpan.isRecording = () => false; - }); + it("doesn't record update spans, if there's no ongoing root span at that time", async () => { + const component = startSpan({ name: 'outer' }, span => { + expect(span).toBeDefined(); - // first we create the component - const { component } = render(DummyComponent, { props: { options: {} } }); + const { component } = render(DummyComponent, { props: { options: {} } }); + return component; + }); - // then clear the current transaction and trigger an update - returnUndefinedTransaction = true; + // then trigger an update after the root span ended - should not record update span await act(() => component.$set({ options: { trackUpdates: true } })); - // we should only record the init spans (including the initial update) - // but not the second update - expect(testTransaction.startChild).toHaveBeenCalledTimes(1); - expect(testTransaction.startChild).toHaveBeenLastCalledWith({ - name: '', - op: 'ui.svelte.init', - origin: 'auto.ui.svelte', - }); - expect(testTransaction.spans.length).toEqual(2); + await getClient()?.flush(); + + expect(transactions).toHaveLength(1); + const transaction = transactions[0]; + + // One update span is triggered by the initial rendering, but the second one is not captured + expect(transaction.spans).toHaveLength(2); + + expect(spanToJSON(transaction.spans![0]).op).toEqual('ui.svelte.init'); + expect(spanToJSON(transaction.spans![1]).op).toEqual('ui.svelte.update'); }); }); diff --git a/packages/tracing-internal/src/node/integrations/apollo.ts b/packages/tracing-internal/src/node/integrations/apollo.ts index d2a7f0ff73ee..2b4268239d63 100644 --- a/packages/tracing-internal/src/node/integrations/apollo.ts +++ b/packages/tracing-internal/src/node/integrations/apollo.ts @@ -1,4 +1,4 @@ -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import type { EventProcessor } from '@sentry/types'; import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -185,7 +185,7 @@ function wrapResolver( // eslint-disable-next-line deprecation/deprecation const scope = getCurrentHub().getScope(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; // eslint-disable-next-line deprecation/deprecation const span = parentSpan?.startChild({ name: `${resolverGroupName}.${resolverName}`, diff --git a/packages/tracing-internal/src/node/integrations/express.ts b/packages/tracing-internal/src/node/integrations/express.ts index e1417c0f1773..b6012bad9a72 100644 --- a/packages/tracing-internal/src/node/integrations/express.ts +++ b/packages/tracing-internal/src/node/integrations/express.ts @@ -1,6 +1,7 @@ /* eslint-disable max-lines */ +import type { Transaction } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON } from '@sentry/core'; -import type { Integration, PolymorphicRequest, Transaction } from '@sentry/types'; +import type { Integration, PolymorphicRequest } from '@sentry/types'; import { GLOBAL_OBJ, extractPathForTransaction, diff --git a/packages/tracing-internal/src/node/integrations/graphql.ts b/packages/tracing-internal/src/node/integrations/graphql.ts index 16256e0dccab..b2ddee7530f3 100644 --- a/packages/tracing-internal/src/node/integrations/graphql.ts +++ b/packages/tracing-internal/src/node/integrations/graphql.ts @@ -1,4 +1,4 @@ -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import type { EventProcessor } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -48,7 +48,7 @@ export class GraphQL implements LazyLoadedIntegration { // eslint-disable-next-line deprecation/deprecation const scope = getCurrentHub().getScope(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; // eslint-disable-next-line deprecation/deprecation const span = parentSpan?.startChild({ diff --git a/packages/tracing-internal/src/node/integrations/mongo.ts b/packages/tracing-internal/src/node/integrations/mongo.ts index 8496ed422821..e052eafa6378 100644 --- a/packages/tracing-internal/src/node/integrations/mongo.ts +++ b/packages/tracing-internal/src/node/integrations/mongo.ts @@ -1,4 +1,4 @@ -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import type { EventProcessor, SpanContext } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -174,7 +174,7 @@ export class Mongo implements LazyLoadedIntegration { // eslint-disable-next-line deprecation/deprecation const client = hub.getClient(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; const sendDefaultPii = client?.getOptions().sendDefaultPii; diff --git a/packages/tracing-internal/src/node/integrations/mysql.ts b/packages/tracing-internal/src/node/integrations/mysql.ts index d7349f804aae..3cca1c8d5ccd 100644 --- a/packages/tracing-internal/src/node/integrations/mysql.ts +++ b/packages/tracing-internal/src/node/integrations/mysql.ts @@ -1,4 +1,4 @@ -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import type { EventProcessor, Span } from '@sentry/types'; import { fill, loadModule, logger } from '@sentry/utils'; @@ -100,7 +100,7 @@ export class Mysql implements LazyLoadedIntegration { // eslint-disable-next-line deprecation/deprecation const scope = getCurrentHub().getScope(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; // eslint-disable-next-line deprecation/deprecation const span = parentSpan?.startChild({ diff --git a/packages/tracing-internal/src/node/integrations/postgres.ts b/packages/tracing-internal/src/node/integrations/postgres.ts index 9b6cb9fd77dc..3c883bb64de1 100644 --- a/packages/tracing-internal/src/node/integrations/postgres.ts +++ b/packages/tracing-internal/src/node/integrations/postgres.ts @@ -1,4 +1,4 @@ -import type { Hub } from '@sentry/core'; +import type { Hub, SentrySpan } from '@sentry/core'; import type { EventProcessor } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -101,7 +101,7 @@ export class Postgres implements LazyLoadedIntegration { // eslint-disable-next-line deprecation/deprecation const scope = getCurrentHub().getScope(); // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan(); + const parentSpan = scope.getSpan() as SentrySpan | undefined; const data: Record = { 'db.system': 'postgresql', diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index 1818b7e99995..018e84e977ec 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -1,7 +1,6 @@ -import type { TraceContext } from './context'; import type { Primitive } from './misc'; import type { HrTime } from './opentelemetry'; -import type { Transaction, TransactionSource } from './transaction'; +import type { TransactionSource } from './transaction'; type SpanOriginType = 'manual' | 'auto'; type SpanOriginCategory = string; // e.g. http, db, ui, .... @@ -97,7 +96,10 @@ export interface SpanContextData { // Note: we do not have traceState here, but this is optional in OpenTelemetry anyhow } -/** Interface holding all properties that can be set on a Span on creation. */ +/** + * Interface holding all properties that can be set on a Span on creation. + * This is only used for the legacy span/transaction creation and will go away in v8. + */ export interface SpanContext { /** * Human-readable identifier for the span. @@ -162,69 +164,10 @@ export interface SpanContext { origin?: SpanOrigin | undefined; } -/** Span holding trace_id, span_id */ -export interface Span extends Omit { - /** - * The ID of the span. - * @deprecated Use `spanContext().spanId` instead. - */ - spanId: string; - - /** - * Parent Span ID - * - * @deprecated Use `spanToJSON(span).parent_span_id` instead. - */ - parentSpanId?: string | undefined; - - /** - * The ID of the trace. - * @deprecated Use `spanContext().traceId` instead. - */ - traceId: string; - - /** - * Was this span chosen to be sent as part of the sample? - * @deprecated Use `isRecording()` instead. - */ - sampled?: boolean | undefined; - - /** - * Timestamp in seconds (epoch time) indicating when the span started. - * @deprecated Use `spanToJSON()` instead. - */ - startTimestamp: number; - - /** - * Timestamp in seconds (epoch time) indicating when the span ended. - * @deprecated Use `spanToJSON()` instead. - */ - endTimestamp?: number | undefined; - - /** - * Tags for the span. - * @deprecated Use `spanToJSON(span).atttributes` instead. - */ - tags: { [key: string]: Primitive }; - - /** - * Data for the span. - * @deprecated Use `spanToJSON(span).atttributes` instead. - */ - data: { [key: string]: any }; - - /** - * Attributes for the span. - * @deprecated Use `spanToJSON(span).atttributes` instead. - */ - attributes: SpanAttributes; - - /** - * The transaction containing this span - * @deprecated Use top level `Sentry.getRootSpan()` instead - */ - transaction?: Transaction; - +/** + * A generic Span which holds trace data. + */ +export interface Span { /** * Get context data for this span. * This includes the spanId & the traceId. @@ -236,25 +179,6 @@ export interface Span extends Omit>): Span; - - /** - * Returns the current span properties as a `SpanContext`. - * @deprecated Use `toJSON()` or access the fields directly instead. - */ - toContext(): SpanContext; - - /** - * Convert the object to JSON for w. spans array info only. - * @deprecated Use `spanToTraceContext()` util function instead. - */ - getTraceContext(): TraceContext; - - /** - * Convert the object to JSON. - * @deprecated Use `spanToJSON(span)` instead. - */ - toJSON(): SpanJSON; - /** * If this is span is actually recording data. * This will return false if tracing is disabled, this span was not sampled or if the span is already finished. diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index 235ba07242db..4b05fb430565 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -37,7 +37,22 @@ export interface TransactionContext extends SpanContext { /** * Data pulled from a `sentry-trace` header */ -export type TraceparentData = Pick; +export interface TraceparentData { + /** + * Trace ID + */ + traceId?: string | undefined; + + /** + * Parent Span ID + */ + parentSpanId?: string | undefined; + + /** + * If this transaction has a parent, the parent's sampling decision + */ + parentSampled?: boolean | undefined; +} /** * Transaction "Class", inherits Span only has `setName` @@ -125,6 +140,14 @@ export interface Transaction extends Omit, Sp * @deprecated Use top-level `getDynamicSamplingContextFromSpan` instead. */ getDynamicSamplingContext(): Partial; + + /** + * Creates a new `Span` while setting the current `Span.id` as `parentSpanId`. + * Also the `sampled` decision will be inherited. + * + * @deprecated Use `startSpan()`, `startSpanManual()` or `startInactiveSpan()` instead. + */ + startChild(spanContext?: Pick>): Span; } /** diff --git a/packages/utils/src/requestdata.ts b/packages/utils/src/requestdata.ts index 19c2cbd18f4e..f6a4129fac2a 100644 --- a/packages/utils/src/requestdata.ts +++ b/packages/utils/src/requestdata.ts @@ -87,9 +87,18 @@ export function addRequestDataToTransaction( if (req.baseUrl) { transaction.setAttribute('baseUrl', req.baseUrl); } - // TODO: We need to rewrite this to a flat format? - // eslint-disable-next-line deprecation/deprecation - transaction.setData('query', extractQueryParams(req)); + + const query = extractQueryParams(req); + if (typeof query === 'string') { + transaction.setAttribute('query', query); + } else if (query) { + Object.keys(query).forEach(key => { + const val = query[key]; + if (typeof val === 'string' || typeof val === 'number') { + transaction.setAttribute(`query.${key}`, val); + } + }); + } } /**