Skip to content

Commit 0ee0799

Browse files
authored
ref(tracing): Clean up various things before fixing sampling (#2944)
1 parent a575c83 commit 0ee0799

File tree

9 files changed

+348
-228
lines changed

9 files changed

+348
-228
lines changed

packages/tracing/src/browser/browsertracing.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
registerRequestInstrumentation,
1414
RequestInstrumentationOptions,
1515
} from './request';
16-
import { defaultBeforeNavigate, defaultRoutingInstrumentation } from './router';
16+
import { defaultRoutingInstrumentation } from './router';
1717

1818
export const DEFAULT_MAX_TRANSACTION_DURATION_SECONDS = 600;
1919

@@ -66,7 +66,7 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
6666
*
6767
* If undefined is returned, a pageload/navigation transaction will not be created.
6868
*/
69-
beforeNavigate(context: TransactionContext): TransactionContext | undefined;
69+
beforeNavigate?(context: TransactionContext): TransactionContext | undefined;
7070

7171
/**
7272
* Instrumentation that creates routing change transactions. By default creates
@@ -80,7 +80,6 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
8080
}
8181

8282
const DEFAULT_BROWSER_TRACING_OPTIONS = {
83-
beforeNavigate: defaultBeforeNavigate,
8483
idleTimeout: DEFAULT_IDLE_TIMEOUT,
8584
markBackgroundTransactions: true,
8685
maxTransactionDuration: DEFAULT_MAX_TRANSACTION_DURATION_SECONDS,
@@ -189,20 +188,21 @@ export class BrowserTracing implements Integration {
189188
const { beforeNavigate, idleTimeout, maxTransactionDuration } = this.options;
190189

191190
// if beforeNavigate returns undefined, we should not start a transaction.
192-
const ctx = beforeNavigate({
191+
const expandedContext = {
193192
...context,
194193
...getHeaderContext(),
195194
trimEnd: true,
196-
});
195+
};
196+
const modifiedContext = typeof beforeNavigate === 'function' ? beforeNavigate(expandedContext) : expandedContext;
197197

198-
if (ctx === undefined) {
198+
if (modifiedContext === undefined) {
199199
logger.log(`[Tracing] Did not create ${context.op} idleTransaction due to beforeNavigate`);
200200
return undefined;
201201
}
202202

203203
const hub = this._getCurrentHub();
204-
logger.log(`[Tracing] starting ${ctx.op} idleTransaction on scope`);
205-
const idleTransaction = startIdleTransaction(hub, ctx, idleTimeout, true);
204+
const idleTransaction = startIdleTransaction(hub, modifiedContext, idleTimeout, true);
205+
logger.log(`[Tracing] Starting ${modifiedContext.op} transaction on scope`);
206206
idleTransaction.registerBeforeFinishCallback((transaction, endTimestamp) => {
207207
this._metrics.addPerformanceEntries(transaction);
208208
adjustTransactionDuration(secToMs(maxTransactionDuration), transaction, endTimestamp);
@@ -217,7 +217,7 @@ export class BrowserTracing implements Integration {
217217
*
218218
* @returns Transaction context data from the header or undefined if there's no header or the header is malformed
219219
*/
220-
function getHeaderContext(): Partial<TransactionContext> | undefined {
220+
export function getHeaderContext(): Partial<TransactionContext> | undefined {
221221
const header = getMetaContent('sentry-trace');
222222
if (header) {
223223
return extractTraceparentData(header);

packages/tracing/src/browser/request.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,24 @@ export interface RequestInstrumentationOptions {
4141
/** Data returned from fetch callback */
4242
export interface FetchData {
4343
// eslint-disable-next-line @typescript-eslint/no-explicit-any
44-
args: any[];
44+
args: any[]; // the arguments passed to the fetch call itself
4545
fetchData?: {
4646
method: string;
4747
url: string;
4848
// span_id
4949
__span?: string;
5050
};
51-
response?: Response;
51+
52+
// TODO Should this be unknown instead? If we vendor types, make it a Response
53+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
54+
response?: any;
55+
5256
startTimestamp: number;
5357
endTimestamp?: number;
5458
}
5559

5660
/** Data returned from XHR request */
57-
interface XHRData {
61+
export interface XHRData {
5862
xhr?: {
5963
__sentry_xhr__?: {
6064
method: string;
@@ -64,8 +68,8 @@ interface XHRData {
6468
data: Record<string, any>;
6569
};
6670
__sentry_xhr_span_id__?: string;
67-
__sentry_own_request__: boolean;
6871
setRequestHeader?: (key: string, val: string) => void;
72+
__sentry_own_request__?: boolean;
6973
};
7074
startTimestamp: number;
7175
endTimestamp?: number;
@@ -114,7 +118,7 @@ export function registerRequestInstrumentation(_options?: Partial<RequestInstrum
114118
if (traceFetch) {
115119
addInstrumentationHandler({
116120
callback: (handlerData: FetchData) => {
117-
_fetchCallback(handlerData, shouldCreateSpan, spans);
121+
fetchCallback(handlerData, shouldCreateSpan, spans);
118122
},
119123
type: 'fetch',
120124
});
@@ -133,7 +137,7 @@ export function registerRequestInstrumentation(_options?: Partial<RequestInstrum
133137
/**
134138
* Create and track fetch request spans
135139
*/
136-
export function _fetchCallback(
140+
export function fetchCallback(
137141
handlerData: FetchData,
138142
shouldCreateSpan: (url: string) => boolean,
139143
spans: Record<string, Span>,
@@ -147,6 +151,8 @@ export function _fetchCallback(
147151
if (span) {
148152
const response = handlerData.response;
149153
if (response) {
154+
// TODO (kmclb) remove this once types PR goes through
155+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
150156
span.setHttpStatus(response.status);
151157
}
152158
span.finish();
@@ -198,7 +204,7 @@ export function _fetchCallback(
198204
/**
199205
* Create and track xhr request spans
200206
*/
201-
function xhrCallback(
207+
export function xhrCallback(
202208
handlerData: XHRData,
203209
shouldCreateSpan: (url: string) => boolean,
204210
spans: Record<string, Span>,
@@ -217,11 +223,10 @@ function xhrCallback(
217223
return;
218224
}
219225

226+
// check first if the request has finished and is tracked by an existing span which should now end
220227
if (handlerData.endTimestamp && handlerData.xhr.__sentry_xhr_span_id__) {
221228
const span = spans[handlerData.xhr.__sentry_xhr_span_id__];
222229
if (span) {
223-
span.setData('url', xhr.url);
224-
span.setData('method', xhr.method);
225230
span.setHttpStatus(xhr.status_code);
226231
span.finish();
227232

@@ -231,12 +236,15 @@ function xhrCallback(
231236
return;
232237
}
233238

239+
// if not, create a new span to track it
234240
const activeTransaction = getActiveTransaction();
235241
if (activeTransaction) {
236242
const span = activeTransaction.startChild({
237243
data: {
238244
...xhr.data,
239245
type: 'xhr',
246+
method: xhr.method,
247+
url: xhr.url,
240248
},
241249
description: `${xhr.method} ${xhr.url}`,
242250
op: 'http',

packages/tracing/src/browser/router.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,3 @@ export function defaultRoutingInstrumentation<T extends TransactionType>(
5454
});
5555
}
5656
}
57-
58-
/** default implementation of Browser Tracing before navigate */
59-
export function defaultBeforeNavigate(context: TransactionContext): TransactionContext | undefined {
60-
return context;
61-
}

packages/tracing/src/hubextensions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ function isValidSampleRate(rate: unknown): boolean {
184184
/**
185185
* Creates a new transaction and adds a sampling decision if it doesn't yet have one.
186186
*
187-
* The Hub.startTransaction method delegates to this method to do its work, passing the Hub instance in as `this`.
188-
* Exists as a separate function so that it can be injected into the class as an "extension method."
187+
* The Hub.startTransaction method delegates to this method to do its work, passing the Hub instance in as `this`, as if
188+
* it had been called on the hub directly. Exists as a separate function so that it can be injected into the class as an
189+
* "extension method."
189190
*
190191
* @param this: The Hub starting the transaction
191192
* @param transactionContext: Data used to configure the transaction

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

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from '../../src/browser/browsertracing';
1212
import { defaultRequestInstrumentionOptions } from '../../src/browser/request';
1313
import { defaultRoutingInstrumentation } from '../../src/browser/router';
14+
import * as hubExtensions from '../../src/hubextensions';
1415
import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../../src/idletransaction';
1516
import { getActiveTransaction, secToMs } from '../../src/utils';
1617

@@ -76,7 +77,6 @@ describe('BrowserTracing', () => {
7677
const browserTracing = createBrowserTracing();
7778

7879
expect(browserTracing.options).toEqual({
79-
beforeNavigate: expect.any(Function),
8080
idleTimeout: DEFAULT_IDLE_TIMEOUT,
8181
markBackgroundTransactions: true,
8282
maxTransactionDuration: DEFAULT_MAX_TRANSACTION_DURATION_SECONDS,
@@ -210,12 +210,20 @@ describe('BrowserTracing', () => {
210210
const name = 'sentry-trace';
211211
const content = '126de09502ae4e0fb26c6967190756a4-b6e54397b12a2a0f-1';
212212
document.head.innerHTML = `<meta name="${name}" content="${content}">`;
213+
const startIdleTransaction = jest.spyOn(hubExtensions, 'startIdleTransaction');
214+
213215
createBrowserTracing(true, { routingInstrumentation: customRoutingInstrumentation });
214-
const transaction = getActiveTransaction(hub) as IdleTransaction;
215216

216-
expect(transaction.traceId).toBe('126de09502ae4e0fb26c6967190756a4');
217-
expect(transaction.parentSpanId).toBe('b6e54397b12a2a0f');
218-
expect(transaction.sampled).toBe(true);
217+
expect(startIdleTransaction).toHaveBeenCalledWith(
218+
expect.any(Object),
219+
expect.objectContaining({
220+
traceId: '126de09502ae4e0fb26c6967190756a4',
221+
parentSpanId: 'b6e54397b12a2a0f',
222+
parentSampled: true,
223+
}),
224+
expect.any(Number),
225+
expect.any(Boolean),
226+
);
219227
});
220228

221229
describe('idleTimeout', () => {
@@ -341,22 +349,35 @@ describe('BrowserTracing', () => {
341349
});
342350
});
343351
});
344-
});
345352

346-
describe('getMeta', () => {
347-
it('returns a found meta tag contents', () => {
348-
const name = 'sentry-trace';
349-
const content = '126de09502ae4e0fb26c6967190756a4-b6e54397b12a2a0f-1';
350-
document.head.innerHTML = `<meta name="${name}" content="${content}">`;
353+
describe('sentry-trace <meta> element', () => {
354+
describe('getMetaContent', () => {
355+
it('finds the specified tag and extracts the value', () => {
356+
const name = 'sentry-trace';
357+
const content = '126de09502ae4e0fb26c6967190756a4-b6e54397b12a2a0f-1';
358+
document.head.innerHTML = `<meta name="${name}" content="${content}">`;
351359

352-
const meta = getMetaContent(name);
353-
expect(meta).toBe(content);
354-
});
360+
const metaTagValue = getMetaContent(name);
361+
expect(metaTagValue).toBe(content);
362+
});
355363

356-
it('only returns meta tags queried for', () => {
357-
document.head.innerHTML = `<meta name="not-test">`;
364+
it("doesn't return meta tags other than the one specified", () => {
365+
document.head.innerHTML = `<meta name="cat-cafe">`;
358366

359-
const meta = getMetaContent('test');
360-
expect(meta).toBe(null);
367+
const metaTagValue = getMetaContent('dogpark');
368+
expect(metaTagValue).toBe(null);
369+
});
370+
371+
it('can pick the correct tag out of multiple options', () => {
372+
const name = 'sentry-trace';
373+
const content = '126de09502ae4e0fb26c6967190756a4-b6e54397b12a2a0f-1';
374+
const sentryTraceMeta = `<meta name="${name}" content="${content}">`;
375+
const otherMeta = `<meta name="cat-cafe">`;
376+
document.head.innerHTML = `${sentryTraceMeta} ${otherMeta}`;
377+
378+
const metaTagValue = getMetaContent(name);
379+
expect(metaTagValue).toBe(content);
380+
});
381+
});
361382
});
362383
});

0 commit comments

Comments
 (0)