From c3ca39b2828eeb2970a22f87baa51b76e5fbd38a Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Apr 2026 18:46:48 +0200 Subject: [PATCH 1/7] feat(core): Emit sentry.sdk.integrations on segment spans in span streaming In the classic (non-streaming) pipeline, SDK integrations ride on the transaction event wrapper via `event.sdk.integrations`. The span streaming pipeline emits segment spans directly as span envelope items with no transaction wrapper, so that metadata never reaches ingest. Add `sentry.sdk.integrations` as a native array attribute on segment spans (matching the wire contract Relay expects: `type: 'array'`). Relies on the homogeneous-primitive-array serializer support. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/core/src/semanticAttributes.ts | 2 + .../core/src/tracing/spans/captureSpan.ts | 11 +++ .../lib/tracing/spans/captureSpan.test.ts | 81 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/packages/core/src/semanticAttributes.ts b/packages/core/src/semanticAttributes.ts index 9eeed6378a5f..fd3b0e0c4022 100644 --- a/packages/core/src/semanticAttributes.ts +++ b/packages/core/src/semanticAttributes.ts @@ -52,6 +52,8 @@ export const SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID = 'sentry.segment.id'; export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME = 'sentry.sdk.name'; /** The version of the Sentry SDK */ export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION = 'sentry.sdk.version'; +/** The list of integrations enabled in the Sentry SDK (e.g., ["InboundFilters", "BrowserTracing"]) */ +export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS = 'sentry.sdk.integrations'; /** The user ID */ export const SEMANTIC_ATTRIBUTE_USER_ID = 'user.id'; diff --git a/packages/core/src/tracing/spans/captureSpan.ts b/packages/core/src/tracing/spans/captureSpan.ts index afbc8ad60358..08f6c4626c35 100644 --- a/packages/core/src/tracing/spans/captureSpan.ts +++ b/packages/core/src/tracing/spans/captureSpan.ts @@ -6,6 +6,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_RELEASE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -64,6 +65,7 @@ export function captureSpan(span: Span, client: Client): SerializedStreamedSpanW if (spanJSON.is_segment) { applyScopeToSegmentSpan(spanJSON, finalScopeData); + applySdkMetadataToSegmentSpan(spanJSON, client); // Allow hook subscribers to mutate the segment span JSON // This also invokes the `processSegmentSpan` hook of all integrations client.emit('processSegmentSpan', spanJSON); @@ -118,6 +120,15 @@ export function safeSetSpanJSONAttributes( }); } +function applySdkMetadataToSegmentSpan(segmentSpanJSON: StreamedSpanJSON, client: Client): void { + const integrationNames = client.getOptions().integrations.map(i => i.name); + if (!integrationNames.length) return; + + safeSetSpanJSONAttributes(segmentSpanJSON, { + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: integrationNames, + }); +} + function applyCommonSpanAttributes( spanJSON: StreamedSpanJSON, serializedSegmentSpan: StreamedSpanJSON, diff --git a/packages/core/test/lib/tracing/spans/captureSpan.test.ts b/packages/core/test/lib/tracing/spans/captureSpan.test.ts index 524f3287e82c..35da3a93200a 100644 --- a/packages/core/test/lib/tracing/spans/captureSpan.test.ts +++ b/packages/core/test/lib/tracing/spans/captureSpan.test.ts @@ -7,6 +7,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_RELEASE, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -230,6 +231,86 @@ describe('captureSpan', () => { }); }); + it('adds sentry.sdk.integrations to segment spans as an array attribute', () => { + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: 'https://dsn@ingest.f00.f00/1', + tracesSampleRate: 1, + release: '1.0.0', + environment: 'staging', + integrations: [ + { name: 'InboundFilters', setupOnce: () => {} }, + { name: 'BrowserTracing', setupOnce: () => {} }, + ], + _metadata: { + sdk: { + name: 'sentry.javascript.browser', + version: '9.0.0', + }, + }, + }), + ); + + const span = withScope(scope => { + scope.setClient(client); + const span = startInactiveSpan({ name: 'my-span', attributes: { 'sentry.op': 'http.client' } }); + span.end(); + return span; + }); + + expect(captureSpan(span, client)).toStrictEqual({ + span_id: expect.stringMatching(/^[\da-f]{16}$/), + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + parent_span_id: undefined, + links: undefined, + start_timestamp: expect.any(Number), + name: 'my-span', + end_timestamp: expect.any(Number), + status: 'ok', + is_segment: true, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: { type: 'string', value: 'http.client' }, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'manual' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME]: { value: 'my-span', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { value: span.spanContext().spanId, type: 'string' }, + 'sentry.span.source': { value: 'custom', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: { value: 'custom', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { value: '1.0.0', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT]: { value: 'staging', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { value: 'sentry.javascript.browser', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { value: '9.0.0', type: 'string' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: ['InboundFilters', 'BrowserTracing'], + }, + }, + _segmentSpan: span, + }); + }); + + it('does not add sentry.sdk.integrations to non-segment child spans', () => { + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: 'https://dsn@ingest.f00.f00/1', + tracesSampleRate: 1, + integrations: [{ name: 'InboundFilters', setupOnce: () => {} }], + }), + ); + + const serializedChild = withScope(scope => { + scope.setClient(client); + return startSpan({ name: 'segment' }, () => { + const childSpan = startInactiveSpan({ name: 'child' }); + childSpan.end(); + return captureSpan(childSpan, client); + }); + }); + + expect(serializedChild.is_segment).toBe(false); + expect(serializedChild.attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]).toBeUndefined(); + }); + describe('client hooks', () => { it('calls processSpan and processSegmentSpan hooks for a segment span', () => { const client = new TestClient( From 6b3d368f27e029f5b9d55ae16d1adc3ad42aa397 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Apr 2026 20:50:05 +0200 Subject: [PATCH 2/7] test: Update streamed segment span expectations for sentry.sdk.integrations Segment spans now ship a sentry.sdk.integrations attribute listing the active SDK integrations. Add the attribute to the full-shape assertions in the browser unit test, the browser/node/cloudflare streamed integration tests, and the Deno e2e test. Matching uses expect.arrayContaining against the explicit integrations configured in each scenario, to stay resilient to default-integration churn. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../suites/public-api/startSpan/streamed/test.ts | 5 +++++ .../browserTracingIntegration/interactions-streamed/test.ts | 5 +++++ .../browserTracingIntegration/navigation-streamed/test.ts | 5 +++++ .../browserTracingIntegration/pageload-streamed/test.ts | 5 +++++ .../suites/public-api/startSpan-streamed/test.ts | 6 ++++-- .../test-applications/deno-streamed/tests/spans.test.ts | 4 ++++ .../public-api/startSpan/basic-usage-streamed/test.ts | 5 +++++ packages/browser/test/integrations/spanstreaming.test.ts | 6 +++++- 8 files changed, 38 insertions(+), 3 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts index 1f1e44e97c43..5eb989f8b268 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/startSpan/streamed/test.ts @@ -4,6 +4,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -209,6 +210,10 @@ sentryTest( type: 'string', value: SDK_VERSION, }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: segmentSpanId, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts index 383ecade3530..b1aaa8c6dd42 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/interactions-streamed/test.ts @@ -4,6 +4,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -80,6 +81,10 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser type: 'string', value: SDK_VERSION, }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: interactionSegmentSpan!.span_id, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts index c2dcad317a3f..0ea3bbe26eeb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts @@ -4,6 +4,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -131,6 +132,10 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow type: 'string', value: SDK_VERSION, }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), + }, 'sentry.segment.id': { type: 'string', value: navigationSpan.span_id, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts index 2344e28c67d4..2068ffef5ef0 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts @@ -4,6 +4,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -138,6 +139,10 @@ sentryTest( type: 'string', value: SDK_VERSION, }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: pageloadSpan?.span_id, diff --git a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts index d9c18431202b..ef7b960ea816 100644 --- a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts @@ -2,13 +2,14 @@ import type { Envelope, SerializedStreamedSpanContainer } from '@sentry/core'; import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_RELEASE, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, } from '@sentry/core'; import { expect, it } from 'vitest'; @@ -175,6 +176,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { type: 'array', value: expect.arrayContaining(['SpanStreaming']) }, [SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { type: 'string', value: '1.0.0' }, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'auto.http.cloudflare' }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: segmentSpanId }, diff --git a/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts b/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts index 11f87c9229db..7ed898351fc2 100644 --- a/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts @@ -95,6 +95,10 @@ const SEGMENT_SPAN = { type: 'string', value: expect.any(String), }, + 'sentry.sdk.integrations': { + type: 'array', + value: expect.arrayContaining(['SpanStreaming']), + }, 'sentry.segment.id': { type: 'string', value: expect.stringMatching(/^[\da-f]{16}$/), diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts index 171fe13fce5c..372ce9750d90 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts @@ -3,6 +3,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_RELEASE, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -128,6 +129,10 @@ test('sends a streamed span envelope with correct spans for a manually started s [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { type: 'string', value: 'sentry.javascript.node' }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: segmentSpanId }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME]: { type: 'string', value: 'test-span' }, [SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { type: 'string', value: '1.0.0' }, diff --git a/packages/browser/test/integrations/spanstreaming.test.ts b/packages/browser/test/integrations/spanstreaming.test.ts index cc51ef6e6c17..fd3ef3e2a1af 100644 --- a/packages/browser/test/integrations/spanstreaming.test.ts +++ b/packages/browser/test/integrations/spanstreaming.test.ts @@ -1,5 +1,5 @@ import * as SentryCore from '@sentry/core/browser'; -import { debug } from '@sentry/core/browser'; +import { debug, SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS } from '@sentry/core/browser'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { BrowserClient, spanStreamingIntegration } from '../../src'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; @@ -145,6 +145,10 @@ describe('spanStreamingIntegration', () => { type: 'string', value: expect.any(String), }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: ['SpanStreaming'], + }, 'sentry.segment.id': { type: 'string', value: span.spanContext().spanId, From 8564b13b50e0368d9fee608c7a2c81e8d7a18233 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 21 Apr 2026 20:52:47 +0200 Subject: [PATCH 3/7] chore(format): Wrap long sentry.sdk.integrations attribute lines oxfmt wants the SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS entries broken across multiple lines because they exceed the max line length. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../suites/public-api/startSpan-streamed/test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts index ef7b960ea816..4ac80208eed2 100644 --- a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan-streamed/test.ts @@ -176,7 +176,10 @@ it('sends a streamed span envelope with correct spans for a manually started spa attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION }, - [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { type: 'array', value: expect.arrayContaining(['SpanStreaming']) }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { type: 'string', value: '1.0.0' }, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'auto.http.cloudflare' }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: segmentSpanId }, From b8aa456c9adaf1689040047584691d508a19ecea Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Apr 2026 09:54:44 +0200 Subject: [PATCH 4/7] test(node-core): Add sentry.sdk.integrations to streamed segment span expectation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same fix applied to node-integration-tests earlier — the node-core-integration-tests streamed basic-usage scenario also needs sentry.sdk.integrations in its full-shape assertion. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../suites/public-api/startSpan/basic-usage-streamed/test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev-packages/node-core-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts index e8d39fcd3f58..98cbcdf0f9c8 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts @@ -3,6 +3,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_RELEASE, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS, SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID, @@ -128,6 +129,10 @@ test('sends a streamed span envelope with correct spans for a manually started s [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { type: 'string', value: 'sentry.javascript.node-core' }, [SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION }, + [SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['SpanStreaming']), + }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { type: 'string', value: segmentSpanId }, [SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME]: { type: 'string', value: 'test-span' }, [SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { type: 'string', value: '1.0.0' }, From cc693ed7037d55869b5811e07fda764747c32452 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 13 May 2026 11:14:47 +0200 Subject: [PATCH 5/7] cache --- packages/core/src/tracing/spans/captureSpan.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/spans/captureSpan.ts b/packages/core/src/tracing/spans/captureSpan.ts index 08f6c4626c35..3c1a1f276cc6 100644 --- a/packages/core/src/tracing/spans/captureSpan.ts +++ b/packages/core/src/tracing/spans/captureSpan.ts @@ -120,8 +120,15 @@ export function safeSetSpanJSONAttributes( }); } +const integrationNamesCache = new WeakMap(); + function applySdkMetadataToSegmentSpan(segmentSpanJSON: StreamedSpanJSON, client: Client): void { - const integrationNames = client.getOptions().integrations.map(i => i.name); + let integrationNames = integrationNamesCache.get(client); + if (!integrationNames) { + integrationNames = client.getOptions().integrations.map(i => i.name); + integrationNamesCache.set(client, integrationNames); + } + if (!integrationNames.length) return; safeSetSpanJSONAttributes(segmentSpanJSON, { From 585ce8c0167c2654292fb3bcc9e5159b1414521a Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 13 May 2026 11:50:18 +0200 Subject: [PATCH 6/7] dynamic integration list --- packages/core/src/client.ts | 11 +++++++++-- packages/core/src/tracing/spans/captureSpan.ts | 9 +-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 94cc00e19f88..dc56b6f22a3c 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -500,6 +500,13 @@ export abstract class Client { return this._integrations[integrationName] as T | undefined; } + /** + * Returns the names of all installed integrations. + */ + public getIntegrationNames(): string[] { + return Object.keys(this._integrations); + } + /** * Add an integration to the client. * This can be used to e.g. lazy load integrations. @@ -1302,8 +1309,8 @@ export abstract class Client { isolationScope: Scope, ): PromiseLike { const options = this.getOptions(); - const integrations = Object.keys(this._integrations); - if (!hint.integrations && integrations?.length) { + const integrations = this.getIntegrationNames(); + if (!hint.integrations && integrations.length) { hint.integrations = integrations; } diff --git a/packages/core/src/tracing/spans/captureSpan.ts b/packages/core/src/tracing/spans/captureSpan.ts index 3c1a1f276cc6..18cec8fa532a 100644 --- a/packages/core/src/tracing/spans/captureSpan.ts +++ b/packages/core/src/tracing/spans/captureSpan.ts @@ -120,15 +120,8 @@ export function safeSetSpanJSONAttributes( }); } -const integrationNamesCache = new WeakMap(); - function applySdkMetadataToSegmentSpan(segmentSpanJSON: StreamedSpanJSON, client: Client): void { - let integrationNames = integrationNamesCache.get(client); - if (!integrationNames) { - integrationNames = client.getOptions().integrations.map(i => i.name); - integrationNamesCache.set(client, integrationNames); - } - + const integrationNames = client.getIntegrationNames(); if (!integrationNames.length) return; safeSetSpanJSONAttributes(segmentSpanJSON, { From cee7f404d3d2442c73f1a1b1fd1ec9bbef8d5832 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 13 May 2026 11:57:11 +0200 Subject: [PATCH 7/7] test(core): Init client in integration attribute tests so _integrations is populated Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/core/test/lib/tracing/spans/captureSpan.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/test/lib/tracing/spans/captureSpan.test.ts b/packages/core/test/lib/tracing/spans/captureSpan.test.ts index 35da3a93200a..fb906d783499 100644 --- a/packages/core/test/lib/tracing/spans/captureSpan.test.ts +++ b/packages/core/test/lib/tracing/spans/captureSpan.test.ts @@ -250,6 +250,7 @@ describe('captureSpan', () => { }, }), ); + client.init(); const span = withScope(scope => { scope.setClient(client); @@ -297,6 +298,7 @@ describe('captureSpan', () => { integrations: [{ name: 'InboundFilters', setupOnce: () => {} }], }), ); + client.init(); const serializedChild = withScope(scope => { scope.setClient(client);