diff --git a/packages/tracing/src/browser/metrics.ts b/packages/tracing/src/browser/metrics.ts index 73dec5cee29a..118de73e8510 100644 --- a/packages/tracing/src/browser/metrics.ts +++ b/packages/tracing/src/browser/metrics.ts @@ -6,7 +6,7 @@ import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, isNode import { Span } from '../span'; import { Transaction } from '../transaction'; import { msToSec } from '../utils'; -import { getCLS } from './web-vitals/getCLS'; +import { getCLS, LayoutShift } from './web-vitals/getCLS'; import { getFID } from './web-vitals/getFID'; import { getLCP, LargestContentfulPaint } from './web-vitals/getLCP'; import { getFirstHidden } from './web-vitals/lib/getFirstHidden'; @@ -20,6 +20,7 @@ export class MetricsInstrumentation { private _performanceCursor: number = 0; private _lcpEntry: LargestContentfulPaint | undefined; + private _clsEntry: LayoutShift | undefined; public constructor() { if (!isNodeEnv() && global?.performance) { @@ -187,26 +188,37 @@ export class MetricsInstrumentation { } transaction.setMeasurements(this._measurements); + this._tagMetricInfo(transaction); + } + } - if (this._lcpEntry) { - logger.log('[Measurements] Adding LCP Data'); - // Capture Properties of the LCP element that contributes to the LCP. - - if (this._lcpEntry.element) { - transaction.setTag('lcp.element', htmlTreeAsString(this._lcpEntry.element)); - } + /** Add LCP / CLS data to transaction to allow debugging */ + private _tagMetricInfo(transaction: Transaction): void { + if (this._lcpEntry) { + logger.log('[Measurements] Adding LCP Data'); + // Capture Properties of the LCP element that contributes to the LCP. - if (this._lcpEntry.id) { - transaction.setTag('lcp.id', this._lcpEntry.id); - } + if (this._lcpEntry.element) { + transaction.setTag('lcp.element', htmlTreeAsString(this._lcpEntry.element)); + } - if (this._lcpEntry.url) { - // Trim URL to the first 200 characters. - transaction.setTag('lcp.url', this._lcpEntry.url.trim().slice(0, 200)); - } + if (this._lcpEntry.id) { + transaction.setTag('lcp.id', this._lcpEntry.id); + } - transaction.setTag('lcp.size', this._lcpEntry.size); + if (this._lcpEntry.url) { + // Trim URL to the first 200 characters. + transaction.setTag('lcp.url', this._lcpEntry.url.trim().slice(0, 200)); } + + transaction.setTag('lcp.size', this._lcpEntry.size); + } + + if (this._clsEntry) { + logger.log('[Measurements] Adding CLS Data'); + this._clsEntry.sources.map((source, index) => + transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)), + ); } } @@ -221,6 +233,7 @@ export class MetricsInstrumentation { logger.log('[Measurements] Adding CLS'); this._measurements['cls'] = { value: metric.value }; + this._clsEntry = entry as LayoutShift; }); } diff --git a/packages/tracing/src/browser/web-vitals/getCLS.ts b/packages/tracing/src/browser/web-vitals/getCLS.ts index 530951834e47..71c6f06cec85 100644 --- a/packages/tracing/src/browser/web-vitals/getCLS.ts +++ b/packages/tracing/src/browser/web-vitals/getCLS.ts @@ -21,9 +21,17 @@ import { onHidden } from './lib/onHidden'; import { ReportHandler } from './types'; // https://wicg.github.io/layout-instability/#sec-layout-shift -interface LayoutShift extends PerformanceEntry { +export interface LayoutShift extends PerformanceEntry { value: number; hadRecentInput: boolean; + sources: Array; + toJSON(): Record; +} + +export interface LayoutShiftAttribution { + node?: Node; + previousRect: DOMRectReadOnly; + currentRect: DOMRectReadOnly; } export const getCLS = (onReport: ReportHandler, reportAllChanges = false): void => {