Skip to content

Commit 0609305

Browse files
committed
implement
1 parent a87183e commit 0609305

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

packages/core/src/semanticAttributes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export const SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID = 'sentry.segment.id';
5252
export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME = 'sentry.sdk.name';
5353
/** The version of the Sentry SDK */
5454
export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION = 'sentry.sdk.version';
55+
/** The list of integrations enabled in the Sentry SDK (e.g., ["InboundFilters", "BrowserTracing"]) */
56+
export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS = 'sentry.sdk.integrations';
57+
/** The list of SDK packages loaded by the application (e.g., ["npm:@sentry/browser@9.0.0"]) */
58+
export const SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES = 'sentry.sdk.packages';
5559

5660
/** The user ID (gated by sendDefaultPii) */
5761
export const SEMANTIC_ATTRIBUTE_USER_ID = 'user.id';

packages/core/src/tracing/spans/captureSpan.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import type { ScopeData } from '../../scope';
44
import {
55
SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT,
66
SEMANTIC_ATTRIBUTE_SENTRY_RELEASE,
7+
SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS,
78
SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME,
9+
SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES,
810
SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION,
911
SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID,
1012
SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME,
@@ -53,6 +55,7 @@ export function captureSpan(span: Span, client: Client): SerializedStreamedSpanW
5355

5456
if (spanJSON.is_segment) {
5557
applyScopeToSegmentSpan(spanJSON, finalScopeData);
58+
applySdkMetadataToSegmentSpan(spanJSON, client);
5659
// Allow hook subscribers to mutate the segment span JSON
5760
// This also invokes the `processSegmentSpan` hook of all integrations
5861
client.emit('processSegmentSpan', spanJSON);
@@ -90,6 +93,18 @@ function applyScopeToSegmentSpan(_segmentSpanJSON: StreamedSpanJSON, _scopeData:
9093
// This will follow in a separate PR
9194
}
9295

96+
function applySdkMetadataToSegmentSpan(segmentSpanJSON: StreamedSpanJSON, client: Client): void {
97+
const integrationNames = client.getOptions().integrations.map(i => i.name);
98+
const packages = client.getSdkMetadata()?.sdk?.packages?.map(p => `${p.name}@${p.version}`);
99+
100+
safeSetSpanJSONAttributes(segmentSpanJSON, {
101+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: integrationNames.length
102+
? JSON.stringify(integrationNames)
103+
: undefined,
104+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES]: packages?.length ? JSON.stringify(packages) : undefined,
105+
});
106+
}
107+
93108
function applyCommonSpanAttributes(
94109
spanJSON: StreamedSpanJSON,
95110
serializedSegmentSpan: StreamedSpanJSON,

packages/core/test/lib/tracing/spans/captureSpan.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
88
SEMANTIC_ATTRIBUTE_SENTRY_RELEASE,
99
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
10+
SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS,
1011
SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME,
12+
SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES,
1113
SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION,
1214
SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID,
1315
SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME,
@@ -291,6 +293,102 @@ describe('captureSpan', () => {
291293
});
292294
});
293295

296+
it('adds sentry.sdk.integrations and sentry.sdk.packages to segment spans as JSON-stringified strings', () => {
297+
const client = new TestClient(
298+
getDefaultTestClientOptions({
299+
dsn: 'https://dsn@ingest.f00.f00/1',
300+
tracesSampleRate: 1,
301+
release: '1.0.0',
302+
environment: 'staging',
303+
integrations: [
304+
{ name: 'InboundFilters', setupOnce: () => {} },
305+
{ name: 'BrowserTracing', setupOnce: () => {} },
306+
],
307+
_metadata: {
308+
sdk: {
309+
name: 'sentry.javascript.browser',
310+
version: '9.0.0',
311+
packages: [
312+
{ name: 'npm:@sentry/browser', version: '9.0.0' },
313+
{ name: 'npm:@sentry/core', version: '9.0.0' },
314+
],
315+
},
316+
},
317+
}),
318+
);
319+
320+
const span = withScope(scope => {
321+
scope.setClient(client);
322+
const span = startInactiveSpan({ name: 'my-span', attributes: { 'sentry.op': 'http.client' } });
323+
span.end();
324+
return span;
325+
});
326+
327+
expect(captureSpan(span, client)).toStrictEqual({
328+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
329+
trace_id: expect.stringMatching(/^[\da-f]{32}$/),
330+
parent_span_id: undefined,
331+
links: undefined,
332+
start_timestamp: expect.any(Number),
333+
name: 'my-span',
334+
end_timestamp: expect.any(Number),
335+
status: 'ok',
336+
is_segment: true,
337+
attributes: {
338+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: { type: 'string', value: 'http.client' },
339+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'manual' },
340+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 },
341+
[SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME]: { value: 'my-span', type: 'string' },
342+
[SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: { value: span.spanContext().spanId, type: 'string' },
343+
'sentry.span.source': { value: 'custom', type: 'string' },
344+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: { value: 'custom', type: 'string' },
345+
[SEMANTIC_ATTRIBUTE_SENTRY_RELEASE]: { value: '1.0.0', type: 'string' },
346+
[SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT]: { value: 'staging', type: 'string' },
347+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: { value: 'sentry.javascript.browser', type: 'string' },
348+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: { value: '9.0.0', type: 'string' },
349+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]: {
350+
type: 'string',
351+
value: '["InboundFilters","BrowserTracing"]',
352+
},
353+
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES]: {
354+
type: 'string',
355+
value: '["npm:@sentry/browser@9.0.0","npm:@sentry/core@9.0.0"]',
356+
},
357+
},
358+
_segmentSpan: span,
359+
});
360+
});
361+
362+
it('does not add sentry.sdk.integrations or sentry.sdk.packages to non-segment child spans', () => {
363+
const client = new TestClient(
364+
getDefaultTestClientOptions({
365+
dsn: 'https://dsn@ingest.f00.f00/1',
366+
tracesSampleRate: 1,
367+
integrations: [{ name: 'InboundFilters', setupOnce: () => {} }],
368+
_metadata: {
369+
sdk: {
370+
name: 'sentry.javascript.browser',
371+
version: '9.0.0',
372+
packages: [{ name: 'npm:@sentry/browser', version: '9.0.0' }],
373+
},
374+
},
375+
}),
376+
);
377+
378+
const serializedChild = withScope(scope => {
379+
scope.setClient(client);
380+
return startSpan({ name: 'segment' }, () => {
381+
const childSpan = startInactiveSpan({ name: 'child' });
382+
childSpan.end();
383+
return captureSpan(childSpan, client);
384+
});
385+
});
386+
387+
expect(serializedChild.is_segment).toBe(false);
388+
expect(serializedChild.attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS]).toBeUndefined();
389+
expect(serializedChild.attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_SDK_PACKAGES]).toBeUndefined();
390+
});
391+
294392
describe('client hooks', () => {
295393
it('calls processSpan and processSegmentSpan hooks for a segment span', () => {
296394
const client = new TestClient(

0 commit comments

Comments
 (0)