8000 feat(web-vitals): Capture extra information from LCP and CLS web vitals. · szechyjs/sentry-javascript@f091d76 · GitHub
[go: up one dir, main page]

Skip to content

Commit f091d76

Browse files
committed
feat(web-vitals): Capture extra information from LCP and CLS web vitals.
1 parent c88499f commit f091d76

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

packages/tracing/src/browser/metrics.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/* eslint-disable max-lines */
22
/* eslint-disable @typescript-eslint/no-explicit-any */
33
import { Measurements, SpanContext } from '@sentry/types';
4-
import { browserPerformanceTimeOrigin, getGlobalObject, logger } from '@sentry/utils';
4+
import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, logger } from '@sentry/utils';
55

66
import { Span } from '../span';
77
import { Transaction } from '../transaction';
88
import { msToSec } from '../utils';
9-
import { getCLS } from './web-vitals/getCLS';
9+
import { getCLS, LayoutShift } from './web-vitals/getCLS';
1010
import { getFID } from './web-vitals/getFID';
11-
import { getLCP } from './web-vitals/getLCP';
11+
import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP';
1212
import { getTTFB } from './web-vitals/getTTFB';
1313
import { getFirstHidden } from './web-vitals/lib/getFirstHidden';
1414
import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types';
@@ -20,6 +20,8 @@ export class MetricsInstrumentation {
2020
private _measurements: Measurements = {};
2121

2222
private _performanceCursor: number = 0;
23+
private _lcpEntry: LargestContentfulPaint | undefined;
24+
private _clsEntry: LayoutShift | undefined;
2325

2426
public constructor() {
2527
if (global && global.performance) {
@@ -135,6 +137,24 @@ export class MetricsInstrumentation {
135137
// Measurements are only available for pageload transactions
136138
if (transaction.op === 'pageload') {
137139
transaction.setMeasurements(this._measurements);
140+
141+
if (this._lcpEntry) {
142+
logger.log('[Measurements] Adding LCP Data');
143+
transaction.setTag('measurements.lcp.url', this._lcpEntry.url);
144+
transaction.setData('measurements.lcp', {
145+
size: this._lcpEntry.size,
146+
id: this._lcpEntry.id,
147+
url: this._lcpEntry.url,
148+
element: this._lcpEntry.element ? htmlTreeAsString(this._lcpEntry.element) : undefined,
149+
});
150+
}
151+
152+
if (this._clsEntry) {
153+
logger.log('[Measurements] Adding CLS Data');
154+
transaction.setData('measurements.cls', {
155+
sources: this._clsEntry.sources.map(source => htmlTreeAsString(source.node)),
156+
});
157+
}
138158
}
139159
}
140160

@@ -149,6 +169,7 @@ export class MetricsInstrumentation {
149169

150170
logger.log('[Measurements] Adding CLS');
151171
this._measurements['cls'] = { value: metric.value };
172+
this._clsEntry = entry as LayoutShift;
152173
});
153174
}
154175

@@ -206,6 +227,7 @@ export class MetricsInstrumentation {
206227
logger.log('[Measurements] Adding LCP');
207228
this._measurements['lcp'] = { value: metric.value };
208229
this._measurements['mark.lcp'] = { value: timeOrigin + startTime };
230+
this._lcpEntry = entry as LargestContentfulPaint;
209231
});
210232
}
211233

packages/tracing/src/browser/web-vitals/getCLS.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ import { onHidden } from './lib/onHidden';
2121
import { ReportHandler } from './types';
2222

2323
// https://wicg.github.io/layout-instability/#sec-layout-shift
24-
interface LayoutShift extends PerformanceEntry {
24+
export interface LayoutShift extends PerformanceEntry {
2525
value: number;
2626
hadRecentInput: boolean;
27+
lastInputTime: DOMHighResTimeStamp;
28+
sources: Array<LayoutShiftAttribution>;
29+
toJSON(): Record<string, unknown>;
30+
}
31+
32+
export interface LayoutShiftAttribution {
33+
node?: Node;
34+
previousRect: DOMRectReadOnly;
35+
currentRect: DOMRectReadOnly;
2736
}
2837

2938
export const getCLS = (onReport: ReportHandler, reportAllChanges = false): void => {

packages/tracing/src/browser/web-vitals/getLCP.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,24 @@ import { onHidden } from './lib/onHidden';
2222
import { whenInput } from './lib/whenInput';
2323
import { ReportHandler } from './types';
2424

25+
// https://wicg.github.io/largest-contentful-paint/#sec-largest-contentful-paint-interface
26+
export interface LargestContentfulPaint extends PerformanceEntry {
27+
renderTime: DOMHighResTimeStamp;
28+
loadTime: DOMHighResTimeStamp;
29+
size: number;
30+
id: string;
31+
url: string;
32+
element?: Element;
33+
toJSON(): Record<string, unknown>;
34+
}
35+
2536
export const getLCP = (onReport: ReportHandler, reportAllChanges = false): void => {
2637
const metric = initMetric('LCP');
2738
const firstHidden = getFirstHidden();
2839

2940
let report: ReturnType<typeof bindReporter>;
3041

31-
const entryHandler = (entry: PerformanceEntry): void => {
42+
const entryHandler = (entry: LargestContentfulPaint): void => {
3243
// The startTime attribute returns the value of the renderTime if it is not 0,
3344
// and the value of the loadTime otherwise.
3445
const value = entry.startTime;
@@ -45,7 +56,7 @@ export const getLCP = (onReport: ReportHandler, reportAllChanges = false): void
4556
report();
4657
};
4758

48-
const po = observe('largest-contentful-paint', entryHandler);
59+
const po = observe('largest-contentful-paint', entryHandler as PerformanceEntryHandler);
4960

5061
if (po) {
5162
report = bindReporter(onReport, metric, po, reportAllChanges);

0 commit comments

Comments
 (0)
0