diff --git a/packages/core/src/tools/experimentalFeatures.ts b/packages/core/src/tools/experimentalFeatures.ts index 87cfc8b3a5..799a210e61 100644 --- a/packages/core/src/tools/experimentalFeatures.ts +++ b/packages/core/src/tools/experimentalFeatures.ts @@ -15,6 +15,7 @@ export enum ExperimentalFeature { FEATURE_FLAGS = 'feature_flags', RESOURCE_PAGE_STATES = 'resource_page_states', COLLECT_FLUSH_REASON = 'collect_flush_reason', + ZERO_LCP_TELEMETRY = 'zero_lcp_telemetry', SCROLLMAP = 'scrollmap', DISABLE_REPLAY_INLINE_CSS = 'disable_replay_inline_css', } diff --git a/packages/rum-core/src/browser/performanceCollection.ts b/packages/rum-core/src/browser/performanceCollection.ts index 05da072dee..740cd58c44 100644 --- a/packages/rum-core/src/browser/performanceCollection.ts +++ b/packages/rum-core/src/browser/performanceCollection.ts @@ -96,6 +96,7 @@ export interface RumLargestContentfulPaintTiming { startTime: RelativeTime size: number element?: Element + toJSON(): Omit } export interface RumFirstInputTiming { diff --git a/packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts b/packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts index 1d1e63da08..4c902e4c1c 100644 --- a/packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts +++ b/packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts @@ -1,5 +1,14 @@ import type { RelativeTime } from '@datadog/browser-core' -import { DOM_EVENT, ONE_MINUTE, addEventListeners, findLast } from '@datadog/browser-core' +import { + DOM_EVENT, + ExperimentalFeature, + ONE_MINUTE, + addEventListeners, + addTelemetryDebug, + findLast, + isExperimentalFeatureEnabled, + relativeNow, +} from '@datadog/browser-core' import { LifeCycleEventType } from '../../lifeCycle' import type { LifeCycle } from '../../lifeCycle' import type { RumConfiguration } from '../../configuration' @@ -16,6 +25,10 @@ export interface LargestContentfulPaint { value: RelativeTime targetSelector?: string } + +let zeroLcpReported = false +let previousNonZeroLcp: Omit | undefined + /** * Track the largest contentful paint (LCP) occurring during the initial View. This can yield * multiple values, only the most recent one should be used. @@ -61,6 +74,10 @@ export function trackLargestContentfulPaint( lcpTargetSelector = getSelectorFromElement(lcpEntry.element, configuration.actionNameAttribute) } + if (isExperimentalFeatureEnabled(ExperimentalFeature.ZERO_LCP_TELEMETRY)) { + monitorZeroLcpEntry(lcpEntry) + } + callback({ value: lcpEntry.startTime, targetSelector: lcpTargetSelector, @@ -76,3 +93,25 @@ export function trackLargestContentfulPaint( }, } } + +function monitorZeroLcpEntry(lcpEntry: RumLargestContentfulPaintTiming) { + if (!zeroLcpReported && lcpEntry.startTime !== 0) { + previousNonZeroLcp = lcpEntry.toJSON() + } + + if (!zeroLcpReported && lcpEntry.startTime === 0) { + zeroLcpReported = true + + addTelemetryDebug('LCP with startTime = 0', { + debug: { + entry: lcpEntry.toJSON(), + previousNonZeroLcp, + navigationStart: performance.timing.navigationStart, + timeOrigin: performance.timeOrigin, + now: relativeNow(), + visibilityState: document.visibilityState, + prerendering: (document as any).prerendering, + }, + }) + } +} diff --git a/packages/rum-core/test/fixtures.ts b/packages/rum-core/test/fixtures.ts index 76a26db043..af5cc6f294 100644 --- a/packages/rum-core/test/fixtures.ts +++ b/packages/rum-core/test/fixtures.ts @@ -155,8 +155,8 @@ export function createPerformanceEntry( }, overrides ) as EntryTypeToReturnType[T] - case RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT: - return assign( + case RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT: { + const entry = assign( { entryType: RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT, startTime: 789 as RelativeTime, @@ -164,6 +164,8 @@ export function createPerformanceEntry( }, overrides ) as EntryTypeToReturnType[T] + return { ...entry, toJSON: () => entry } + } case RumPerformanceEntryType.LAYOUT_SHIFT: return assign( {