Skip to content

Commit a91fb10

Browse files
k-fishAbhiPrasad
andauthored
feat(perf): Add experimental option to improve LCP collection (#3879)
This will add an experimental option (options._metricOptions._reportAllChanges) that will set 'getLCP' to always report. Now that we are recording LCP element, it should be sufficient to collection the additional information and let the user pick the appropriate LCP element in the ui to improve accuracy dynamically instead. We'd like to test this out on Sentry first. Co-authored-by: Abhijeet Prasad <[email protected]>
1 parent 2afd60d commit a91fb10

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

packages/tracing/src/browser/browsertracing.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../idletransaction';
77
import { SpanStatus } from '../spanstatus';
88
import { extractTraceparentData, secToMs } from '../utils';
99
import { registerBackgroundTabDetection } from './backgroundtab';
10-
import { MetricsInstrumentation } from './metrics';
10+
import { DEFAULT_METRICS_INSTR_OPTIONS, MetricsInstrumentation, MetricsInstrumentationOptions } from './metrics';
1111
import {
1212
defaultRequestInstrumentationOptions,
1313
instrumentOutgoingRequests,
@@ -60,6 +60,15 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
6060
*/
6161
markBackgroundTransactions: boolean;
6262

63+
/**
64+
* _metricOptions allows the user to send options to change how metrics are collected.
65+
*
66+
* _metricOptions is currently experimental.
67+
*
68+
* Default: undefined
69+
*/
70+
_metricOptions?: Partial<MetricsInstrumentationOptions>;
71+
6372
/**
6473
* beforeNavigate is called before a pageload/navigation transaction is created and allows users to modify transaction
6574
* context data, or drop the transaction entirely (by setting `sampled = false` in the context).
@@ -116,7 +125,7 @@ export class BrowserTracing implements Integration {
116125

117126
private _getCurrentHub?: () => Hub;
118127

119-
private readonly _metrics: MetricsInstrumentation = new MetricsInstrumentation();
128+
private readonly _metrics: MetricsInstrumentation;
120129

121130
private readonly _emitOptionsWarning: boolean = false;
122131

@@ -139,6 +148,8 @@ export class BrowserTracing implements Integration {
139148
..._options,
140149
tracingOrigins,
141150
};
151+
152+
this._metrics = new MetricsInstrumentation({ ...DEFAULT_METRICS_INSTR_OPTIONS, ...this.options._metricOptions });
142153
}
143154

144155
/**

packages/tracing/src/browser/metrics.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals
1414

1515
const global = getGlobalObject<Window>();
1616

17+
/**
18+
* Exports a way to add options to our metric collection. Currently experimental.
19+
*/
20+
export interface MetricsInstrumentationOptions {
21+
_reportAllChanges: boolean;
22+
}
23+
24+
export const DEFAULT_METRICS_INSTR_OPTIONS: MetricsInstrumentationOptions = {
25+
_reportAllChanges: false,
26+
};
27+
1728
/** Class tracking metrics */
1829
export class MetricsInstrumentation {
1930
private _measurements: Measurements = {};
@@ -22,14 +33,14 @@ export class MetricsInstrumentation {
2233
private _lcpEntry: LargestContentfulPaint | undefined;
2334
private _clsEntry: LayoutShift | undefined;
2435

25-
public constructor() {
36+
public constructor(_options: MetricsInstrumentationOptions) {
2637
if (!isNodeEnv() && global?.performance && global?.document) {
2738
if (global.performance.mark) {
2839
global.performance.mark('sentry-tracing-init');
2940
}
3041

3142
this._trackCLS();
32-
this._trackLCP();
43+
this._trackLCP(_options._reportAllChanges);
3344
this._trackFID();
3445
}
3546
}
@@ -285,7 +296,7 @@ export class MetricsInstrumentation {
285296
}
286297

287298
/** Starts tracking the Largest Contentful Paint on the current page. */
288-
private _trackLCP(): void {
299+
private _trackLCP(reportAllChanges: boolean): void {
289300
getLCP(metric => {
290301
const entry = metric.entries.pop();
291302

@@ -299,7 +310,7 @@ export class MetricsInstrumentation {
299310
this._measurements['lcp'] = { value: metric.value };
300311
this._measurements['mark.lcp'] = { value: timeOrigin + startTime };
301312
this._lcpEntry = entry as LargestContentfulPaint;
302-
});
313+
}, reportAllChanges);
303314
}
304315

305316
/** Starts tracking the First Input Delay on the current page. */

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

+30
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
getHeaderContext,
1212
getMetaContent,
1313
} from '../../src/browser/browsertracing';
14+
import { DEFAULT_METRICS_INSTR_OPTIONS, MetricsInstrumentation } from '../../src/browser/metrics';
1415
import { defaultRequestInstrumentationOptions } from '../../src/browser/request';
1516
import { instrumentRoutingWithDefaults } from '../../src/browser/router';
1617
import * as hubExtensions from '../../src/hubextensions';
@@ -32,6 +33,8 @@ jest.mock('@sentry/utils', () => {
3233
};
3334
});
3435

36+
jest.mock('../../src/browser/metrics');
37+
3538
const { logger } = jest.requireActual('@sentry/utils');
3639
const warnSpy = jest.spyOn(logger, 'warn');
3740

@@ -493,4 +496,31 @@ describe('BrowserTracing', () => {
493496
);
494497
});
495498
});
499+
500+
describe('metrics', () => {
501+
beforeEach(() => {
502+
// @ts-ignore mock clear
503+
MetricsInstrumentation.mockClear();
504+
});
505+
506+
it('creates metrics instrumentation', () => {
507+
createBrowserTracing(true, {});
508+
509+
expect(MetricsInstrumentation).toHaveBeenCalledTimes(1);
510+
expect(MetricsInstrumentation).toHaveBeenLastCalledWith(DEFAULT_METRICS_INSTR_OPTIONS);
511+
});
512+
513+
it('creates metrics instrumentation with custom options', () => {
514+
createBrowserTracing(true, {
515+
_metricOptions: {
516+
_reportAllChanges: true,
517+
},
518+
});
519+
520+
expect(MetricsInstrumentation).toHaveBeenCalledTimes(1);
521+
expect(MetricsInstrumentation).toHaveBeenLastCalledWith({
522+
_reportAllChanges: true,
523+
});
524+
});
525+
});
496526
});

