Skip to content

Commit cf786f1

Browse files
committed
ref: Add updated CLS
1 parent 323b343 commit cf786f1

File tree

5 files changed

+114
-32
lines changed

5 files changed

+114
-32
lines changed

packages/tracing/src/browser/metrics.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { msToSec } from '../utils';
99
import { getCLS, LayoutShift } from './web-vitals/getCLS';
1010
import { getFID } from './web-vitals/getFID';
1111
import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP';
12+
import { getUpdatedCLS } from './web-vitals/getUpdatedCLS';
1213
import { getVisibilityWatcher } from './web-vitals/lib/getVisibilityWatcher';
1314
import { NavigatorDeviceMemory, NavigatorNetworkInformation } from './web-vitals/types';
1415

@@ -21,6 +22,7 @@ export class MetricsInstrumentation {
2122
private _performanceCursor: number = 0;
2223
private _lcpEntry: LargestContentfulPaint | undefined;
2324
private _clsEntry: LayoutShift | undefined;
25+
private _updatedClsEntry: LayoutShift | undefined;
2426

2527
public constructor() {
2628
if (!isNodeEnv() && global?.performance) {
@@ -221,13 +223,19 @@ export class MetricsInstrumentation {
221223
transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)),
222224
);
223225
}
226+
227+
if (this._updatedClsEntry && this._updatedClsEntry.sources) {
228+
logger.log('[Measurements] Adding Updated CLS Data');
229+
this._updatedClsEntry.sources.map((source, index) =>
230+
transaction.setTag(`updated-cls.source.${index + 1}`, htmlTreeAsString(source.node)),
231+
);
232+
}
224233
}
225234

226235
/** Starts tracking the Cumulative Layout Shift on the current page. */
227236
private _trackCLS(): void {
228237
getCLS(metric => {
229238
const entry = metric.entries.pop();
230-
231239
if (!entry) {
232240
return;
233241
}
@@ -236,6 +244,17 @@ export class MetricsInstrumentation {
236244
this._measurements['cls'] = { value: metric.value };
237245
this._clsEntry = entry as LayoutShift;
238246
});
247+
248+
getUpdatedCLS(metric => {
249+
const entry = metric.entries.pop();
250+
if (!entry) {
251+
return;
252+
}
253+
254+
logger.log('[Measurements] Adding Updated CLS');
255+
this._measurements['updated-cls'] = { value: metric.value };
256+
this._updatedClsEntry = entry as LayoutShift;
257+
});
239258
}
240259

241260
/**

packages/tracing/src/browser/web-vitals/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Current vendored web vitals are:
1818

1919
## CHANGELOG
2020

21+
https://github.com/getsentry/sentry-javascript/pull/3781
22+
- Bumped from Web Vitals v0.2.4 to v2.1.0
23+
2124
https://github.com/getsentry/sentry-javascript/pull/3515
2225
- Remove support for Time to First Byte (TTFB)
2326

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

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,38 +38,12 @@ export const getCLS = (onReport: ReportHandler, reportAllChanges?: boolean): voi
3838
const metric = initMetric('CLS', 0);
3939
let report: ReturnType<typeof bindReporter>;
4040

41-
let sessionValue = 0;
42-
let sessionEntries: PerformanceEntry[] = [];
43-
4441
const entryHandler = (entry: LayoutShift): void => {
45-
// Only count layout shifts without recent user input.
4642
if (!entry.hadRecentInput) {
47-
const firstSessionEntry = sessionEntries[0];
48-
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
49-
50-
// If the entry occurred less than 1 second after the previous entry and
51-
// less than 5 seconds after the first entry in the session, include the
52-
// entry in the current session. Otherwise, start a new session.
53-
if (
54-
sessionValue &&
55-
entry.startTime - lastSessionEntry.startTime < 1000 &&
56-
entry.startTime - firstSessionEntry.startTime < 5000
57-
) {
58-
sessionValue += entry.value;
59-
sessionEntries.push(entry);
60-
} else {
61-
sessionValue = entry.value;
62-
sessionEntries = [entry];
63-
}
64-
65-
// If the current session value is larger than the current CLS value,
66-
// update CLS and the entries contributing to it.
67-
if (sessionValue > metric.value) {
68-
metric.value = sessionValue;
69-
metric.entries = sessionEntries;
70-
if (report) {
71-
report();
72-
}
43+
(metric.value as number) += entry.value;
44+
metric.entries.push(entry);
45+
if (report) {
46+
report();
7347
}
7448
}
7549
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { bindReporter } from './lib/bindReporter';
18+
import { initMetric } from './lib/initMetric';
19+
import { observe, PerformanceEntryHandler } from './lib/observe';
20+
import { onHidden } from './lib/onHidden';
21+
import { ReportHandler } from './types';
22+
23+
// https://wicg.github.io/layout-instability/#sec-layout-shift
24+
export interface LayoutShift extends PerformanceEntry {
25+
value: number;
26+
hadRecentInput: boolean;
27+
sources: Array<LayoutShiftAttribution>;
28+
toJSON(): Record<string, unknown>;
29+
}
30+
31+
export interface LayoutShiftAttribution {
32+
node?: Node;
33+
previousRect: DOMRectReadOnly;
34+
currentRect: DOMRectReadOnly;
35+
}
36+
37+
export const getUpdatedCLS = (onReport: ReportHandler, reportAllChanges?: boolean): void => {
38+
const metric = initMetric('UpdatedCLS', 0);
39+
let report: ReturnType<typeof bindReporter>;
40+
41+
let sessionValue = 0;
42+
let sessionEntries: PerformanceEntry[] = [];
43+
44+
const entryHandler = (entry: LayoutShift): void => {
45+
// Only count layout shifts without recent user input.
46+
if (!entry.hadRecentInput) {
47+
const firstSessionEntry = sessionEntries[0];
48+
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
49+
50+
// If the entry occurred less than 1 second after the previous entry and
51+
// less than 5 seconds after the first entry in the session, include the
52+
// entry in the current session. Otherwise, start a new session.
53+
if (
54+
sessionValue &&
55+
entry.startTime - lastSessionEntry.startTime < 1000 &&
56+
entry.startTime - firstSessionEntry.startTime < 5000
57+
) {
58+
sessionValue += entry.value;
59+
sessionEntries.push(entry);
60+
} else {
61+
sessionValue = entry.value;
62+
sessionEntries = [entry];
63+
}
64+
65+
// If the current session value is larger than the current CLS value,
66+
// update CLS and the entries contributing to it.
67+
if (sessionValue > metric.value) {
68+
metric.value = sessionValue;
69+
metric.entries = sessionEntries;
70+
if (report) {
71+
report();
72+
}
73+
}
74+
}
75+
};
76+
77+
const po = observe('layout-shift', entryHandler as PerformanceEntryHandler);
78+
if (po) {
79+
report = bindReporter(onReport, metric, reportAllChanges);
80+
81+
onHidden(() => {
82+
po.takeRecords().map(entryHandler as PerformanceEntryHandler);
83+
report(true);
84+
});
85+
}
86+
};

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
export interface Metric {
1818
// The name of the metric (in acronym form).
19-
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB';
19+
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB' | 'UpdatedCLS';
2020

2121
// The current value of the metric.
2222
value: number;

0 commit comments

Comments
 (0)