Skip to content

Commit

Permalink
Merge branch 'aymeric/debug-selector-match-nodeid' (cd2d54b) into sta…
Browse files Browse the repository at this point in the history
…ging-29

 pm_trace_id: 17966364
 feature_branch_pipeline_id: 17966364
 source: to-staging

* commit 'cd2d54b63859edd8c4cab09c591f064e37a04800':
  Update imports
  Add telemetry debug web vital attribution data
  Update RecorderApi - Add recorderStartObservable - Expose getSerializedNodeId
  • Loading branch information
amortemousque committed Jul 19, 2023
2 parents 6b60006 + cd2d54b commit 4592763
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 37 deletions.
4 changes: 3 additions & 1 deletion packages/rum-core/src/boot/rumPublicApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Context, InitConfiguration, TimeStamp, RelativeTime, User } from '@datadog/browser-core'
import type { Context, InitConfiguration, TimeStamp, RelativeTime, User, Observable } from '@datadog/browser-core'
import {
noop,
CustomerDataType,
Expand Down Expand Up @@ -52,6 +52,8 @@ export interface RecorderApi {
sessionManager: RumSessionManager,
viewContexts: ViewContexts
) => string | undefined
getSerializedNodeId: (node: Node) => number | undefined
recorderStartObservable: Observable<RelativeTime>
}
interface RumPublicApiOptions {
ignoreInitIfSyntheticsWillInjectRum?: boolean
Expand Down
5 changes: 5 additions & 0 deletions packages/rum-core/src/browser/performanceCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,24 @@ export interface RumLargestContentfulPaintTiming {
entryType: 'largest-contentful-paint'
startTime: RelativeTime
size: number
element?: Element
}

export interface RumFirstInputTiming {
entryType: 'first-input'
startTime: RelativeTime
processingStart: RelativeTime
target?: Node
}

export interface RumLayoutShiftTiming {
entryType: 'layout-shift'
startTime: RelativeTime
value: number
hadRecentInput: boolean
sources?: Array<{
node?: Node
}>
}

export type RumPerformanceEntry =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { addTelemetryDebug, elapsed, relativeNow, type RelativeTime } from '@datadog/browser-core'
import type { RecorderApi } from '../../../boot/rumPublicApi'

export function addWebVitalTelemetryDebug(
recorderApi: RecorderApi,
webVitalName: string,
webVitalNode: Node | undefined,
webVitalTime: RelativeTime
) {
const computationTime = relativeNow()
if (!recorderApi.isRecording()) {
recorderApi.recorderStartObservable.subscribe((recordingStartTime) => {
addTelemetryDebug(`${webVitalName} attribution recording delay`, {
computationDelay: elapsed(webVitalTime, computationTime),
recordingDelay: elapsed(webVitalTime, recordingStartTime),
hasNode: !!webVitalNode,
serializedDomNode: webVitalNode ? recorderApi.getSerializedNodeId(webVitalNode) : undefined,
})
})
}

addTelemetryDebug(`${webVitalName} attribution`, {
computationDelay: elapsed(webVitalTime, computationTime),
hasNode: !!webVitalNode,
replayRecording: recorderApi.isRecording(),
serializedDomNode: webVitalNode ? recorderApi.getSerializedNodeId(webVitalNode) : undefined,
})
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BuildContext } from '../../../../test'
import { noopRecorderApi, type BuildContext } from '../../../../test'
import { LifeCycleEventType } from '../../lifeCycle'
import type { ViewEvent, ViewOptions } from './trackViews'
import { trackViews } from './trackViews'
Expand Down Expand Up @@ -33,6 +33,7 @@ export function setupViewTest(
configuration,
locationChangeObservable,
!configuration.trackViewsManually,
noopRecorderApi,
initialViewOptions
)
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Duration, RelativeTime } from '@datadog/browser-core'
import { DOM_EVENT } from '@datadog/browser-core'
import { restorePageVisibility, setPageVisibility, createNewEvent } from '@datadog/browser-core/test'
import type { TestSetupBuilder } from '../../../../test'
import { setup } from '../../../../test'
import { noopRecorderApi, setup } from '../../../../test'
import type {
RumFirstInputTiming,
RumLargestContentfulPaintTiming,
Expand Down Expand Up @@ -41,12 +41,14 @@ const FAKE_LARGEST_CONTENTFUL_PAINT_ENTRY: RumLargestContentfulPaintTiming = {
entryType: 'largest-contentful-paint',
startTime: 789 as RelativeTime,
size: 10,
element: document.createElement('div'),
}

const FAKE_FIRST_INPUT_ENTRY: RumFirstInputTiming = {
entryType: 'first-input',
processingStart: 1100 as RelativeTime,
startTime: 1000 as RelativeTime,
target: document.createElement('button'),
}

describe('trackInitialViewTimings', () => {
Expand All @@ -59,7 +61,12 @@ describe('trackInitialViewTimings', () => {
scheduleViewUpdateSpy = jasmine.createSpy()
setLoadEventSpy = jasmine.createSpy()
setupBuilder = setup().beforeBuild(({ lifeCycle }) => {
trackInitialViewTimingsResult = trackInitialViewTimings(lifeCycle, setLoadEventSpy, scheduleViewUpdateSpy)
trackInitialViewTimingsResult = trackInitialViewTimings(
lifeCycle,
noopRecorderApi,
setLoadEventSpy,
scheduleViewUpdateSpy
)
return trackInitialViewTimingsResult
})
})
Expand Down Expand Up @@ -194,7 +201,7 @@ describe('trackFirstContentfulPaintTiming', () => {

describe('largestContentfulPaintTiming', () => {
let setupBuilder: TestSetupBuilder
let lcpCallback: jasmine.Spy<(value: RelativeTime) => void>
let lcpCallback: jasmine.Spy<(value: RelativeTime, lcpElement: Element | undefined) => void>
let eventTarget: Window

beforeEach(() => {
Expand All @@ -217,7 +224,7 @@ describe('largestContentfulPaintTiming', () => {

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [FAKE_LARGEST_CONTENTFUL_PAINT_ENTRY])
expect(lcpCallback).toHaveBeenCalledTimes(1 as RelativeTime)
expect(lcpCallback).toHaveBeenCalledWith(789 as RelativeTime)
expect(lcpCallback).toHaveBeenCalledWith(789 as RelativeTime, jasmine.any(Element))
})

it('should be discarded if it is reported after a user interaction', () => {
Expand Down Expand Up @@ -254,7 +261,14 @@ describe('largestContentfulPaintTiming', () => {
describe('firstInputTimings', () => {
let setupBuilder: TestSetupBuilder
let fitCallback: jasmine.Spy<
({ firstInputDelay, firstInputTime }: { firstInputDelay: number; firstInputTime: number }) => void
({
firstInputDelay,
firstInputTime,
}: {
firstInputDelay: number
firstInputTime: number
firstInputTarget: Node | undefined
}) => void
>

beforeEach(() => {
Expand All @@ -274,7 +288,11 @@ describe('firstInputTimings', () => {

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [FAKE_FIRST_INPUT_ENTRY])
expect(fitCallback).toHaveBeenCalledTimes(1)
expect(fitCallback).toHaveBeenCalledWith({ firstInputDelay: 100, firstInputTime: 1000 })
expect(fitCallback).toHaveBeenCalledWith({
firstInputDelay: 100,
firstInputTime: 1000,
firstInputTarget: jasmine.any(Node),
})
})

it('should be discarded if the page is hidden', () => {
Expand All @@ -298,6 +316,6 @@ describe('firstInputTimings', () => {
])

expect(fitCallback).toHaveBeenCalledTimes(1)
expect(fitCallback).toHaveBeenCalledWith({ firstInputDelay: 0, firstInputTime: 1000 })
expect(fitCallback).toHaveBeenCalledWith({ firstInputDelay: 0, firstInputTime: 1000, firstInputTarget: undefined })
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
relativeNow,
} from '@datadog/browser-core'

import type { RecorderApi } from '../../../boot/rumPublicApi'
import type { LifeCycle } from '../../lifeCycle'
import { LifeCycleEventType } from '../../lifeCycle'
import type {
Expand All @@ -19,6 +20,7 @@ import type {
RumPerformancePaintTiming,
} from '../../../browser/performanceCollection'
import { trackFirstHidden } from './trackFirstHidden'
import { addWebVitalTelemetryDebug } from './addWebVitalTelemetryDebug'

// Discard LCP and FCP timings above a certain delay to avoid incorrect data
// It happens in some cases like sleep mode or some browser implementations
Expand Down Expand Up @@ -46,6 +48,7 @@ export interface Timings {

export function trackInitialViewTimings(
lifeCycle: LifeCycle,
recorderApi: RecorderApi,
setLoadEvent: (loadEnd: Duration) => void,
scheduleViewUpdate: () => void
) {
Expand All @@ -63,17 +66,29 @@ export function trackInitialViewTimings(
const { stop: stopFCPTracking } = trackFirstContentfulPaintTiming(lifeCycle, (firstContentfulPaint) =>
setTimings({ firstContentfulPaint })
)
const { stop: stopLCPTracking } = trackLargestContentfulPaintTiming(lifeCycle, window, (largestContentfulPaint) => {
setTimings({
largestContentfulPaint,
})
})
const { stop: stopFIDTracking } = trackFirstInputTimings(lifeCycle, ({ firstInputDelay, firstInputTime }) => {
setTimings({
firstInputDelay,
firstInputTime,
})
})
const { stop: stopLCPTracking } = trackLargestContentfulPaintTiming(
lifeCycle,
window,
(largestContentfulPaint, lcpElement) => {
addWebVitalTelemetryDebug(recorderApi, 'LCP', lcpElement, largestContentfulPaint)

setTimings({
largestContentfulPaint,
})
}
)

const { stop: stopFIDTracking } = trackFirstInputTimings(
lifeCycle,
({ firstInputDelay, firstInputTime, firstInputTarget }) => {
addWebVitalTelemetryDebug(recorderApi, 'FID', firstInputTarget, firstInputTime)

setTimings({
firstInputDelay,
firstInputTime,
})
}
)

function stop() {
stopNavigationTracking()
Expand Down Expand Up @@ -148,7 +163,7 @@ export function trackFirstContentfulPaintTiming(lifeCycle: LifeCycle, callback:
export function trackLargestContentfulPaintTiming(
lifeCycle: LifeCycle,
eventTarget: Window,
callback: (lcpTiming: RelativeTime) => void
callback: (lcpTiming: RelativeTime, lcpElement?: Element) => void
) {
const firstHidden = trackFirstHidden()

Expand Down Expand Up @@ -177,7 +192,7 @@ export function trackLargestContentfulPaintTiming(
entry.startTime < TIMING_MAXIMUM_DELAY
)
if (lcpEntry) {
callback(lcpEntry.startTime)
callback(lcpEntry.startTime, lcpEntry.element)
}
}
)
Expand All @@ -200,7 +215,15 @@ export function trackLargestContentfulPaintTiming(
*/
export function trackFirstInputTimings(
lifeCycle: LifeCycle,
callback: ({ firstInputDelay, firstInputTime }: { firstInputDelay: Duration; firstInputTime: Duration }) => void
callback: ({
firstInputDelay,
firstInputTime,
firstInputTarget,
}: {
firstInputDelay: Duration
firstInputTime: RelativeTime
firstInputTarget: Node | undefined
}) => void
) {
const firstHidden = trackFirstHidden()

Expand All @@ -216,7 +239,8 @@ export function trackFirstInputTimings(
// Ensure firstInputDelay to be positive, see
// https://bugs.chromium.org/p/chromium/issues/detail?id=1185815
firstInputDelay: firstInputDelay >= 0 ? firstInputDelay : (0 as Duration),
firstInputTime: firstInputEntry.startTime as Duration,
firstInputTime: firstInputEntry.startTime,
firstInputTarget: firstInputEntry.target,
})
}
})
Expand Down
Loading

0 comments on commit 4592763

Please sign in to comment.