packages/tracing/test/browser/metrics.test.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { Span, Transaction } from '../../src';
2-
import { _startChild, addResourceSpans, MetricsInstrumentation, ResourceEntry } from '../../src/browser/metrics';
2+
import {
3+
_startChild,
4+
addResourceSpans,
5+
DEFAULT_METRICS_INSTR_OPTIONS,
6+
MetricsInstrumentation,
7+
ResourceEntry,
8+
} from '../../src/browser/metrics';
39
import { addDOMPropertiesToGlobal } from '../testutils';
410

511
// eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var
@@ -182,7 +188,7 @@ describe('MetricsInstrumentation', () => {
182188
jest.spyOn(MetricsInstrumentation.prototype as any, tracker),
183189
);
184190

185-
new MetricsInstrumentation();
191+
new MetricsInstrumentation(DEFAULT_METRICS_INSTR_OPTIONS);
186192

187193
trackers.forEach(tracker => expect(tracker).not.toBeCalled());
188194
});
@@ -196,8 +202,7 @@ describe('MetricsInstrumentation', () => {
196202
const trackers = ['_trackCLS', '_trackLCP', '_trackFID'].map(tracker =>
197203
jest.spyOn(MetricsInstrumentation.prototype as any, tracker),
198204
);
199-
200-
new MetricsInstrumentation();
205+
new MetricsInstrumentation(DEFAULT_METRICS_INSTR_OPTIONS);
201206
global.process = backup;
202207

203208
trackers.forEach(tracker => expect(tracker).toBeCalled());

0 commit comments

Comments
 (0)