Skip to content

Commit b707cfc

Browse files
committed
refactor toTraceparent into extractTraceparentData
1 parent 9824fd2 commit b707cfc

File tree

10 files changed

+126
-99
lines changed

10 files changed

+126
-99
lines changed

packages/node/src/handlers.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-lines */
22
/* eslint-disable @typescript-eslint/no-explicit-any */
33
import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core';
4-
import { Span } from '@sentry/tracing';
4+
import { extractTraceparentData, Span } from '@sentry/tracing';
55
import { Event } from '@sentry/types';
66
import {
77
extractNodeRequestData,
@@ -40,26 +40,16 @@ export function tracingHandler(): (
4040
const reqMethod = (req.method || '').toUpperCase();
4141
const reqUrl = req.url && stripUrlQueryAndFragment(req.url);
4242

43-
let traceId;
44-
let parentSpanId;
45-
let sampled;
46-
4743
// If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision)
44+
let traceparentData;
4845
if (req.headers && isString(req.headers['sentry-trace'])) {
49-
const span = Span.fromTraceparent(req.headers['sentry-trace'] as string);
50-
if (span) {
51-
traceId = span.traceId;
52-
parentSpanId = span.parentSpanId;
53-
sampled = span.sampled;
54-
}
46+
traceparentData = extractTraceparentData(req.headers['sentry-trace'] as string);
5547
}
5648

5749
const transaction = startTransaction({
5850
name: `${reqMethod} ${reqUrl}`,
5951
op: 'http.server',
60-
parentSpanId,
61-
sampled,
62-
traceId,
52+
...traceparentData,
6353
});
6454

6555
// We put the transaction on the scope so users can attach children to it

packages/tracing/src/browser/browsertracing.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { logger } from '@sentry/utils';
44

55
import { startIdleTransaction } from '../hubextensions';
66
import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../idletransaction';
7-
import { Span } from '../span';
87
import { SpanStatus } from '../spanstatus';
8+
import { extractTraceparentData } from '../utils';
99
import { registerBackgroundTabDetection } from './backgroundtab';
1010
import { MetricsInstrumentation } from './metrics';
1111
import {
@@ -213,21 +213,16 @@ export class BrowserTracing implements Integration {
213213

214214
/**
215215
* Gets transaction context from a sentry-trace meta.
216+
*
217+
* @returns Transaction context data from the header or undefined if there's no header or the header is malformed
216218
*/
217-
function getHeaderContext(): Partial<TransactionContext> {
219+
function getHeaderContext(): Partial<TransactionContext> | undefined {
218220
const header = getMetaContent('sentry-trace');
219221
if (header) {
220-
const span = Span.fromTraceparent(header);
221-
if (span) {
222-
return {
223-
parentSpanId: span.parentSpanId,
224-
sampled: span.sampled,
225-
traceId: span.traceId,
226-
};
227-
}
222+
return extractTraceparentData(header);
228223
}
229224

230-
return {};
225+
return undefined;
231226
}
232227

233228
/** Returns the value of a meta tag */

packages/tracing/src/index.bundle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import { getGlobalObject } from '@sentry/utils';
5555
import { BrowserTracing } from './browser';
5656
import { addExtensionMethods } from './hubextensions';
5757

58-
export { Span, TRACEPARENT_REGEXP } from './span';
58+
export { Span } from './span';
5959

6060
let windowIntegrations = {};
6161

packages/tracing/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as ApmIntegrations from './integrations';
55
const Integrations = { ...ApmIntegrations, BrowserTracing };
66

77
export { Integrations };
8-
export { Span, TRACEPARENT_REGEXP } from './span';
8+
export { Span } from './span';
99
export { Transaction } from './transaction';
1010

1111
export { SpanStatus } from './spanstatus';

packages/tracing/src/span.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,6 @@ import { dropUndefinedKeys, timestampWithMs, uuid4 } from '@sentry/utils';
44

55
import { SpanStatus } from './spanstatus';
66

7-
export const TRACEPARENT_REGEXP = new RegExp(
8-
'^[ \\t]*' + // whitespace
9-
'([0-9a-f]{32})?' + // trace_id
10-
'-?([0-9a-f]{16})?' + // span_id
11-
'-?([01])?' + // sampled
12-
'[ \\t]*$', // whitespace
13-
);
14-
157
/**
168
* Keeps track of finished spans for a given transaction
179
* @internal
@@ -153,32 +145,6 @@ export class Span implements SpanInterface, SpanContext {
153145
}
154146
}
155147

156-
/**
157-
* Continues a trace from a string (usually the header).
158-
* @param traceparent Traceparent string
159-
*/
160-
public static fromTraceparent(
161-
traceparent: string,
162-
spanContext?: Pick<SpanContext, Exclude<keyof SpanContext, 'spanId' | 'sampled' | 'traceId' | 'parentSpanId'>>,
163-
): Span | undefined {
164-
const matches = traceparent.match(TRACEPARENT_REGEXP);
165-
if (matches) {
166-
let sampled: boolean | undefined;
167-
if (matches[3] === '1') {
168-
sampled = true;
169-
} else if (matches[3] === '0') {
170-
sampled = false;
171-
}
172-
return new Span({
173-
...spanContext,
174-
parentSpanId: matches[2],
175-
sampled,
176-
traceId: matches[1],
177-
});
178-
}
179-
return undefined;
180-
}
181-
182148
/**
183149
* @inheritDoc
184150
* @deprecated

packages/tracing/src/utils.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { Options } from '@sentry/types';
1+
import { Options, TraceparentData } from '@sentry/types';
2+
3+
export const TRACEPARENT_REGEXP = new RegExp(
4+
'^[ \\t]*' + // whitespace
5+
'([0-9a-f]{32})?' + // trace_id
6+
'-?([0-9a-f]{16})?' + // span_id
7+
'-?([01])?' + // sampled
8+
'[ \\t]*$', // whitespace
9+
);
210

311
/**
412
* Determines if tracing is currently enabled.
@@ -8,3 +16,28 @@ import { Options } from '@sentry/types';
816
export function hasTracingEnabled(options: Options): boolean {
917
return 'tracesSampleRate' in options || 'tracesSampler' in options;
1018
}
19+
20+
/**
21+
* Extract transaction context data from a `sentry-trace` header.
22+
*
23+
* @param traceparent Traceparent string
24+
*
25+
* @returns Object containing data from the header, or undefined if traceparent string is malformed
26+
*/
27+
export function extractTraceparentData(traceparent: string): TraceparentData | undefined {
28+
const matches = traceparent.match(TRACEPARENT_REGEXP);
29+
if (matches) {
30+
let parentSampled: boolean | undefined;
31+
if (matches[3] === '1') {
32+
parentSampled = true;
33+
} else if (matches[3] === '0') {
34+
parentSampled = false;
35+
}
36+
return {
37+
traceId: matches[1],
38+
parentSampled,
39+
parentSpanId: matches[2],
40+
};
41+
}
42+
return undefined;
43+
}

packages/tracing/test/span.test.ts

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -100,42 +100,6 @@ describe('Span', () => {
100100
});
101101
});
102102

103-
describe('fromTraceparent', () => {
104-
test('no sample', () => {
105-
const from = Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb') as any;
106-
107-
expect(from.parentSpanId).toEqual('bbbbbbbbbbbbbbbb');
108-
expect(from.traceId).toEqual('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
109-
expect(from.spanId).not.toEqual('bbbbbbbbbbbbbbbb');
110-
expect(from.sampled).toBeUndefined();
111-
});
112-
test('sample true', () => {
113-
const from = Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-1') as any;
114-
expect(from.sampled).toBeTruthy();
115-
});
116-
117-
test('sample false', () => {
118-
const from = Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-0') as any;
119-
expect(from.sampled).toBeFalsy();
120-
});
121-
122-
test('just sample rate', () => {
123-
const from = Span.fromTraceparent('0') as any;
124-
expect(from.traceId).toHaveLength(32);
125-
expect(from.spanId).toHaveLength(16);
126-
expect(from.sampled).toBeFalsy();
127-
128-
const from2 = Span.fromTraceparent('1') as any;
129-
expect(from2.traceId).toHaveLength(32);
130-
expect(from2.spanId).toHaveLength(16);
131-
expect(from2.sampled).toBeTruthy();
132-
});
133-
134-
test('invalid', () => {
135-
expect(Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-x')).toBeUndefined();
136-
});
137-
});
138-
139103
describe('toJSON', () => {
140104
test('simple', () => {
141105
const span = JSON.parse(

packages/tracing/test/utils.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { extractTraceparentData } from '../src/utils';
2+
3+
describe('extractTraceparentData', () => {
4+
test('no sample', () => {
5+
const data = extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb') as any;
6+
7+
expect(data).toBeDefined();
8+
expect(data.parentSpanId).toEqual('bbbbbbbbbbbbbbbb');
9+
expect(data.traceId).toEqual('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
10+
expect(data?.parentSampled).toBeUndefined();
11+
});
12+
13+
test('sample true', () => {
14+
const data = extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-1') as any;
15+
16+
expect(data).toBeDefined();
17+
expect(data.parentSampled).toBeTruthy();
18+
});
19+
20+
test('sample false', () => {
21+
const data = extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-0') as any;
22+
23+
expect(data).toBeDefined();
24+
expect(data.parentSampled).toBeFalsy();
25+
});
26+
27+
test('just sample decision - false', () => {
28+
const data = extractTraceparentData('0') as any;
29+
30+
expect(data).toBeDefined();
31+
expect(data.traceId).toBeUndefined();
32+
expect(data.spanId).toBeUndefined();
33+
expect(data.parentSampled).toBeFalsy();
34+
});
35+
36+
test('just sample decision - true', () => {
37+
const data = extractTraceparentData('1') as any;
38+
39+
expect(data).toBeDefined();
40+
expect(data.traceId).toBeUndefined();
41+
expect(data.spanId).toBeUndefined();
42+
expect(data.parentSampled).toBeTruthy();
43+
});
44+
45+
test('invalid', () => {
46+
// trace id wrong length
47+
expect(extractTraceparentData('a-bbbbbbbbbbbbbbbb-1')).toBeUndefined();
48+
49+
// parent span id wrong length
50+
expect(extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-b-1')).toBeUndefined();
51+
52+
// parent sampling decision wrong length
53+
expect(extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-11')).toBeUndefined();
54+
55+
// trace id invalid hex value
56+
expect(extractTraceparentData('someStuffHereWhichIsNotAtAllHexy-bbbbbbbbbbbbbbbb-1')).toBeUndefined();
57+
58+
// parent span id invalid hex value
59+
expect(extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-alsoNotSuperHexy-1')).toBeUndefined();
60+
61+
// bogus sampling decision
62+
expect(extractTraceparentData('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-x')).toBeUndefined();
63+
});
64+
});

packages/types/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ export { Span, SpanContext } from './span';
2323
export { StackFrame } from './stackframe';
2424
export { Stacktrace } from './stacktrace';
2525
export { Status } from './status';
26-
export { CustomSamplingContext, SamplingContext, Transaction, TransactionContext } from './transaction';
26+
export {
27+
CustomSamplingContext,
28+
SamplingContext,
29+
TraceparentData,
30+
Transaction,
31+
TransactionContext,
32+
} from './transaction';
2733
export { Thread } from './thread';
2834
export { Transport, TransportOptions, TransportClass } from './transport';
2935
export { User } from './user';

packages/types/src/transaction.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { Span, SpanContext } from './span';
55
* Interface holding Transaction-specific properties
66
*/
77
export interface TransactionContext extends SpanContext {
8+
/**
9+
* Human-readable identifier for the transaction
10+
*/
811
name: string;
12+
913
/**
1014
* If true, sets the end timestamp of the transaction to the highest timestamp of child spans, trimming
1115
* the duration of the transaction. This is useful to discard extra time in the transaction that is not
@@ -20,6 +24,11 @@ export interface TransactionContext extends SpanContext {
2024
parentSampled?: boolean;
2125
}
2226

27+
/**
28+
* Data pulled from a `sentry-trace` header
29+
*/
30+
export type TraceparentData = Pick<TransactionContext, 'traceId' | 'parentSpanId' | 'parentSampled'>;
31+
2332
/**
2433
* Transaction "Class", inherits Span only has `setName`
2534
*/

0 commit comments

Comments
 (0)