Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.feedback.min.js'),
gzip: false,
brotli: false,
limit: '271 KB',
limit: '272 KB',
disablePlugins: ['@size-limit/esbuild'],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
'sentry.message.template': { value: 'Mixed: {} {} {} {}', type: 'string' },
'sentry.message.parameter.0': { value: 'prefix', type: 'string' },
'sentry.message.parameter.1': { value: '{"obj":true}', type: 'string' },
'sentry.message.parameter.2': { value: '[4,5,6]', type: 'string' },
'sentry.message.parameter.2': { value: [4, 5, 6], type: 'array' },
'sentry.message.parameter.3': { value: 'suffix', type: 'string' },
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ Sentry.logger.info('log_before_any_scope', { log_attr: 'log_attr_1' });

Sentry.getGlobalScope().setAttributes({ global_scope_attr: true });

// this attribute will not be sent for now
Sentry.getGlobalScope().setAttribute('array_attr', [1, 2, 3]);

// global scope, log attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ sentryTest('captures logs with scope attributes', async ({ getLocalTestUrl, page
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
'sentry.timestamp.sequence': { value: expect.any(Number), type: 'integer' },
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
log_attr: { value: 'log_attr_2', type: 'string' },
},
},
Expand All @@ -61,6 +62,7 @@ sentryTest('captures logs with scope attributes', async ({ getLocalTestUrl, page
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
'sentry.timestamp.sequence': { value: expect.any(Number), type: 'integer' },
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_3', type: 'string' },
},
Expand All @@ -76,6 +78,7 @@ sentryTest('captures logs with scope attributes', async ({ getLocalTestUrl, page
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
'sentry.timestamp.sequence': { value: expect.any(Number), type: 'integer' },
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
scope_attr: { value: 200, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_4', type: 'string' },
Expand All @@ -92,6 +95,7 @@ sentryTest('captures logs with scope attributes', async ({ getLocalTestUrl, page
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
'sentry.timestamp.sequence': { value: expect.any(Number), type: 'integer' },
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
scope_2_attr: { value: 300, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_5', type: 'string' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -207,6 +208,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -175,6 +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_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 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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}$/),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -129,6 +130,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' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ async function run(): Promise<void> {

Sentry.getGlobalScope().setAttribute('global_scope_attr', true);

// this attribute will not be sent for now
Sentry.getGlobalScope().setAttributes({ array_attr: [1, 2, 3] });

// global scope, log attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('logs', () => {
attributes: {
...commonAttributes,
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
log_attr: { value: 'log_attr_2', type: 'string' },
},
},
Expand All @@ -72,6 +73,7 @@ describe('logs', () => {
attributes: {
...commonAttributes,
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_3', type: 'string' },
},
Expand All @@ -85,6 +87,7 @@ describe('logs', () => {
attributes: {
...commonAttributes,
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
scope_attr: { value: 200, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_4', type: 'string' },
Expand All @@ -99,6 +102,7 @@ describe('logs', () => {
attributes: {
...commonAttributes,
global_scope_attr: { value: true, type: 'boolean' },
array_attr: { value: [1, 2, 3], type: 'array' },
isolation_scope_1_attr: { value: 100, unit: 'millisecond', type: 'integer' },
scope_2_attr: { value: 300, unit: 'millisecond', type: 'integer' },
log_attr: { value: 'log_attr_5', type: 'string' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -129,6 +130,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' },
Expand Down
6 changes: 5 additions & 1 deletion packages/browser/test/integrations/spanstreaming.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as SentryCore from '@sentry/core';
import { debug } from '@sentry/core';
import { debug, SEMANTIC_ATTRIBUTE_SENTRY_SDK_INTEGRATIONS } from '@sentry/core';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { BrowserClient, spanStreamingIntegration } from '../../src';
import { getDefaultBrowserClientOptions } from '../helper/browser-client-options';
Expand Down Expand Up @@ -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,
Expand Down
34 changes: 20 additions & 14 deletions packages/core/src/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ type AttributeTypeMap = {
integer: number;
double: number;
boolean: boolean;
'string[]': Array<string>;
'integer[]': Array<number>;
'double[]': Array<number>;
'boolean[]': Array<boolean>;
array: Array<string> | Array<number> | Array<boolean>;
};

/* Generates a type from the AttributeTypeMap like:
Expand Down Expand Up @@ -66,9 +63,9 @@ export function isAttributeObject(maybeObj: unknown): maybeObj is AttributeObjec
/**
* Converts an attribute value to a typed attribute value.
*
* For now, we intentionally only support primitive values and attribute objects with primitive values.
* If @param useFallback is true, we stringify non-primitive values to a string attribute value. Otherwise
* we return `undefined` for unsupported values.
* For now, we support primitive values and homogeneous arrays of primitives, either raw or
* inside attribute objects. If @param useFallback is true, we stringify other non-primitive values
* to a string attribute value. Otherwise we return `undefined` for unsupported values.
*
* @param value - The value of the passed attribute.
* @param useFallback - If true, unsupported values will be stringified to a string attribute value.
Expand Down Expand Up @@ -170,17 +167,18 @@ function estimatePrimitiveSizeInBytes(value: Primitive): number {
}

/**
* NOTE: We intentionally do not return anything for non-primitive values:
* - array support will come in the future but if we stringify arrays now,
* sending arrays (unstringified) later will be a subtle breaking change.
* NOTE: We return typed attributes for primitives and homogeneous arrays of primitives:
* - Homogeneous primitive arrays ship with `type: 'array'` (Relay's wire tag for arrays).
* - Mixed-type and nested arrays are not supported and return undefined.
* - Objects are not supported yet and product support is still TBD.
* - We still keep the type signature for TypedAttributeValue wider to avoid a
* breaking change once we add support for non-primitive values.
* - Once we go back to supporting arrays and stringifying all other values,
* we already implemented the serialization logic here:
* https://github.com/getsentry/sentry-javascript/pull/18165
* breaking change once we add support for other non-primitive values.
*/
function getTypedAttributeValue(value: unknown): TypedAttributeValue | void {
if (isHomogeneousPrimitiveArray(value)) {
return { value, type: 'array' };
}

const primitiveType =
typeof value === 'string'
? 'string'
Expand All @@ -201,3 +199,11 @@ function getTypedAttributeValue(value: unknown): TypedAttributeValue | void {
return { value, type: primitiveType };
}
}

function isHomogeneousPrimitiveArray(arr: unknown): arr is Array<string> | Array<number> | Array<boolean> {
if (!Array.isArray(arr)) return false;
if (arr.length === 0) return true;
const t = typeof arr[0];
if (t !== 'string' && t !== 'number' && t !== 'boolean') return false;
return arr.every(v => typeof v === t && (t !== 'number' || !Number.isNaN(v)));
}
2 changes: 2 additions & 0 deletions packages/core/src/semanticAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (gated by sendDefaultPii) */
export const SEMANTIC_ATTRIBUTE_USER_ID = 'user.id';
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/tracing/spans/captureSpan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -63,6 +64,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);
Expand Down Expand Up @@ -100,6 +102,15 @@ function applyScopeToSegmentSpan(_segmentSpanJSON: StreamedSpanJSON, _scopeData:
// This will follow in a separate PR
}

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,
Expand Down
Loading
Loading