diff --git a/MIGRATION.md b/MIGRATION.md index 98d92427c03f..0d8396cd6229 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -15,6 +15,7 @@ In v8, the Span class is heavily reworked. The following properties & methods ar * `span.toContext()`: Access the fields directly instead. * `span.updateWithContext(newSpanContext)`: Update the fields directly instead. * `span.setName(newName)`: Use `span.updateName(newName)` instead. +* `span.toTraceparent()`: use `spanToTraceHeader(span)` util instead. ## Deprecate `pushScope` & `popScope` in favor of `withScope` diff --git a/packages/astro/src/server/meta.ts b/packages/astro/src/server/meta.ts index 643565e7afaa..bdc0d89d4f80 100644 --- a/packages/astro/src/server/meta.ts +++ b/packages/astro/src/server/meta.ts @@ -1,4 +1,4 @@ -import { getDynamicSamplingContextFromClient } from '@sentry/core'; +import { getDynamicSamplingContextFromClient, spanToTraceHeader } from '@sentry/core'; import type { Client, Scope, Span } from '@sentry/types'; import { TRACEPARENT_REGEXP, @@ -30,7 +30,7 @@ export function getTracingMetaTags( const { dsc, sampled, traceId } = scope.getPropagationContext(); const transaction = span?.transaction; - const sentryTrace = span ? span.toTraceparent() : generateSentryTraceHeader(traceId, undefined, sampled); + const sentryTrace = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = transaction ? transaction.getDynamicSamplingContext() diff --git a/packages/astro/test/server/meta.test.ts b/packages/astro/test/server/meta.test.ts index 279f36395107..69caef326936 100644 --- a/packages/astro/test/server/meta.test.ts +++ b/packages/astro/test/server/meta.test.ts @@ -4,7 +4,9 @@ import { vi } from 'vitest'; import { getTracingMetaTags, isValidBaggageString } from '../../src/server/meta'; const mockedSpan = { - toTraceparent: () => '12345678901234567890123456789012-1234567890123456-1', + sampled: true, + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', transaction: { getDynamicSamplingContext: () => ({ environment: 'production', @@ -68,7 +70,9 @@ describe('getTracingMetaTags', () => { const tags = getTracingMetaTags( // @ts-expect-error - only passing a partial span object { - toTraceparent: () => '12345678901234567890123456789012-1234567890123456-1', + sampled: true, + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', transaction: undefined, }, mockedScope, @@ -89,7 +93,9 @@ describe('getTracingMetaTags', () => { const tags = getTracingMetaTags( // @ts-expect-error - only passing a partial span object { - toTraceparent: () => '12345678901234567890123456789012-1234567890123456-1', + sampled: true, + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', transaction: undefined, }, mockedScope, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3954dc6a7005..385352470b34 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -69,6 +69,7 @@ export { prepareEvent } from './utils/prepareEvent'; export { createCheckInEnvelope } from './checkin'; export { hasTracingEnabled } from './utils/hasTracingEnabled'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; +export { spanToTraceHeader } from './utils/spanUtils'; export { DEFAULT_ENVIRONMENT } from './constants'; export { ModuleMetadata } from './integrations/metadata'; export { RequestData } from './integrations/requestdata'; diff --git a/packages/core/src/tracing/hubextensions.ts b/packages/core/src/tracing/hubextensions.ts index 7cd8286860b2..7047a54f1d72 100644 --- a/packages/core/src/tracing/hubextensions.ts +++ b/packages/core/src/tracing/hubextensions.ts @@ -4,6 +4,7 @@ import { logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; import type { Hub } from '../hub'; import { getMainCarrier } from '../hub'; +import { spanToTraceHeader } from '../utils/spanUtils'; import { registerErrorInstrumentation } from './errors'; import { IdleTransaction } from './idletransaction'; import { sampleTransaction } from './sampling'; @@ -16,7 +17,7 @@ function traceHeaders(this: Hub): { [key: string]: string } { return span ? { - 'sentry-trace': span.toTraceparent(), + 'sentry-trace': spanToTraceHeader(span), } : {}; } diff --git a/packages/core/src/tracing/span.ts b/packages/core/src/tracing/span.ts index 29584f65858d..a3496c5d01d0 100644 --- a/packages/core/src/tracing/span.ts +++ b/packages/core/src/tracing/span.ts @@ -10,9 +10,10 @@ import type { TraceContext, Transaction, } from '@sentry/types'; -import { dropUndefinedKeys, generateSentryTraceHeader, logger, timestampInSeconds, uuid4 } from '@sentry/utils'; +import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; +import { spanToTraceHeader } from '../utils/spanUtils'; import { ensureTimestampInSeconds } from './utils'; /** @@ -320,7 +321,7 @@ export class Span implements SpanInterface { * @inheritDoc */ public toTraceparent(): string { - return generateSentryTraceHeader(this.traceId, this.spanId, this.sampled); + return spanToTraceHeader(this); } /** diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts new file mode 100644 index 000000000000..17385fb59c3c --- /dev/null +++ b/packages/core/src/utils/spanUtils.ts @@ -0,0 +1,9 @@ +import type { Span } from '@sentry/types'; +import { generateSentryTraceHeader } from '@sentry/utils'; + +/** + * Convert a Span to a Sentry trace header. + */ +export function spanToTraceHeader(span: Span): string { + return generateSentryTraceHeader(span.traceId, span.spanId, span.sampled); +} diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts new file mode 100644 index 000000000000..c2ed4dd0d4cd --- /dev/null +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -0,0 +1,13 @@ +import { TRACEPARENT_REGEXP } from '@sentry/utils'; +import { Span, spanToTraceHeader } from '../../../src'; + +describe('spanToTraceHeader', () => { + test('simple', () => { + const span = new Span(); + expect(spanToTraceHeader(span)).toMatch(TRACEPARENT_REGEXP); + }); + test('with sample', () => { + const span = new Span({ sampled: true }); + expect(spanToTraceHeader(span)).toMatch(TRACEPARENT_REGEXP); + }); +}); diff --git a/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts index eddf7f4e25e4..cd6cc4934493 100644 --- a/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope, spanToTraceHeader } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type App from 'next/app'; @@ -63,7 +63,7 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI } if (requestTransaction) { - appGetInitialProps.pageProps._sentryTraceData = requestTransaction.toTraceparent(); + appGetInitialProps.pageProps._sentryTraceData = spanToTraceHeader(requestTransaction); const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext(); appGetInitialProps.pageProps._sentryBaggage = diff --git a/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts index 0e4601886cee..b26e4a2434c3 100644 --- a/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope, spanToTraceHeader } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPageContext } from 'next'; import type { ErrorProps } from 'next/error'; @@ -55,7 +55,7 @@ export function wrapErrorGetInitialPropsWithSentry( const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { - errorGetInitialProps._sentryTraceData = requestTransaction.toTraceparent(); + errorGetInitialProps._sentryTraceData = spanToTraceHeader(requestTransaction); const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext(); errorGetInitialProps._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); diff --git a/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts index 510cbae5684c..df4e3febfefc 100644 --- a/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope, spanToTraceHeader } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPage } from 'next'; @@ -51,7 +51,7 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { - initialProps._sentryTraceData = requestTransaction.toTraceparent(); + initialProps._sentryTraceData = spanToTraceHeader(requestTransaction); const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext(); initialProps._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); diff --git a/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts index f93c7193418e..c74f9db7292b 100644 --- a/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope, spanToTraceHeader } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { GetServerSideProps } from 'next'; @@ -48,7 +48,7 @@ export function wrapGetServerSidePropsWithSentry( if (serverSideProps && 'props' in serverSideProps) { const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { - serverSideProps.props._sentryTraceData = requestTransaction.toTraceparent(); + serverSideProps.props._sentryTraceData = spanToTraceHeader(requestTransaction); const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext(); serverSideProps.props._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); diff --git a/packages/node/src/integrations/hapi/index.ts b/packages/node/src/integrations/hapi/index.ts index 15ff63be5f70..732839d3995c 100644 --- a/packages/node/src/integrations/hapi/index.ts +++ b/packages/node/src/integrations/hapi/index.ts @@ -5,6 +5,7 @@ import { convertIntegrationFnToClass, getActiveTransaction, getCurrentScope, + spanToTraceHeader, startTransaction, } from '@sentry/core'; import type { IntegrationFn } from '@sentry/types'; @@ -93,7 +94,7 @@ export const hapiTracingPlugin = { if (request.response && isResponseObject(request.response) && transaction) { const response = request.response as ResponseObject; - response.header('sentry-trace', transaction.toTraceparent()); + response.header('sentry-trace', spanToTraceHeader(transaction)); const dynamicSamplingContext = dynamicSamplingContextToSentryBaggageHeader( transaction.getDynamicSamplingContext(), diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index f0734142a3e1..61046eb8f38d 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,6 +1,7 @@ import type * as http from 'http'; import type * as https from 'https'; import type { Hub } from '@sentry/core'; +import { spanToTraceHeader } from '@sentry/core'; import { addBreadcrumb, getClient, getCurrentScope } from '@sentry/core'; import { getCurrentHub, getDynamicSamplingContextFromClient, isSentryRequestUrl } from '@sentry/core'; import type { @@ -260,7 +261,7 @@ function _createWrappedRequestMethodFactory( if (shouldAttachTraceData(rawRequestUrl)) { if (requestSpan) { - const sentryTraceHeader = requestSpan.toTraceparent(); + const sentryTraceHeader = spanToTraceHeader(requestSpan); const dynamicSamplingContext = requestSpan?.transaction?.getDynamicSamplingContext(); addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); } else { diff --git a/packages/node/src/integrations/undici/index.ts b/packages/node/src/integrations/undici/index.ts index d7111a6076de..117ef602ac38 100644 --- a/packages/node/src/integrations/undici/index.ts +++ b/packages/node/src/integrations/undici/index.ts @@ -5,6 +5,7 @@ import { getCurrentScope, getDynamicSamplingContextFromClient, isSentryRequestUrl, + spanToTraceHeader, } from '@sentry/core'; import type { EventProcessor, Integration, Span } from '@sentry/types'; import { @@ -183,7 +184,7 @@ export class Undici implements Integration { const dynamicSamplingContext = span?.transaction?.getDynamicSamplingContext(); const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - setHeadersOnRequest(request, span.toTraceparent(), sentryBaggageHeader); + setHeadersOnRequest(request, spanToTraceHeader(span), sentryBaggageHeader); } else { const { traceId, sampled, dsc } = scope.getPropagationContext(); const sentryTrace = generateSentryTraceHeader(traceId, undefined, sampled); diff --git a/packages/node/test/integrations/undici.test.ts b/packages/node/test/integrations/undici.test.ts index 27cde77fdcc3..078d90c98721 100644 --- a/packages/node/test/integrations/undici.test.ts +++ b/packages/node/test/integrations/undici.test.ts @@ -1,5 +1,6 @@ import * as http from 'http'; import type { Transaction } from '@sentry/core'; +import { spanToTraceHeader } from '@sentry/core'; import { Hub, makeMain, runWithAsyncContext } from '@sentry/core'; import type { fetch as FetchType } from 'undici'; @@ -207,7 +208,7 @@ conditionalTest({ min: 16 })('Undici integration', () => { expect(transaction.spanRecorder?.spans.length).toBe(2); const span = transaction.spanRecorder?.spans[1]; - expect(requestHeaders['sentry-trace']).toEqual(span?.toTraceparent()); + expect(requestHeaders['sentry-trace']).toEqual(spanToTraceHeader(span!)); expect(requestHeaders['baggage']).toEqual( `sentry-environment=production,sentry-public_key=0,sentry-trace_id=${transaction.traceId},sentry-sample_rate=1,sentry-transaction=test-transaction`, ); diff --git a/packages/opentelemetry-node/src/propagator.ts b/packages/opentelemetry-node/src/propagator.ts index a8154c21e088..ce0f295ce720 100644 --- a/packages/opentelemetry-node/src/propagator.ts +++ b/packages/opentelemetry-node/src/propagator.ts @@ -1,6 +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 { spanToTraceHeader } from '@sentry/core'; import { SENTRY_BAGGAGE_KEY_PREFIX, baggageHeaderToDynamicSamplingContext, @@ -32,7 +33,7 @@ export class SentryPropagator extends W3CBaggagePropagator { const span = getSentrySpan(spanContext.spanId); if (span) { - setter.set(carrier, SENTRY_TRACE_HEADER, span.toTraceparent()); + setter.set(carrier, SENTRY_TRACE_HEADER, spanToTraceHeader(span)); if (span.transaction) { const dynamicSamplingContext = span.transaction.getDynamicSamplingContext(); diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index 62d820a30eeb..f557542e64ce 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -1,5 +1,12 @@ /* eslint-disable max-lines */ -import { getActiveTransaction, getClient, getCurrentScope, hasTracingEnabled, runWithAsyncContext } from '@sentry/core'; +import { + getActiveTransaction, + getClient, + getCurrentScope, + hasTracingEnabled, + runWithAsyncContext, + spanToTraceHeader, +} from '@sentry/core'; import type { Hub } from '@sentry/node'; import { captureException, getCurrentHub } from '@sentry/node'; import type { Transaction, TransactionSource, WrappedFunction } from '@sentry/types'; @@ -293,7 +300,7 @@ function getTraceAndBaggage(): { const dynamicSamplingContext = transaction.getDynamicSamplingContext(); return { - sentryTrace: span.toTraceparent(), + sentryTrace: spanToTraceHeader(span), sentryBaggage: dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext), }; } diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index c999e7ad6fe9..540de96c6de9 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -1,4 +1,4 @@ -import { getCurrentScope } from '@sentry/core'; +import { getCurrentScope, spanToTraceHeader } from '@sentry/core'; import { getActiveTransaction, runWithAsyncContext, startSpan } from '@sentry/core'; import { captureException } from '@sentry/node'; /* eslint-disable @sentry-internal/sdk/no-optional-chaining */ @@ -97,7 +97,7 @@ export function addSentryCodeToPage(options: SentryHandleOptions): NonNullable { const transaction = getActiveTransaction(); if (transaction) { - const traceparentData = transaction.toTraceparent(); + const traceparentData = spanToTraceHeader(transaction); const dynamicSamplingContext = dynamicSamplingContextToSentryBaggageHeader( transaction.getDynamicSamplingContext(), ); diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index cc77ca4889f9..b85d54c96622 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -1,5 +1,11 @@ /* eslint-disable max-lines */ -import { getClient, getCurrentScope, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; +import { + getClient, + getCurrentScope, + getDynamicSamplingContextFromClient, + hasTracingEnabled, + spanToTraceHeader, +} from '@sentry/core'; import type { HandlerDataXhr, SentryWrappedXMLHttpRequest, Span } from '@sentry/types'; import { BAGGAGE_HEADER_NAME, @@ -291,7 +297,7 @@ export function xhrCallback( const transaction = span && span.transaction; const dynamicSamplingContext = transaction && transaction.getDynamicSamplingContext(); const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - setHeaderOnXhr(xhr, span.toTraceparent(), sentryBaggageHeader); + setHeaderOnXhr(xhr, spanToTraceHeader(span), sentryBaggageHeader); } else { const client = getClient(); const { traceId, sampled, dsc } = scope.getPropagationContext(); diff --git a/packages/tracing-internal/src/common/fetch.ts b/packages/tracing-internal/src/common/fetch.ts index 2150518ea570..dfc81bff05f7 100644 --- a/packages/tracing-internal/src/common/fetch.ts +++ b/packages/tracing-internal/src/common/fetch.ts @@ -1,4 +1,10 @@ -import { getClient, getCurrentScope, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; +import { + getClient, + getCurrentScope, + getDynamicSamplingContextFromClient, + hasTracingEnabled, + spanToTraceHeader, +} from '@sentry/core'; import type { Client, HandlerDataFetch, Scope, Span, SpanOrigin } from '@sentry/types'; import { BAGGAGE_HEADER_NAME, @@ -128,7 +134,7 @@ export function addTracingHeadersToFetchRequest( const { traceId, sampled, dsc } = scope.getPropagationContext(); - const sentryTraceHeader = span ? span.toTraceparent() : generateSentryTraceHeader(traceId, undefined, sampled); + const sentryTraceHeader = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = transaction ? transaction.getDynamicSamplingContext() : dsc diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index 3614961e7182..8fd384f4ba11 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -225,7 +225,10 @@ export interface Span extends SpanContext { */ isSuccess(): boolean; - /** Return a traceparent compatible header string */ + /** + * Return a traceparent compatible header string. + * @deprecated Use `spanToTraceHeader()` instead. + */ toTraceparent(): string; /**