Skip to content

Commit e97a639

Browse files
authored
ref(tracing): Include transaction in DSC if transaction source is not an unparameterized URL (#5392)
This patch re-introduces the `transaction` field in the Dynamic Sampling Context (DSC). However, its presence is now determined by the [transaction source](https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations) which was introduced in #5367. As of this we we add the `transaction` field back, if the source indicates that the transaction name is not an unparameterized URL (meaning, the source is set and it is not `url`). Additionally, the PR (once again) adjusts our unit and integration tests to reflect this change. Repurposed some DSC<=>`sendDefaultPii` tests that we previously skipped to now cover the transaction<=>transaction source dependence. Did some cleanup of commented out old code and explanations that no longer apply. Remove he `'unknown'` field from the `TransactionSource` type because it is only used by Relay and SDKs shouldn't set it.
1 parent 439b9d3 commit e97a639

File tree

16 files changed

+71
-51
lines changed

16 files changed

+71
-51
lines changed

packages/core/test/lib/envelope.test.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ describe('createEventEnvelope', () => {
4444
{
4545
environment: 'prod',
4646
release: '1.0.0',
47-
// transaction: 'TX',
48-
// user_id: 'bob',
47+
transaction: 'TX',
4948
user_segment: 'segmentA',
5049
sample_rate: '0.95',
5150
public_key: 'pubKey123',
@@ -59,8 +58,7 @@ describe('createEventEnvelope', () => {
5958
{
6059
environment: 'prod',
6160
release: '1.0.0',
62-
// transaction: 'TX',
63-
// user_id: 'bob',
61+
transaction: 'TX',
6462
user_segment: 'segmentA',
6563
sample_rate: '0.95',
6664
public_key: 'pubKey123',

packages/integration-tests/suites/tracing/envelope-header-no-pii/init.js renamed to packages/integration-tests/suites/tracing/envelope-header-transaction-name/init.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ Sentry.init({
1414
Sentry.configureScope(scope => {
1515
scope.setUser({ id: 'user123', segment: 'segmentB' });
1616
scope.setTransactionName('testTransactionDSC');
17+
scope.getTransaction().setMetadata({ source: 'custom' });
1718
});

packages/integration-tests/suites/tracing/envelope-header-no-pii/test.ts renamed to packages/integration-tests/suites/tracing/envelope-header-transaction-name/test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { sentryTest } from '../../../utils/fixtures';
55
import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
66

77
sentryTest(
8-
'should not send user_id and transaction in DSC data in trace envelope header (for now)',
8+
'should only include transaction name if source is better than an unparameterized URL',
99
async ({ getLocalTestPath, page }) => {
1010
const url = await getLocalTestPath({ testDir: __dirname });
1111

@@ -16,6 +16,7 @@ sentryTest(
1616
environment: 'production',
1717
user_segment: 'segmentB',
1818
sample_rate: '1',
19+
transaction: expect.stringContaining('/index.html'),
1920
trace_id: expect.any(String),
2021
public_key: 'public',
2122
});

packages/integration-tests/suites/tracing/envelope-header/init.js

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ Sentry.init({
88
integrations: [new Integrations.BrowserTracing({ tracingOrigins: [/.*/] })],
99
environment: 'production',
1010
tracesSampleRate: 1,
11-
// TODO: We're rethinking the mechanism for including Pii data in DSC, hence commenting out sendDefaultPii for now
12-
// sendDefaultPii: true,
1311
debug: true,
1412
});
1513

packages/integration-tests/suites/tracing/envelope-header/test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ sentryTest(
1111

1212
const envHeader = await getFirstSentryEnvelopeRequest<EventEnvelopeHeaders>(page, url, envelopeHeaderRequestParser);
1313

14+
// In this test, we don't expect trace.transaction to be present because without a custom routing instrumentation
15+
// we for now don't have parameterization. This might change in the future but for now the only way of having
16+
// transaction in DSC with the default BrowserTracing integration is when the transaction name is set manually.
17+
// This scenario is covered in another integration test (envelope-header-transaction-name).
1418
expect(envHeader.trace).toBeDefined();
1519
expect(envHeader.trace).toEqual({
1620
environment: 'production',
17-
// transaction: expect.stringContaining('index.html'),
18-
// user_id: 'user123',
1921
user_segment: 'segmentB',
2022
sample_rate: '1',
2123
trace_id: expect.any(String),

packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,8 @@ test('Should populate and propagate sentry baggage if sentry-trace header does n
7878
expect(response).toMatchObject({
7979
test_data: {
8080
host: 'somewhere.not.sentry',
81+
// TraceId changes, hence we only expect that the string contains the traceid key
8182
baggage: expect.stringContaining(
82-
// Commented out as long as transaction and user_id are not part of DSC
83-
// 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,' +
84-
// 'sentry-public_key=public,sentry-trace_id=',
8583
'sentry-environment=prod,sentry-release=1.0,sentry-public_key=public,sentry-trace_id=',
8684
),
8785
},
@@ -101,9 +99,6 @@ test('Should populate Sentry and ignore 3rd party content if sentry-trace header
10199
host: 'somewhere.not.sentry',
102100
// TraceId changes, hence we only expect that the string contains the traceid key
103101
baggage: expect.stringContaining(
104-
// Commented out as long as transaction and user_id are not part of DSC
105-
// 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,' +
106-
// 'sentry-public_key=public,sentry-trace_id=',
107102
'sentry-environment=prod,sentry-release=1.0,sentry-public_key=public,sentry-trace_id=',
108103
),
109104
},

packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ test('should attach a `baggage` header to an outgoing request.', async () => {
1313
test_data: {
1414
host: 'somewhere.not.sentry',
1515
baggage:
16-
// Commented out as long as transaction and user_id are not part of DSC
17-
// 'sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-user_segment=SegmentA' +
1816
'sentry-environment=prod,sentry-release=1.0,sentry-user_segment=SegmentA' +
1917
',sentry-public_key=public,sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-sample_rate=1',
2018
},
2119
});
2220
});
2321

24-
test('Does not include user_id and transaction name (for now)', async () => {
22+
test('Does not include transaction name if transaction source is not set', async () => {
2523
const url = await runServer(__dirname, `${path.resolve(__dirname, '.')}/server.ts`);
2624

2725
const response = (await getAPIResponse(new URL(`${url}/express`))) as TestAPIResponse;

packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts

-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ test('should ignore sentry-values in `baggage` header of a third party vendor an
3030
test_data: {
3131
host: 'somewhere.not.sentry',
3232
baggage: expect.stringContaining(
33-
// Commented out as long as transaction and user_id are not part of DSC
34-
// 'other=vendor,foo=bar,third=party,last=item,sentry-environment=prod,sentry-release=1.0,' +
35-
// 'sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public',
3633
'other=vendor,foo=bar,third=party,last=item,sentry-environment=prod,sentry-release=1.0,sentry-public_key=public',
3734
),
3835
},

packages/node-integration-tests/suites/express/sentry-trace/baggage-user-id/server.ts renamed to packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ app.get('/test/express', (_req, res) => {
2929
const transaction = Sentry.getCurrentHub().getScope()?.getTransaction();
3030
if (transaction) {
3131
transaction.traceId = '86f39e84263a4de99c326acab3bfe3bd';
32+
transaction.setMetadata({ source: 'route' });
3233
}
3334
const headers = http.get('http://somewhere.not.sentry/').getHeaders();
3435

packages/node-integration-tests/suites/express/sentry-trace/baggage-user-id/test.ts renamed to packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/test.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import * as path from 'path';
33
import { getAPIResponse, runServer } from '../../../../utils/index';
44
import { TestAPIResponse } from '../server';
55

6-
// TODO: Skipping this test because right now we're rethinking the mechanism for including such data
7-
test.skip('Includes user_id in baggage if <optionTBA> is set to true', async () => {
6+
test('Includes transaction in baggage if the transaction name is parameterized', async () => {
87
const url = await runServer(__dirname, `${path.resolve(__dirname, '.')}/server.ts`);
98

109
const response = (await getAPIResponse(new URL(`${url}/express`))) as TestAPIResponse;
@@ -13,7 +12,7 @@ test.skip('Includes user_id in baggage if <optionTBA> is set to true', async ()
1312
expect(response).toMatchObject({
1413
test_data: {
1514
host: 'somewhere.not.sentry',
16-
baggage: expect.stringContaining('sentry-user_id=user123'),
15+
baggage: expect.stringContaining('sentry-transaction=GET%20%2Ftest%2Fexpress'),
1716
},
1817
});
1918
});

packages/node/test/integrations/http.test.ts

+10-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as sentryCore from '@sentry/core';
22
import * as hubModule from '@sentry/hub';
33
import { Hub } from '@sentry/hub';
44
import { addExtensionMethods, Span, TRACEPARENT_REGEXP, Transaction } from '@sentry/tracing';
5+
import { TransactionContext } from '@sentry/types';
56
import { parseSemver } from '@sentry/utils';
67
import * as http from 'http';
78
import * as https from 'https';
@@ -17,7 +18,10 @@ import { getDefaultNodeClientOptions } from '../helper/node-client-options';
1718
const NODE_VERSION = parseSemver(process.versions.node);
1819

1920
describe('tracing', () => {
20-
function createTransactionOnScope(customOptions: Partial<NodeClientOptions> = {}) {
21+
function createTransactionOnScope(
22+
customOptions: Partial<NodeClientOptions> = {},
23+
customContext?: Partial<TransactionContext>,
24+
) {
2125
const options = getDefaultNodeClientOptions({
2226
dsn: 'https://[email protected]/12312012',
2327
tracesSampleRate: 1.0,
@@ -44,7 +48,9 @@ describe('tracing', () => {
4448
const transaction = hub.startTransaction({
4549
name: 'dogpark',
4650
traceId: '12312012123120121231201212312012',
51+
...customContext,
4752
});
53+
4854
hub.getScope()?.setSpan(transaction);
4955

5056
return transaction;
@@ -112,8 +118,6 @@ describe('tracing', () => {
112118
expect(baggageHeader).toBeDefined();
113119
expect(typeof baggageHeader).toEqual('string');
114120
expect(baggageHeader).toEqual(
115-
// Commented out as long as transaction and user_id are not part of DSC
116-
// 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,sentry-user_id=uid123,' +
117121
'sentry-environment=production,sentry-release=1.0.0,' +
118122
'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
119123
'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1',
@@ -131,31 +135,24 @@ describe('tracing', () => {
131135
expect(baggageHeader).toBeDefined();
132136
expect(typeof baggageHeader).toEqual('string');
133137
expect(baggageHeader).toEqual(
134-
// Commented out as long as transaction and user_id are not part of DSC
135-
// 'dog=great,sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
136-
// 'sentry-user_id=uid123,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
137-
// 'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1',
138138
'dog=great,sentry-environment=production,sentry-release=1.0.0,' +
139139
'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
140140
'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1',
141141
);
142142
});
143143

144-
// TODO: Skipping this test because right now we're rethinking the mechanism for including such data
145-
it.skip('does not add the user_id to the baggage header if <optionTBA> is set to false', async () => {
144+
it('adds the transaction name to the the baggage header if a valid transaction source is set', async () => {
146145
nock('http://dogs.are.great').get('/').reply(200);
147146

148-
createTransactionOnScope();
147+
createTransactionOnScope({}, { metadata: { source: 'custom' } });
149148

150149
const request = http.get({ host: 'http://dogs.are.great/', headers: { baggage: 'dog=great' } });
151150
const baggageHeader = request.getHeader('baggage') as string;
152151

153152
expect(baggageHeader).toBeDefined();
154153
expect(typeof baggageHeader).toEqual('string');
155154
expect(baggageHeader).toEqual(
156-
// Commented out as long as transaction and user_id are not part of DSC
157-
// 'dog=great,sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
158-
'dog=great,sentry-environment=production,sentry-release=1.0.0,' +
155+
'dog=great,sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
159156
'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
160157
'sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1',
161158
);

packages/tracing/src/transaction.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -235,19 +235,17 @@ export class Transaction extends SpanClass implements TransactionInterface {
235235
? rate.toLocaleString('fullwide', { useGrouping: false, maximumFractionDigits: 16 })
236236
: undefined;
237237

238-
// For now we're not sending the transaction name and user_id due to PII concerns
239-
// commenting it out for now because we'll probably need it in the future
240-
241238
const scope = hub.getScope();
242-
const { /* id: user_id, */ segment: user_segment } = (scope && scope.getUser()) || {};
239+
const { segment: user_segment } = (scope && scope.getUser()) || {};
240+
241+
const source = this.metadata.source;
242+
const transaction = source && source !== 'url' ? this.name : undefined;
243243

244244
return createBaggage(
245245
dropUndefinedKeys({
246246
environment,
247247
release,
248-
// transaction: this.name,
249-
// replace `someContidion` with whatever decision we come up with to guard PII in DSC
250-
// ...(someCondition && { user_id }),
248+
transaction,
251249
user_segment,
252250
public_key,
253251
trace_id: this.traceId,

packages/tracing/test/browser/browsertracing.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,6 @@ describe('BrowserTracing', () => {
509509
expect(baggage[0]).toEqual({
510510
release: '1.0.0',
511511
environment: 'production',
512-
// transaction: 'blank',
513512
public_key: 'pubKey',
514513
trace_id: expect.not.stringMatching('12312012123120121231201212312012'),
515514
});

packages/tracing/test/span.test.ts

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BrowserClient } from '@sentry/browser';
22
import { Hub, makeMain, Scope } from '@sentry/hub';
3-
import { BaseTransportOptions, ClientOptions } from '@sentry/types';
3+
import { BaseTransportOptions, ClientOptions, TransactionSource } from '@sentry/types';
44
import { createBaggage, getSentryBaggageItems, getThirdPartyBaggage, isSentryBaggageEmpty } from '@sentry/utils';
55

66
import { Span, Transaction } from '../src';
@@ -443,7 +443,6 @@ describe('Span', () => {
443443
expect(baggage && getSentryBaggageItems(baggage)).toStrictEqual({
444444
release: '1.0.1',
445445
environment: 'production',
446-
// transaction: 'tx',
447446
sample_rate: '0.56',
448447
trace_id: expect.any(String),
449448
});
@@ -473,6 +472,46 @@ describe('Span', () => {
473472
});
474473
expect(baggage && getThirdPartyBaggage(baggage)).toStrictEqual('');
475474
});
475+
476+
describe('Including transaction name in DSC', () => {
477+
test.each([
478+
['is not included if transaction source is not set', undefined],
479+
['is not included if transaction source is url', 'url'],
480+
])('%s', (_: string, source) => {
481+
const transaction = new Transaction(
482+
{
483+
name: 'tx',
484+
metadata: {
485+
...(source && { source: source as TransactionSource }),
486+
},
487+
},
488+
hub,
489+
);
490+
491+
const dsc = getSentryBaggageItems(transaction.getBaggage());
492+
493+
expect(dsc.transaction).toBeUndefined();
494+
});
495+
496+
test.each([
497+
['is included if transaction source is paremeterized route/url', 'route'],
498+
['is included if transaction source is a custom name', 'custom'],
499+
])('%s', (_: string, source) => {
500+
const transaction = new Transaction(
501+
{
502+
name: 'tx',
503+
metadata: {
504+
...(source && { source: source as TransactionSource }),
505+
},
506+
},
507+
hub,
508+
);
509+
510+
const dsc = getSentryBaggageItems(transaction.getBaggage());
511+
512+
expect(dsc.transaction).toEqual('tx');
513+
});
514+
});
476515
});
477516

478517
describe('Transaction source', () => {

packages/types/src/envelope.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ export type DynamicSamplingContext = {
1515
sample_rate?: string;
1616
release?: string;
1717
environment?: string;
18-
// transaction?: string; // omitted for now
19-
// user_id?: string; // omitted for now
18+
transaction?: string;
2019
user_segment?: string;
2120
};
2221

packages/types/src/transaction.ts

-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,6 @@ export type TransactionSource =
162162
| 'route'
163163
/** Name of the view handling the request */
164164
| 'view'
165-
/** This is the default value set by Relay for legacy SDKs. */
166-
| 'unknown'
167165
/** Named after a software component, such as a function or class name. */
168166
| 'component'
169167
/** Name of a background task (e.g. a Celery task) */

0 commit comments

Comments
 (0)