1- import type { Context , Telemetry } from '@datadog/browser-core'
2- import { performDraw , addTelemetryMetrics , noop } from '@datadog/browser-core'
1+ import type { Context , RelativeTime , Telemetry } from '@datadog/browser-core'
2+ import { PageExitReason , performDraw , addTelemetryMetrics , noop , relativeNow } from '@datadog/browser-core'
3+ import { getNavigationEntry } from '../../../browser/performanceUtils'
34import { LifeCycleEventType } from '../../lifeCycle'
45import type { LifeCycle } from '../../lifeCycle'
56import type { RumConfiguration } from '../../configuration'
@@ -8,7 +9,7 @@ import type { NavigationTimings } from './trackNavigationTimings'
89
910const INITIAL_VIEW_METRICS_TELEMETRY_NAME = 'Initial view metrics'
1011
11- interface CoreInitialViewMetrics extends Context {
12+ interface AfterPageLoadInitialViewMetrics extends Context {
1213 lcp : {
1314 value : number
1415 }
@@ -21,6 +22,13 @@ interface CoreInitialViewMetrics extends Context {
2122 }
2223}
2324
25+ interface EarlyPageUnloadInitialViewMetrics extends Context {
26+ earlyPageUnload : {
27+ domContentLoaded : number | undefined
28+ timestamp : number
29+ }
30+ }
31+
2432export function startInitialViewMetricsTelemetry (
2533 configuration : RumConfiguration ,
2634 lifeCycle : LifeCycle ,
@@ -32,38 +40,65 @@ export function startInitialViewMetricsTelemetry(
3240 return { stop : noop }
3341 }
3442
35- const { unsubscribe } = lifeCycle . subscribe ( LifeCycleEventType . VIEW_UPDATED , ( { initialViewMetrics } ) => {
36- if ( ! initialViewMetrics . largestContentfulPaint || ! initialViewMetrics . navigationTimings ) {
37- return
43+ const { unsubscribe : unsubscribePageMayExit } = lifeCycle . subscribe (
44+ LifeCycleEventType . PAGE_MAY_EXIT ,
45+ ( { reason } ) => {
46+ if ( reason !== PageExitReason . UNLOADING ) {
47+ return
48+ }
49+
50+ const navigationEntry = getNavigationEntry ( )
51+ addTelemetryMetrics ( INITIAL_VIEW_METRICS_TELEMETRY_NAME , {
52+ metrics : createEarlyPageUnloadInitialViewMetrics ( navigationEntry . domContentLoadedEventEnd , relativeNow ( ) ) ,
53+ } )
54+
55+ // Only send metrics in response to PAGE_MAY_EXIT once, but keep the subscription to
56+ // VIEW_UPDATED in case the page doesn't actually exit and we do eventually get
57+ // final numbers.
58+ unsubscribePageMayExit ( )
3859 }
60+ )
61+
62+ const { unsubscribe : unsubscribeViewUpdated } = lifeCycle . subscribe (
63+ LifeCycleEventType . VIEW_UPDATED ,
64+ ( { initialViewMetrics } ) => {
65+ if ( ! initialViewMetrics . largestContentfulPaint || ! initialViewMetrics . navigationTimings ) {
66+ return
67+ }
3968
40- // The navigation timings become available shortly after the load event fires, so
41- // we're snapshotting the LCP value available at that point. However, more LCP values
42- // can be emitted until the page is scrolled or interacted with, so it's possible that
43- // the final LCP value may differ. These metrics are intended to help diagnose
44- // performance issues early in the page load process, and using LCP-at-page-load is a
45- // good fit for that use case, but it's important to be aware that this is not
46- // necessarily equivalent to the normal LCP metric.
69+ // The navigation timings become available shortly after the load event fires, so
70+ // we're snapshotting the LCP value available at that point. However, more LCP values
71+ // can be emitted until the page is scrolled or interacted with, so it's possible that
72+ // the final LCP value may differ. These metrics are intended to help diagnose
73+ // performance issues early in the page load process, and using LCP-at-page-load is a
74+ // good fit for that use case, but it's important to be aware that this is not
75+ // necessarily equivalent to the normal LCP metric.
4776
48- addTelemetryMetrics ( INITIAL_VIEW_METRICS_TELEMETRY_NAME , {
49- metrics : createCoreInitialViewMetrics (
50- initialViewMetrics . largestContentfulPaint ,
51- initialViewMetrics . navigationTimings
52- ) ,
53- } )
77+ addTelemetryMetrics ( INITIAL_VIEW_METRICS_TELEMETRY_NAME , {
78+ metrics : createAfterPageLoadInitialViewMetrics (
79+ initialViewMetrics . largestContentfulPaint ,
80+ initialViewMetrics . navigationTimings
81+ ) ,
82+ } )
5483
55- unsubscribe ( )
56- } )
84+ // Don't send any further metrics.
85+ unsubscribePageMayExit ( )
86+ unsubscribeViewUpdated ( )
87+ }
88+ )
5789
5890 return {
59- stop : unsubscribe ,
91+ stop : ( ) => {
92+ unsubscribePageMayExit ( )
93+ unsubscribeViewUpdated ( )
94+ } ,
6095 }
6196}
6297
63- function createCoreInitialViewMetrics (
98+ function createAfterPageLoadInitialViewMetrics (
6499 lcp : LargestContentfulPaint ,
65100 navigation : NavigationTimings
66- ) : CoreInitialViewMetrics {
101+ ) : AfterPageLoadInitialViewMetrics {
67102 return {
68103 lcp : {
69104 value : lcp . value ,
@@ -77,3 +112,15 @@ function createCoreInitialViewMetrics(
77112 } ,
78113 }
79114}
115+
116+ function createEarlyPageUnloadInitialViewMetrics (
117+ domContentLoadedEventEnd : RelativeTime ,
118+ timestamp : RelativeTime
119+ ) : EarlyPageUnloadInitialViewMetrics {
120+ return {
121+ earlyPageUnload : {
122+ domContentLoaded : domContentLoadedEventEnd > 0 ? domContentLoadedEventEnd : undefined ,
123+ timestamp,
124+ } ,
125+ }
126+ }
0 commit comments