Skip to content
Merged
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
8 changes: 4 additions & 4 deletions packages/core/src/semanticAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ 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 user ID (gated by sendDefaultPii) */
/** The user ID */
export const SEMANTIC_ATTRIBUTE_USER_ID = 'user.id';
/** The user email (gated by sendDefaultPii) */
/** The user email */
export const SEMANTIC_ATTRIBUTE_USER_EMAIL = 'user.email';
/** The user IP address (gated by sendDefaultPii) */
/** The user IP address */
export const SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS = 'user.ip_address';
/** The user username (gated by sendDefaultPii) */
/** The user username */
export const SEMANTIC_ATTRIBUTE_USER_USERNAME = 'user.name';

/**
Expand Down
14 changes: 5 additions & 9 deletions packages/core/src/tracing/spans/captureSpan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function applyCommonSpanAttributes(
scopeData: ScopeData,
): void {
const sdk = client.getSdkMetadata();
const { release, environment, sendDefaultPii } = client.getOptions();
const { release, environment } = client.getOptions();

// avoid overwriting any previously set attributes (from users or potentially our SDK instrumentation)
safeSetSpanJSONAttributes(spanJSON, {
Expand All @@ -135,14 +135,10 @@ function applyCommonSpanAttributes(
[SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID]: serializedSegmentSpan.span_id,
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME]: sdk?.sdk?.name,
[SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION]: sdk?.sdk?.version,
...(sendDefaultPii
? {
[SEMANTIC_ATTRIBUTE_USER_ID]: scopeData.user?.id,
[SEMANTIC_ATTRIBUTE_USER_EMAIL]: scopeData.user?.email,
[SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS]: scopeData.user?.ip_address,
[SEMANTIC_ATTRIBUTE_USER_USERNAME]: scopeData.user?.username,
}
: {}),
[SEMANTIC_ATTRIBUTE_USER_ID]: scopeData.user?.id,
[SEMANTIC_ATTRIBUTE_USER_EMAIL]: scopeData.user?.email,
[SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS]: scopeData.user?.ip_address,
[SEMANTIC_ATTRIBUTE_USER_USERNAME]: scopeData.user?.username,
...scopeData.attributes,
});
}
Expand Down
273 changes: 106 additions & 167 deletions packages/core/test/lib/tracing/spans/captureSpan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,179 +25,102 @@ import { inferSpanDataFromOtelAttributes, safeSetSpanJSONAttributes } from '../.
import { getDefaultTestClientOptions, TestClient } from '../../../mocks/client';

describe('captureSpan', () => {
it('captures user attributes iff sendDefaultPii is true', () => {
const client = new TestClient(
getDefaultTestClientOptions({
dsn: 'https://dsn@ingest.f00.f00/1',
tracesSampleRate: 1,
release: '1.0.0',
environment: 'staging',
sendDefaultPii: true,
}),
);

const span = withScope(scope => {
scope.setClient(client);
scope.setUser({
id: '123',
email: 'user@example.com',
username: 'testuser',
ip_address: '127.0.0.1',
});

const span = startInactiveSpan({ name: 'my-span', attributes: { 'sentry.op': 'http.client' } });
span.end();

return span;
});

const serializedSpan = captureSpan(span, client);
it.each([true, false, undefined])(
'always applies scope user attributes to spans (sendDefaultPii: %s)',
sendDefaultPii => {
const client = new TestClient(
getDefaultTestClientOptions({
dsn: 'https://dsn@ingest.f00.f00/1',
tracesSampleRate: 1,
release: '1.0.0',
environment: 'staging',
sendDefaultPii,
}),
);

expect(serializedSpan).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_USER_ID]: {
value: '123',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_EMAIL]: {
value: 'user@example.com',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_USERNAME]: {
value: 'testuser',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS]: {
value: '127.0.0.1',
type: 'string',
},
},
_segmentSpan: span,
});
});
const span = withScope(scope => {
scope.setClient(client);
scope.setUser({
id: '123',
email: 'user@example.com',
username: 'testuser',
ip_address: '127.0.0.1',
});

it.each([false, undefined])("doesn't capture user attributes if sendDefaultPii is %s", sendDefaultPii => {
const client = new TestClient(
getDefaultTestClientOptions({
dsn: 'https://dsn@ingest.f00.f00/1',
tracesSampleRate: 1,
release: '1.0.0',
environment: 'staging',
sendDefaultPii,
}),
);
const span = startInactiveSpan({ name: 'my-span', attributes: { 'sentry.op': 'http.client' } });
span.end();

const span = withScope(scope => {
scope.setClient(client);
scope.setUser({
id: '123',
email: 'user@example.com',
username: 'testuser',
ip_address: '127.0.0.1',
return span;
});

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',
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_USER_ID]: {
value: '123',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_EMAIL]: {
value: 'user@example.com',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_USERNAME]: {
value: 'testuser',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS]: {
value: '127.0.0.1',
type: 'string',
},
},
},
_segmentSpan: span,
});
});
_segmentSpan: span,
});
},
);

it('captures sdk name and version if available', () => {
const client = new TestClient(
Expand Down Expand Up @@ -286,6 +209,22 @@ describe('captureSpan', () => {
value: '1.0.0',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_ID]: {
value: '123',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_EMAIL]: {
value: 'user@example.com',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_USERNAME]: {
value: 'testuser',
type: 'string',
},
[SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS]: {
value: '127.0.0.1',
type: 'string',
},
},
_segmentSpan: span,
});
Expand Down
Loading