From 9296b8866830ee251f5149837765f129aca25c7d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 31 Jul 2024 13:09:36 +0200 Subject: [PATCH 1/9] feat(node): Add `getTracingMetaTags` function --- packages/astro/src/server/meta.ts | 86 -------- packages/astro/src/server/middleware.ts | 11 +- packages/astro/test/server/meta.test.ts | 200 ------------------ packages/astro/test/server/middleware.test.ts | 7 +- packages/node/src/index.ts | 1 + 5 files changed, 15 insertions(+), 290 deletions(-) delete mode 100644 packages/astro/src/server/meta.ts delete mode 100644 packages/astro/test/server/meta.test.ts diff --git a/packages/astro/src/server/meta.ts b/packages/astro/src/server/meta.ts deleted file mode 100644 index 42d50c9d865d..000000000000 --- a/packages/astro/src/server/meta.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - getDynamicSamplingContextFromClient, - getDynamicSamplingContextFromSpan, - getRootSpan, - spanToTraceHeader, -} from '@sentry/core'; -import type { Client, Scope, Span } from '@sentry/types'; -import { - TRACEPARENT_REGEXP, - dynamicSamplingContextToSentryBaggageHeader, - generateSentryTraceHeader, - logger, -} from '@sentry/utils'; - -/** - * Extracts the tracing data from the current span or from the client's scope - * (via transaction or propagation context) and renders the data to tags. - * - * This function creates two serialized tags: - * - `` - * - `` - * - * TODO: Extract this later on and export it from the Core or Node SDK - * - * @param span the currently active span - * @param client the SDK's client - * - * @returns an object with the two serialized tags - */ -export function getTracingMetaTags( - span: Span | undefined, - scope: Scope, - client: Client | undefined, -): { sentryTrace: string; baggage?: string } { - const { dsc, sampled, traceId } = scope.getPropagationContext(); - const rootSpan = span && getRootSpan(span); - - const sentryTrace = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); - - const dynamicSamplingContext = rootSpan - ? getDynamicSamplingContextFromSpan(rootSpan) - : dsc - ? dsc - : client - ? getDynamicSamplingContextFromClient(traceId, client) - : undefined; - - const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - - const isValidSentryTraceHeader = TRACEPARENT_REGEXP.test(sentryTrace); - if (!isValidSentryTraceHeader) { - logger.warn('Invalid sentry-trace data. Returning empty tag'); - } - - const validBaggage = isValidBaggageString(baggage); - if (!validBaggage) { - logger.warn('Invalid baggage data. Returning empty tag'); - } - - return { - sentryTrace: ``, - baggage: baggage && ``, - }; -} - -/** - * Tests string against baggage spec as defined in: - * - * - W3C Baggage grammar: https://www.w3.org/TR/baggage/#definition - * - RFC7230 token definition: https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 - * - * exported for testing - */ -export function isValidBaggageString(baggage?: string): boolean { - if (!baggage || !baggage.length) { - return false; - } - const keyRegex = "[-!#$%&'*+.^_`|~A-Za-z0-9]+"; - const valueRegex = '[!#-+-./0-9:<=>?@A-Z\\[\\]a-z{-}]+'; - const spaces = '\\s*'; - // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- RegExp for readability, no user input - const baggageRegex = new RegExp( - `^${keyRegex}${spaces}=${spaces}${valueRegex}(${spaces},${spaces}${keyRegex}${spaces}=${spaces}${valueRegex})*$`, - ); - return baggageRegex.test(baggage); -} diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index adfac32843f8..e4fd7e5a937e 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -14,7 +14,7 @@ import type { Client, Scope, Span, SpanAttributes } from '@sentry/types'; import { addNonEnumerableProperty, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; import type { APIContext, MiddlewareResponseHandler } from 'astro'; -import { getTracingMetaTags } from './meta'; +import { getTracingMetaTags } from '@sentry/node'; type MiddlewareOptions = { /** @@ -189,9 +189,14 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span? if (typeof htmlChunk !== 'string') { return htmlChunk; } + console.log({ scope }); + const { 'sentry-trace': sentryTrace, baggage } = getTracingMetaTags(span, scope, client); + + const sentryTraceMeta = ``; + const baggageMeta = baggage && ``; + + const content = `\n${sentryTraceMeta}`.concat(baggageMeta ? `\n${baggageMeta}` : '', '\n'); - const { sentryTrace, baggage } = getTracingMetaTags(span, scope, client); - const content = `\n${sentryTrace}\n${baggage}\n`; return htmlChunk.replace('', content); } diff --git a/packages/astro/test/server/meta.test.ts b/packages/astro/test/server/meta.test.ts deleted file mode 100644 index 8b65beaa4eaf..000000000000 --- a/packages/astro/test/server/meta.test.ts +++ /dev/null @@ -1,200 +0,0 @@ -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 = 1; - -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; - -const mockedScope = { - getPropagationContext: () => ({ - traceId: '123', - }), -} as any; - -describe('getTracingMetaTags', () => { - it('returns the tracing tags from the span, if it is provided', () => { - { - vi.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ - environment: 'production', - }); - - const tags = getTracingMetaTags(mockedSpan, mockedScope, mockedClient); - - expect(tags).toEqual({ - sentryTrace: '', - baggage: '', - }); - } - }); - - it('returns propagationContext DSC data if no span is available', () => { - const tags = getTracingMetaTags( - undefined, - { - getPropagationContext: () => ({ - traceId: '12345678901234567890123456789012', - sampled: true, - spanId: '1234567890123456', - dsc: { - environment: 'staging', - public_key: 'key', - trace_id: '12345678901234567890123456789012', - }, - }), - } as any, - mockedClient, - ); - - expect(tags).toEqual({ - sentryTrace: expect.stringMatching( - //, - ), - baggage: - '', - }); - }); - - it('returns only the `sentry-trace` tag if no DSC is available', () => { - vi.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ - trace_id: '', - public_key: undefined, - }); - - const tags = getTracingMetaTags( - // @ts-expect-error - only passing a partial span object - { - isRecording: () => true, - spanContext: () => { - return { - traceId: '12345678901234567890123456789012', - spanId: '1234567890123456', - traceFlags: TRACE_FLAG_SAMPLED, - }; - }, - transaction: undefined, - }, - mockedScope, - mockedClient, - ); - - expect(tags).toEqual({ - sentryTrace: '', - }); - }); - - it('returns only the `sentry-trace` tag if no DSC is available without a client', () => { - vi.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ - trace_id: '', - public_key: undefined, - }); - - const tags = getTracingMetaTags( - // @ts-expect-error - only passing a partial span object - { - isRecording: () => true, - spanContext: () => { - return { - traceId: '12345678901234567890123456789012', - spanId: '1234567890123456', - traceFlags: TRACE_FLAG_SAMPLED, - }; - }, - transaction: undefined, - }, - mockedScope, - undefined, - ); - - expect(tags).toEqual({ - sentryTrace: '', - }); - }); -}); - -describe('isValidBaggageString', () => { - it.each([ - 'sentry-environment=production', - 'sentry-environment=staging,sentry-public_key=key,sentry-trace_id=abc', - // @ is allowed in values - 'sentry-release=project@1.0.0', - // spaces are allowed around the delimiters - 'sentry-environment=staging , sentry-public_key=key ,sentry-release=myproject@1.0.0', - 'sentry-environment=staging , thirdparty=value ,sentry-release=myproject@1.0.0', - // these characters are explicitly allowed for keys in the baggage spec: - "!#$%&'*+-.^_`|~1234567890abcxyzABCXYZ=true", - // special characters in values are fine (except for ",;\ - see other test) - 'key=(value)', - 'key=[{(value)}]', - 'key=some$value', - 'key=more#value', - 'key=max&value', - 'key=max:value', - 'key=x=value', - ])('returns true if the baggage string is valid (%s)', baggageString => { - expect(isValidBaggageString(baggageString)).toBe(true); - }); - - it.each([ - // baggage spec doesn't permit leading spaces - ' sentry-environment=production,sentry-publickey=key,sentry-trace_id=abc', - // no spaces in keys or values - 'sentry-public key=key', - 'sentry-publickey=my key', - // no delimiters ("(),/:;<=>?@[\]{}") in keys - 'asdf(x=value', - 'asdf)x=value', - 'asdf,x=value', - 'asdf/x=value', - 'asdf:x=value', - 'asdf;x=value', - 'asdfx=value', - 'asdf?x=value', - 'asdf@x=value', - 'asdf[x=value', - 'asdf]x=value', - 'asdf\\x=value', - 'asdf{x=value', - 'asdf}x=value', - // no ,;\" in values - 'key=va,lue', - 'key=va;lue', - 'key=va\\lue', - 'key=va"lue"', - // baggage headers can have properties but we currently don't support them - 'sentry-environment=production;prop1=foo;prop2=bar,nextkey=value', - // no fishy stuff - 'absolutely not a valid baggage string', - 'val"/>', - 'something"/>', - '', - '/>', - '" onblur="alert("xss")', - ])('returns false if the baggage string is invalid (%s)', baggageString => { - expect(isValidBaggageString(baggageString)).toBe(false); - }); - - it('returns false if the baggage string is empty', () => { - expect(isValidBaggageString('')).toBe(false); - }); - - it('returns false if the baggage string is empty', () => { - expect(isValidBaggageString(undefined)).toBe(false); - }); -}); diff --git a/packages/astro/test/server/middleware.test.ts b/packages/astro/test/server/middleware.test.ts index a678fcceaee6..343c0e60cdc6 100644 --- a/packages/astro/test/server/middleware.test.ts +++ b/packages/astro/test/server/middleware.test.ts @@ -1,7 +1,8 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import * as SentryCore from '@sentry/core'; import * as SentryNode from '@sentry/node'; import type { Client, Span } from '@sentry/types'; -import { vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { handleRequest, interpolateRouteFromUrlAndParams } from '../../src/server/middleware'; @@ -28,10 +29,14 @@ describe('sentryMiddleware', () => { setPropagationContext: vi.fn(), getSpan: getSpanMock, setSDKProcessingMetadata: setSDKProcessingMetadataMock, + getPropagationContext: () => ({}), } as any; }); vi.spyOn(SentryNode, 'getActiveSpan').mockImplementation(getSpanMock); vi.spyOn(SentryNode, 'getClient').mockImplementation(() => ({}) as Client); + vi.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockImplementation(() => ({ + transaction: 'test', + })); }); const nextResult = Promise.resolve(new Response(null, { status: 200, headers: new Headers() })); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 85d001b465e5..c1a62d7d9b04 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -40,6 +40,7 @@ export { initOpenTelemetry, preloadOpenTelemetry } from './sdk/initOtel'; export { getAutoPerformanceIntegrations } from './integrations/tracing'; export { getSentryRelease, defaultStackParser } from './sdk/api'; export { createGetModuleFromFilename } from './utils/module'; +export { getTracingMetaTags } from './utils/meta'; export { makeNodeTransport } from './transports'; export { NodeClient } from './sdk/client'; export { cron } from './cron'; From a40028518481d62e2ee94d212d6e4b35340d69e0 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 31 Jul 2024 13:17:27 +0200 Subject: [PATCH 2/9] actually add files in node lol --- packages/node/src/utils/meta.ts | 84 ++++++++++++ packages/node/test/utils/meta.test.ts | 188 ++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 packages/node/src/utils/meta.ts create mode 100644 packages/node/test/utils/meta.test.ts diff --git a/packages/node/src/utils/meta.ts b/packages/node/src/utils/meta.ts new file mode 100644 index 000000000000..dadaee6edb4a --- /dev/null +++ b/packages/node/src/utils/meta.ts @@ -0,0 +1,84 @@ +import { + getDynamicSamplingContextFromClient, + getDynamicSamplingContextFromSpan, + getRootSpan, + spanToTraceHeader, +} from '@sentry/core'; +import type { Client, Scope, Span } from '@sentry/types'; +import { + TRACEPARENT_REGEXP, + dynamicSamplingContextToSentryBaggageHeader, + generateSentryTraceHeader, + logger, +} from '@sentry/utils'; + +/** + * Extracts the tracing data from the current span or from the client's scope (via transaction or propagation context) + * and serializes the data to tag contents. + * + * Use this function to obtain the tracing meta tags you can inject when rendering an HTML response to continue + * the server-initiated trace on the client. + * + * @param span the currently active span + * @param client the SDK's client + * + * @returns an object with the two meta tags. The object keys are the name of the meta tag, + * the respective value is the content. + */ +export function getTracingMetaTags( + span: Span | undefined, + scope: Scope, + client: Client | undefined, +): { 'sentry-trace': string; baggage?: string } { + const { dsc, sampled, traceId } = scope.getPropagationContext(); + const rootSpan = span && getRootSpan(span); + + const sentryTrace = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); + + const dynamicSamplingContext = rootSpan + ? getDynamicSamplingContextFromSpan(rootSpan) + : dsc + ? dsc + : client + ? getDynamicSamplingContextFromClient(traceId, client) + : undefined; + + const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + + const isValidSentryTraceHeader = TRACEPARENT_REGEXP.test(sentryTrace); + if (!isValidSentryTraceHeader) { + logger.warn('Invalid sentry-trace data. Returning empty "sentry-trace" meta tag'); + } + + const validBaggage = isValidBaggageString(baggage); + if (!validBaggage) { + logger.warn('Invalid baggage data. Not returning "baggage" meta tag'); + } + + return { + 'sentry-trace': isValidSentryTraceHeader ? sentryTrace : '', + ...(validBaggage && { baggage }), + }; +} + +/** + * Tests string against baggage spec as defined in: + * + * - W3C Baggage grammar: https://www.w3.org/TR/baggage/#definition + * - RFC7230 token definition: https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 + * + * exported for testing + */ +export function isValidBaggageString(baggage?: string): boolean { + if (!baggage || !baggage.length) { + return false; + } + const keyRegex = "[-!#$%&'*+.^_`|~A-Za-z0-9]+"; + const valueRegex = '[!#-+-./0-9:<=>?@A-Z\\[\\]a-z{-}]+'; + const spaces = '\\s*'; + // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- RegExp for readability, no user input + const baggageRegex = new RegExp( + `^${keyRegex}${spaces}=${spaces}${valueRegex}(${spaces},${spaces}${keyRegex}${spaces}=${spaces}${valueRegex})*$`, + ); + return baggageRegex.test(baggage); +} diff --git a/packages/node/test/utils/meta.test.ts b/packages/node/test/utils/meta.test.ts new file mode 100644 index 000000000000..d1582ef9d677 --- /dev/null +++ b/packages/node/test/utils/meta.test.ts @@ -0,0 +1,188 @@ +import * as SentryCore from '@sentry/core'; +import { SentrySpan } from '@sentry/core'; + +import { getTracingMetaTags, isValidBaggageString } from '../../src/utils/meta'; + +const TRACE_FLAG_SAMPLED = 1; + +const mockedSpan = new SentrySpan({ + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', + sampled: true, +}); + +const mockedClient = {} as any; + +const mockedScope = { + getPropagationContext: () => ({ + traceId: '123', + }), +} as any; + +describe('getTracingMetaTags', () => { + it('returns the tracing meta tags from the span, if it is provided', () => { + { + jest.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ + environment: 'production', + }); + + const tags = getTracingMetaTags(mockedSpan, mockedScope, mockedClient); + + expect(tags).toEqual({ + 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', + baggage: 'sentry-environment=production', + }); + } + }); + + it('returns propagationContext DSC data if no span is available', () => { + const tags = getTracingMetaTags( + undefined, + { + getPropagationContext: () => ({ + traceId: '12345678901234567890123456789012', + sampled: true, + spanId: '1234567890123456', + dsc: { + environment: 'staging', + public_key: 'key', + trace_id: '12345678901234567890123456789012', + }, + }), + } as any, + mockedClient, + ); + + expect(tags).toEqual({ + 'sentry-trace': expect.stringMatching(/12345678901234567890123456789012-(.{16})-1/), + baggage: 'sentry-environment=staging,sentry-public_key=key,sentry-trace_id=12345678901234567890123456789012', + }); + }); + + it('returns only the `sentry-trace` tag if no DSC is available', () => { + jest.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ + trace_id: '', + public_key: undefined, + }); + + const tags = getTracingMetaTags( + // @ts-expect-error - we don't need to provide all the properties + { + isRecording: () => true, + spanContext: () => { + return { + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', + traceFlags: TRACE_FLAG_SAMPLED, + }; + }, + }, + mockedScope, + mockedClient, + ); + + expect(tags).toEqual({ + 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', + }); + }); + + it('returns only the `sentry-trace` tag if no DSC is available without a client', () => { + jest.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ + trace_id: '', + public_key: undefined, + }); + + const tags = getTracingMetaTags( + // @ts-expect-error - we don't need to provide all the properties + { + isRecording: () => true, + spanContext: () => { + return { + traceId: '12345678901234567890123456789012', + spanId: '1234567890123456', + traceFlags: TRACE_FLAG_SAMPLED, + }; + }, + }, + mockedScope, + undefined, + ); + + expect(tags).toEqual({ + 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', + }); + expect('baggage' in tags).toBe(false); + }); +}); + +describe('isValidBaggageString', () => { + it.each([ + 'sentry-environment=production', + 'sentry-environment=staging,sentry-public_key=key,sentry-trace_id=abc', + // @ is allowed in values + 'sentry-release=project@1.0.0', + // spaces are allowed around the delimiters + 'sentry-environment=staging , sentry-public_key=key ,sentry-release=myproject@1.0.0', + 'sentry-environment=staging , thirdparty=value ,sentry-release=myproject@1.0.0', + // these characters are explicitly allowed for keys in the baggage spec: + "!#$%&'*+-.^_`|~1234567890abcxyzABCXYZ=true", + // special characters in values are fine (except for ",;\ - see other test) + 'key=(value)', + 'key=[{(value)}]', + 'key=some$value', + 'key=more#value', + 'key=max&value', + 'key=max:value', + 'key=x=value', + ])('returns true if the baggage string is valid (%s)', baggageString => { + expect(isValidBaggageString(baggageString)).toBe(true); + }); + + it.each([ + // baggage spec doesn't permit leading spaces + ' sentry-environment=production,sentry-publickey=key,sentry-trace_id=abc', + // no spaces in keys or values + 'sentry-public key=key', + 'sentry-publickey=my key', + // no delimiters ("(),/:;<=>?@[\]{}") in keys + 'asdf(x=value', + 'asdf)x=value', + 'asdf,x=value', + 'asdf/x=value', + 'asdf:x=value', + 'asdf;x=value', + 'asdfx=value', + 'asdf?x=value', + 'asdf@x=value', + 'asdf[x=value', + 'asdf]x=value', + 'asdf\\x=value', + 'asdf{x=value', + 'asdf}x=value', + // no ,;\" in values + 'key=va,lue', + 'key=va;lue', + 'key=va\\lue', + 'key=va"lue"', + // baggage headers can have properties but we currently don't support them + 'sentry-environment=production;prop1=foo;prop2=bar,nextkey=value', + // no fishy stuff + 'absolutely not a valid baggage string', + 'val"/>', + 'something"/>', + '', + '/>', + '" onblur="alert("xss")', + ])('returns false if the baggage string is invalid (%s)', baggageString => { + expect(isValidBaggageString(baggageString)).toBe(false); + }); + + it('returns false if the baggage string is empty', () => { + expect(isValidBaggageString('')).toBe(false); + }); + + it('returns false if the baggage string is empty', () => { + expect(isValidBaggageString(undefined)).toBe(false); + }); +}); From 91472d40881289f42578b8f8afb1feb052ceb39d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 31 Jul 2024 13:29:01 +0200 Subject: [PATCH 3/9] add missing exports, cleanup --- packages/astro/src/index.server.ts | 1 + packages/astro/src/server/middleware.ts | 1 - packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + 6 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index a235b6a16b83..d4f7ef75e7c7 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -55,6 +55,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, + getTracingMetaTags, graphqlIntegration, hapiIntegration, httpIntegration, diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index e4fd7e5a937e..796b902a60c3 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -189,7 +189,6 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span? if (typeof htmlChunk !== 'string') { return htmlChunk; } - console.log({ scope }); const { 'sentry-trace': sentryTrace, baggage } = getTracingMetaTags(span, scope, client); const sentryTraceMeta = ``; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index eee24075bdf8..27089d73c2cd 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -20,6 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, + getTracingMetaTags, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 199013b959ff..f2c155042d88 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -40,6 +40,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, + getTracingMetaTags, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 73e94aa5f271..030cf005ccae 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -20,6 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, + getTracingMetaTags, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 3a14771218e4..3058d16275a4 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -51,6 +51,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, + getTracingMetaTags, graphqlIntegration, hapiIntegration, httpIntegration, From 63283d0ed26fd8db1aa2082da04dec1a0dcbae2c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 31 Jul 2024 15:22:16 +0200 Subject: [PATCH 4/9] s/getTracingMetaTags/getTracingMetaTagValues --- packages/astro/src/index.server.ts | 2 +- packages/astro/src/server/middleware.ts | 4 ++-- .../test/integration/middleware/index.test.ts | 2 +- packages/astro/test/server/middleware.test.ts | 2 +- packages/aws-serverless/src/index.ts | 2 +- packages/bun/src/index.ts | 2 +- packages/google-cloud-serverless/src/index.ts | 2 +- packages/node/src/index.ts | 2 +- packages/node/src/utils/meta.ts | 22 ++++++++++++++----- packages/node/test/utils/meta.test.ts | 12 +++++----- packages/sveltekit/src/server/index.ts | 2 +- 11 files changed, 32 insertions(+), 22 deletions(-) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index d4f7ef75e7c7..af08cdfd383e 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -55,7 +55,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, - getTracingMetaTags, + getTracingMetaTagValues, graphqlIntegration, hapiIntegration, httpIntegration, diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index 796b902a60c3..df60322cfc79 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -14,7 +14,7 @@ import type { Client, Scope, Span, SpanAttributes } from '@sentry/types'; import { addNonEnumerableProperty, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; import type { APIContext, MiddlewareResponseHandler } from 'astro'; -import { getTracingMetaTags } from '@sentry/node'; +import { getTracingMetaTagValues } from '@sentry/node'; type MiddlewareOptions = { /** @@ -189,7 +189,7 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span? if (typeof htmlChunk !== 'string') { return htmlChunk; } - const { 'sentry-trace': sentryTrace, baggage } = getTracingMetaTags(span, scope, client); + const { 'sentry-trace': sentryTrace, baggage } = getTracingMetaTagValues(span, scope, client); const sentryTraceMeta = ``; const baggageMeta = baggage && ``; diff --git a/packages/astro/test/integration/middleware/index.test.ts b/packages/astro/test/integration/middleware/index.test.ts index 3b12508feaa7..3c48086a2ee2 100644 --- a/packages/astro/test/integration/middleware/index.test.ts +++ b/packages/astro/test/integration/middleware/index.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from 'vitest'; import { onRequest } from '../../../src/integration/middleware'; vi.mock('../../../src/server/meta', () => ({ - getTracingMetaTags: () => ({ + getTracingMetaTagValues: () => ({ sentryTrace: '', baggage: '', }), diff --git a/packages/astro/test/server/middleware.test.ts b/packages/astro/test/server/middleware.test.ts index 343c0e60cdc6..83c42fca0f37 100644 --- a/packages/astro/test/server/middleware.test.ts +++ b/packages/astro/test/server/middleware.test.ts @@ -7,7 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { handleRequest, interpolateRouteFromUrlAndParams } from '../../src/server/middleware'; vi.mock('../../src/server/meta', () => ({ - getTracingMetaTags: () => ({ + getTracingMetaTagValues: () => ({ sentryTrace: '', baggage: '', }), diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 27089d73c2cd..981fe56986e9 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -20,7 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTags, + getTracingMetaTagValues, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index f2c155042d88..c751bf92732d 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -40,7 +40,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTags, + getTracingMetaTagValues, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 030cf005ccae..9cddd632046d 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -20,7 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTags, + getTracingMetaTagValues, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index c1a62d7d9b04..100f6db8120d 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -40,7 +40,7 @@ export { initOpenTelemetry, preloadOpenTelemetry } from './sdk/initOtel'; export { getAutoPerformanceIntegrations } from './integrations/tracing'; export { getSentryRelease, defaultStackParser } from './sdk/api'; export { createGetModuleFromFilename } from './utils/module'; -export { getTracingMetaTags } from './utils/meta'; +export { getTracingMetaTagValues } from './utils/meta'; export { makeNodeTransport } from './transports'; export { NodeClient } from './sdk/client'; export { cron } from './cron'; diff --git a/packages/node/src/utils/meta.ts b/packages/node/src/utils/meta.ts index dadaee6edb4a..d7ca00c71cd4 100644 --- a/packages/node/src/utils/meta.ts +++ b/packages/node/src/utils/meta.ts @@ -13,19 +13,29 @@ import { } from '@sentry/utils'; /** - * Extracts the tracing data from the current span or from the client's scope (via transaction or propagation context) - * and serializes the data to tag contents. + * Extracts trace propagation data from the current span or from the client's scope (via transaction or propagation + * context) and serializes it to meta tag content values. * - * Use this function to obtain the tracing meta tags you can inject when rendering an HTML response to continue - * the server-initiated trace on the client. + * Use this function to obtain data for the tracing meta tags you can inject when rendering an HTML response to + * continue the server-initiated trace on the client. + * + * Example usage: + * + * ```js + * // render meta tags as html + * const tagValues = getTracingMetaTagValues(span, scope, client); + * return ` + * + * ${tagValues.baggage ? `` : ''}` + * ``` * * @param span the currently active span * @param client the SDK's client * - * @returns an object with the two meta tags. The object keys are the name of the meta tag, + * @returns an object with the values of the tracing meta tags. The object keys are the name of the meta tag, * the respective value is the content. */ -export function getTracingMetaTags( +export function getTracingMetaTagValues( span: Span | undefined, scope: Scope, client: Client | undefined, diff --git a/packages/node/test/utils/meta.test.ts b/packages/node/test/utils/meta.test.ts index d1582ef9d677..dace038ce466 100644 --- a/packages/node/test/utils/meta.test.ts +++ b/packages/node/test/utils/meta.test.ts @@ -1,7 +1,7 @@ import * as SentryCore from '@sentry/core'; import { SentrySpan } from '@sentry/core'; -import { getTracingMetaTags, isValidBaggageString } from '../../src/utils/meta'; +import { getTracingMetaTagValues, isValidBaggageString } from '../../src/utils/meta'; const TRACE_FLAG_SAMPLED = 1; @@ -19,14 +19,14 @@ const mockedScope = { }), } as any; -describe('getTracingMetaTags', () => { +describe('getTracingMetaTagValues', () => { it('returns the tracing meta tags from the span, if it is provided', () => { { jest.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ environment: 'production', }); - const tags = getTracingMetaTags(mockedSpan, mockedScope, mockedClient); + const tags = getTracingMetaTagValues(mockedSpan, mockedScope, mockedClient); expect(tags).toEqual({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', @@ -36,7 +36,7 @@ describe('getTracingMetaTags', () => { }); it('returns propagationContext DSC data if no span is available', () => { - const tags = getTracingMetaTags( + const tags = getTracingMetaTagValues( undefined, { getPropagationContext: () => ({ @@ -65,7 +65,7 @@ describe('getTracingMetaTags', () => { public_key: undefined, }); - const tags = getTracingMetaTags( + const tags = getTracingMetaTagValues( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, @@ -92,7 +92,7 @@ describe('getTracingMetaTags', () => { public_key: undefined, }); - const tags = getTracingMetaTags( + const tags = getTracingMetaTagValues( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 3058d16275a4..def49319c024 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -51,7 +51,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, - getTracingMetaTags, + getTracingMetaTagValues, graphqlIntegration, hapiIntegration, httpIntegration, From 63926bf9af21fb06058eaf5c0525415476d04d81 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Aug 2024 12:16:10 +0200 Subject: [PATCH 5/9] rework: export `getTraceData` from core --- packages/astro/src/index.server.ts | 2 +- packages/astro/src/server/middleware.ts | 4 +- packages/astro/test/server/middleware.test.ts | 4 ++ packages/aws-serverless/src/index.ts | 2 +- packages/bun/src/index.ts | 2 +- packages/cloudflare/src/index.ts | 1 + packages/core/src/index.ts | 1 + .../meta.ts => core/src/utils/traceData.ts} | 59 ++++++++----------- .../test/lib/utils/traceData.test.ts} | 22 +++---- packages/deno/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 2 +- packages/node/src/index.ts | 2 +- packages/sveltekit/src/server/index.ts | 2 +- packages/vercel-edge/src/index.ts | 1 + 14 files changed, 53 insertions(+), 52 deletions(-) rename packages/{node/src/utils/meta.ts => core/src/utils/traceData.ts} (54%) rename packages/{node/test/utils/meta.test.ts => core/test/lib/utils/traceData.test.ts} (87%) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index af08cdfd383e..1084643584d6 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -55,7 +55,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, - getTracingMetaTagValues, + getTraceData, graphqlIntegration, hapiIntegration, httpIntegration, diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index df60322cfc79..f1a1fc11c2c2 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -14,7 +14,7 @@ import type { Client, Scope, Span, SpanAttributes } from '@sentry/types'; import { addNonEnumerableProperty, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; import type { APIContext, MiddlewareResponseHandler } from 'astro'; -import { getTracingMetaTagValues } from '@sentry/node'; +import { getTraceData } from '@sentry/node'; type MiddlewareOptions = { /** @@ -189,7 +189,7 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span? if (typeof htmlChunk !== 'string') { return htmlChunk; } - const { 'sentry-trace': sentryTrace, baggage } = getTracingMetaTagValues(span, scope, client); + const { 'sentry-trace': sentryTrace, baggage } = getTraceData(span, scope, client); const sentryTraceMeta = ``; const baggageMeta = baggage && ``; diff --git a/packages/astro/test/server/middleware.test.ts b/packages/astro/test/server/middleware.test.ts index 83c42fca0f37..58405c8d1c12 100644 --- a/packages/astro/test/server/middleware.test.ts +++ b/packages/astro/test/server/middleware.test.ts @@ -34,6 +34,10 @@ describe('sentryMiddleware', () => { }); vi.spyOn(SentryNode, 'getActiveSpan').mockImplementation(getSpanMock); vi.spyOn(SentryNode, 'getClient').mockImplementation(() => ({}) as Client); + vi.spyOn(SentryNode, 'getTraceData').mockImplementation(() => ({ + 'sentry-trace': '123', + baggage: 'abc', + })); vi.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockImplementation(() => ({ transaction: 'test', })); diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 981fe56986e9..95b2d553f2d4 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -20,7 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTagValues, + getTraceData, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index c751bf92732d..287dbc26eeee 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -40,7 +40,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTagValues, + getTraceData, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index a4a466fa5bb5..867abd8e4a6e 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -55,6 +55,7 @@ export { setMeasurement, getActiveSpan, getRootSpan, + getTraceData, startSpan, startInactiveSpan, startSpanManual, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1971bb8c94bd..5c21c8e484ed 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -82,6 +82,7 @@ export { } from './utils/spanUtils'; export { parseSampleRate } from './utils/parseSampleRate'; export { applySdkMetadata } from './utils/sdkMetadata'; +export { getTraceData } from './utils/traceData'; export { DEFAULT_ENVIRONMENT } from './constants'; export { addBreadcrumb } from './breadcrumbs'; export { functionToStringIntegration } from './integrations/functiontostring'; diff --git a/packages/node/src/utils/meta.ts b/packages/core/src/utils/traceData.ts similarity index 54% rename from packages/node/src/utils/meta.ts rename to packages/core/src/utils/traceData.ts index d7ca00c71cd4..e88079c813f6 100644 --- a/packages/node/src/utils/meta.ts +++ b/packages/core/src/utils/traceData.ts @@ -1,9 +1,3 @@ -import { - getDynamicSamplingContextFromClient, - getDynamicSamplingContextFromSpan, - getRootSpan, - spanToTraceHeader, -} from '@sentry/core'; import type { Client, Scope, Span } from '@sentry/types'; import { TRACEPARENT_REGEXP, @@ -11,53 +5,52 @@ import { generateSentryTraceHeader, logger, } from '@sentry/utils'; +import { getClient, getCurrentScope } from '../currentScopes'; +import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan } from '../tracing'; +import { getActiveSpan, getRootSpan, spanToTraceHeader } from './spanUtils'; /** * Extracts trace propagation data from the current span or from the client's scope (via transaction or propagation - * context) and serializes it to meta tag content values. - * - * Use this function to obtain data for the tracing meta tags you can inject when rendering an HTML response to - * continue the server-initiated trace on the client. + * context) and serializes it to `sentry-trace` and `baggage` values to strings. These values can be used to propagate + * a trace via our tracing Http headers or Html `` tags. * - * Example usage: + * This function also applies some validation to the generated sentry-trace and baggage values to ensure that + * only valid strings are returned. * - * ```js - * // render meta tags as html - * const tagValues = getTracingMetaTagValues(span, scope, client); - * return ` - * - * ${tagValues.baggage ? `` : ''}` - * ``` + * @param span a span to take the trace data from. By default, the currently active span is used. + * @param scope the scope to take trace data from By default, the active current scope is used. + * @param client the SDK's client to take trace data from. By default, the current client is used. * - * @param span the currently active span - * @param client the SDK's client - * - * @returns an object with the values of the tracing meta tags. The object keys are the name of the meta tag, - * the respective value is the content. + * @returns an object with the tracing data values. The object keys are the name of the tracing key to be used as header + * or meta tag name. */ -export function getTracingMetaTagValues( - span: Span | undefined, - scope: Scope, - client: Client | undefined, +export function getTraceData( + span?: Span | undefined, + scope?: Scope, + client?: Client, ): { 'sentry-trace': string; baggage?: string } { - const { dsc, sampled, traceId } = scope.getPropagationContext(); - const rootSpan = span && getRootSpan(span); + const clientToUse = client || getClient(); + const scopeToUser = scope || getCurrentScope(); + const spanToUse = span || getActiveSpan(); + + const { dsc, sampled, traceId } = scopeToUser.getPropagationContext(); + const rootSpan = spanToUse && getRootSpan(spanToUse); - const sentryTrace = span ? spanToTraceHeader(span) : generateSentryTraceHeader(traceId, undefined, sampled); + const sentryTrace = spanToUse ? spanToTraceHeader(spanToUse) : generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = rootSpan ? getDynamicSamplingContextFromSpan(rootSpan) : dsc ? dsc - : client - ? getDynamicSamplingContextFromClient(traceId, client) + : clientToUse + ? getDynamicSamplingContextFromClient(traceId, clientToUse) : undefined; const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); const isValidSentryTraceHeader = TRACEPARENT_REGEXP.test(sentryTrace); if (!isValidSentryTraceHeader) { - logger.warn('Invalid sentry-trace data. Returning empty "sentry-trace" meta tag'); + logger.warn('Invalid sentry-trace data. Returning empty "sentry-trace" value'); } const validBaggage = isValidBaggageString(baggage); diff --git a/packages/node/test/utils/meta.test.ts b/packages/core/test/lib/utils/traceData.test.ts similarity index 87% rename from packages/node/test/utils/meta.test.ts rename to packages/core/test/lib/utils/traceData.test.ts index dace038ce466..05324fbde84d 100644 --- a/packages/node/test/utils/meta.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -1,7 +1,7 @@ -import * as SentryCore from '@sentry/core'; -import { SentrySpan } from '@sentry/core'; +import { SentrySpan, getTraceData } from '../../../src/'; +import * as SentryCoreTracing from '../../../src/tracing'; -import { getTracingMetaTagValues, isValidBaggageString } from '../../src/utils/meta'; +import { isValidBaggageString } from '../../../src/utils/traceData'; const TRACE_FLAG_SAMPLED = 1; @@ -19,14 +19,14 @@ const mockedScope = { }), } as any; -describe('getTracingMetaTagValues', () => { +describe('getTraceData', () => { it('returns the tracing meta tags from the span, if it is provided', () => { { - jest.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ + jest.spyOn(SentryCoreTracing, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ environment: 'production', }); - const tags = getTracingMetaTagValues(mockedSpan, mockedScope, mockedClient); + const tags = getTraceData(mockedSpan, mockedScope, mockedClient); expect(tags).toEqual({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', @@ -36,7 +36,7 @@ describe('getTracingMetaTagValues', () => { }); it('returns propagationContext DSC data if no span is available', () => { - const tags = getTracingMetaTagValues( + const tags = getTraceData( undefined, { getPropagationContext: () => ({ @@ -60,12 +60,12 @@ describe('getTracingMetaTagValues', () => { }); it('returns only the `sentry-trace` tag if no DSC is available', () => { - jest.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ + jest.spyOn(SentryCoreTracing, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ trace_id: '', public_key: undefined, }); - const tags = getTracingMetaTagValues( + const tags = getTraceData( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, @@ -87,12 +87,12 @@ describe('getTracingMetaTagValues', () => { }); it('returns only the `sentry-trace` tag if no DSC is available without a client', () => { - jest.spyOn(SentryCore, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ + jest.spyOn(SentryCoreTracing, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ trace_id: '', public_key: undefined, }); - const tags = getTracingMetaTagValues( + const tags = getTraceData( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index aa30c762d624..69b26bb1729a 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -55,6 +55,7 @@ export { setMeasurement, getActiveSpan, getRootSpan, + getTraceData, startSpan, startInactiveSpan, startSpanManual, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 9cddd632046d..351f843d2c2d 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -20,7 +20,7 @@ export { getCurrentScope, getGlobalScope, getIsolationScope, - getTracingMetaTagValues, + getTraceData, setCurrentClient, Scope, SDK_VERSION, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 100f6db8120d..3aa519c055d1 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -40,7 +40,6 @@ export { initOpenTelemetry, preloadOpenTelemetry } from './sdk/initOtel'; export { getAutoPerformanceIntegrations } from './integrations/tracing'; export { getSentryRelease, defaultStackParser } from './sdk/api'; export { createGetModuleFromFilename } from './utils/module'; -export { getTracingMetaTagValues } from './utils/meta'; export { makeNodeTransport } from './transports'; export { NodeClient } from './sdk/client'; export { cron } from './cron'; @@ -96,6 +95,7 @@ export { getCurrentHub, getCurrentScope, getIsolationScope, + getTraceData, withScope, withIsolationScope, captureException, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index def49319c024..a74e5bb89dc0 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -51,7 +51,7 @@ export { getSentryRelease, getSpanDescendants, getSpanStatusFromHttpCode, - getTracingMetaTagValues, + getTraceData, graphqlIntegration, hapiIntegration, httpIntegration, diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 6a768627b5d2..a96fc15e35d2 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -55,6 +55,7 @@ export { setMeasurement, getActiveSpan, getRootSpan, + getTraceData, startSpan, startInactiveSpan, startSpanManual, From 324b747b779a68797217cb881c97db04a221da32 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Aug 2024 12:17:53 +0200 Subject: [PATCH 6/9] adjust test --- packages/core/src/utils/traceData.ts | 2 +- packages/core/test/lib/utils/traceData.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/utils/traceData.ts b/packages/core/src/utils/traceData.ts index e88079c813f6..51352872c403 100644 --- a/packages/core/src/utils/traceData.ts +++ b/packages/core/src/utils/traceData.ts @@ -55,7 +55,7 @@ export function getTraceData( const validBaggage = isValidBaggageString(baggage); if (!validBaggage) { - logger.warn('Invalid baggage data. Not returning "baggage" meta tag'); + logger.warn('Invalid baggage data. Not returning "baggage" value'); } return { diff --git a/packages/core/test/lib/utils/traceData.test.ts b/packages/core/test/lib/utils/traceData.test.ts index 05324fbde84d..2e924ac60ec9 100644 --- a/packages/core/test/lib/utils/traceData.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -20,7 +20,7 @@ const mockedScope = { } as any; describe('getTraceData', () => { - it('returns the tracing meta tags from the span, if it is provided', () => { + it('returns the tracing data from the span, if a span is available', () => { { jest.spyOn(SentryCoreTracing, 'getDynamicSamplingContextFromSpan').mockReturnValueOnce({ environment: 'production', @@ -59,7 +59,7 @@ describe('getTraceData', () => { }); }); - it('returns only the `sentry-trace` tag if no DSC is available', () => { + it('returns only the `sentry-trace` value if no DSC is available', () => { jest.spyOn(SentryCoreTracing, 'getDynamicSamplingContextFromClient').mockReturnValueOnce({ trace_id: '', public_key: undefined, From 3106bf62fd752577ac4f3ce4820ddd2dea6deec6 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Aug 2024 13:44:52 +0200 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com> --- packages/core/src/utils/traceData.ts | 6 +++--- packages/core/test/lib/utils/traceData.test.ts | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/utils/traceData.ts b/packages/core/src/utils/traceData.ts index 51352872c403..2f1586fd24f5 100644 --- a/packages/core/src/utils/traceData.ts +++ b/packages/core/src/utils/traceData.ts @@ -25,15 +25,15 @@ import { getActiveSpan, getRootSpan, spanToTraceHeader } from './spanUtils'; * or meta tag name. */ export function getTraceData( - span?: Span | undefined, + span?: Span, scope?: Scope, client?: Client, ): { 'sentry-trace': string; baggage?: string } { const clientToUse = client || getClient(); - const scopeToUser = scope || getCurrentScope(); + const scopeToUse = scope || getCurrentScope(); const spanToUse = span || getActiveSpan(); - const { dsc, sampled, traceId } = scopeToUser.getPropagationContext(); + const { dsc, sampled, traceId } = scopeToUse.getPropagationContext(); const rootSpan = spanToUse && getRootSpan(spanToUse); const sentryTrace = spanToUse ? spanToTraceHeader(spanToUse) : generateSentryTraceHeader(traceId, undefined, sampled); diff --git a/packages/core/test/lib/utils/traceData.test.ts b/packages/core/test/lib/utils/traceData.test.ts index 2e924ac60ec9..d013b5893f7d 100644 --- a/packages/core/test/lib/utils/traceData.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -36,7 +36,7 @@ describe('getTraceData', () => { }); it('returns propagationContext DSC data if no span is available', () => { - const tags = getTraceData( + const traceData = getTraceData( undefined, { getPropagationContext: () => ({ @@ -53,7 +53,7 @@ describe('getTraceData', () => { mockedClient, ); - expect(tags).toEqual({ + expect(traceData).toEqual({ 'sentry-trace': expect.stringMatching(/12345678901234567890123456789012-(.{16})-1/), baggage: 'sentry-environment=staging,sentry-public_key=key,sentry-trace_id=12345678901234567890123456789012', }); @@ -65,7 +65,7 @@ describe('getTraceData', () => { public_key: undefined, }); - const tags = getTraceData( + const traceData = getTraceData( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, @@ -81,7 +81,7 @@ describe('getTraceData', () => { mockedClient, ); - expect(tags).toEqual({ + expect(traceData).toEqual({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', }); }); @@ -92,7 +92,7 @@ describe('getTraceData', () => { public_key: undefined, }); - const tags = getTraceData( + const traceData = getTraceData( // @ts-expect-error - we don't need to provide all the properties { isRecording: () => true, @@ -108,10 +108,10 @@ describe('getTraceData', () => { undefined, ); - expect(tags).toEqual({ + expect(traceData).toEqual({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', }); - expect('baggage' in tags).toBe(false); + expect('baggage' in traceData).toBe(false); }); }); From 2a3f134202e054774480f4b2c22c1d30a7ae2c1f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Aug 2024 14:59:03 +0200 Subject: [PATCH 8/9] improve invalid sentry-trace handling --- packages/astro/src/server/middleware.ts | 4 ++++ packages/core/src/utils/traceData.ts | 14 +++++++------ .../core/test/lib/utils/traceData.test.ts | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index f1a1fc11c2c2..6b668f462489 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -191,6 +191,10 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span? } const { 'sentry-trace': sentryTrace, baggage } = getTraceData(span, scope, client); + if (!sentryTrace) { + return htmlChunk; + } + const sentryTraceMeta = ``; const baggageMeta = baggage && ``; diff --git a/packages/core/src/utils/traceData.ts b/packages/core/src/utils/traceData.ts index 2f1586fd24f5..e24a56ce1032 100644 --- a/packages/core/src/utils/traceData.ts +++ b/packages/core/src/utils/traceData.ts @@ -9,6 +9,11 @@ import { getClient, getCurrentScope } from '../currentScopes'; import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan } from '../tracing'; import { getActiveSpan, getRootSpan, spanToTraceHeader } from './spanUtils'; +type TraceData = { + 'sentry-trace'?: string; + baggage?: string; +}; + /** * Extracts trace propagation data from the current span or from the client's scope (via transaction or propagation * context) and serializes it to `sentry-trace` and `baggage` values to strings. These values can be used to propagate @@ -24,11 +29,7 @@ import { getActiveSpan, getRootSpan, spanToTraceHeader } from './spanUtils'; * @returns an object with the tracing data values. The object keys are the name of the tracing key to be used as header * or meta tag name. */ -export function getTraceData( - span?: Span, - scope?: Scope, - client?: Client, -): { 'sentry-trace': string; baggage?: string } { +export function getTraceData(span?: Span, scope?: Scope, client?: Client): TraceData { const clientToUse = client || getClient(); const scopeToUse = scope || getCurrentScope(); const spanToUse = span || getActiveSpan(); @@ -50,7 +51,8 @@ export function getTraceData( const isValidSentryTraceHeader = TRACEPARENT_REGEXP.test(sentryTrace); if (!isValidSentryTraceHeader) { - logger.warn('Invalid sentry-trace data. Returning empty "sentry-trace" value'); + logger.warn('Invalid sentry-trace data. Cannot generate trace data'); + return {}; } const validBaggage = isValidBaggageString(baggage); diff --git a/packages/core/test/lib/utils/traceData.test.ts b/packages/core/test/lib/utils/traceData.test.ts index d013b5893f7d..e757926ca30d 100644 --- a/packages/core/test/lib/utils/traceData.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -113,6 +113,26 @@ describe('getTraceData', () => { }); expect('baggage' in traceData).toBe(false); }); + + it('returns an empty object if the `sentry-trace` value is invalid', () => { + const traceData = getTraceData( + // @ts-expect-error - we don't need to provide all the properties + { + isRecording: () => true, + spanContext: () => { + return { + traceId: '1234567890123456789012345678901+', + spanId: '1234567890123456', + traceFlags: TRACE_FLAG_SAMPLED, + }; + }, + }, + mockedScope, + mockedClient, + ); + + expect(traceData).toEqual({}); + }); }); describe('isValidBaggageString', () => { From a44cb30ff1fb5822cf5929ac1f481759616f8156 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Aug 2024 15:20:54 +0200 Subject: [PATCH 9/9] simplify --- packages/core/src/utils/traceData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils/traceData.ts b/packages/core/src/utils/traceData.ts index e24a56ce1032..abc05f449365 100644 --- a/packages/core/src/utils/traceData.ts +++ b/packages/core/src/utils/traceData.ts @@ -61,7 +61,7 @@ export function getTraceData(span?: Span, scope?: Scope, client?: Client): Trace } return { - 'sentry-trace': isValidSentryTraceHeader ? sentryTrace : '', + 'sentry-trace': sentryTrace, ...(validBaggage && { baggage }), }; }