Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RUMF-636] initial document trace id #492

Merged
merged 10 commits into from
Aug 24, 2020
Merged
5 changes: 3 additions & 2 deletions packages/rum/src/lifeCycle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ErrorMessage } from '@datadog/browser-core'
import { RumPerformanceEntry } from './performanceCollection'
import { RequestCompleteEvent, RequestStartEvent } from './requestCollection'
import { AutoUserAction, CustomUserAction } from './userActionCollection'
import { View } from './viewCollection'
Expand Down Expand Up @@ -28,7 +29,7 @@ export class LifeCycle {
private callbacks: { [key in LifeCycleEventType]?: Array<(data: any) => void> } = {}

notify(eventType: LifeCycleEventType.ERROR_COLLECTED, data: ErrorMessage): void
notify(eventType: LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, data: PerformanceEntry): void
notify(eventType: LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, data: RumPerformanceEntry): void
notify(eventType: LifeCycleEventType.REQUEST_STARTED, data: RequestStartEvent): void
notify(eventType: LifeCycleEventType.REQUEST_COMPLETED, data: RequestCompleteEvent): void
notify(eventType: LifeCycleEventType.AUTO_ACTION_COMPLETED, data: AutoUserAction): void
Expand All @@ -54,7 +55,7 @@ export class LifeCycle {
subscribe(eventType: LifeCycleEventType.ERROR_COLLECTED, callback: (data: ErrorMessage) => void): Subscription
subscribe(
eventType: LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
callback: (data: PerformanceEntry) => void
callback: (data: RumPerformanceEntry) => void
): Subscription
subscribe(eventType: LifeCycleEventType.REQUEST_STARTED, callback: (data: RequestStartEvent) => void): Subscription
subscribe(
Expand Down
137 changes: 85 additions & 52 deletions packages/rum/src/performanceCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,52 @@ interface BrowserWindow extends Window {
PerformanceObserver?: PerformanceObserver
}

export interface RumPerformanceResourceTiming {
BenoitZugmeyer marked this conversation as resolved.
Show resolved Hide resolved
entryType: 'resource'
initiatorType: string
name: string
startTime: number
duration: number
fetchStart: number
domainLookupStart: number
domainLookupEnd: number
connectStart: number
secureConnectionStart: number
connectEnd: number
requestStart: number
responseStart: number
responseEnd: number
redirectStart: number
redirectEnd: number
decodedBodySize: number
}

export interface RumPerformanceLongTaskTiming {
entryType: 'longtask'
startTime: number
duration: number
}

export interface RumPerformancePaintTiming {
entryType: 'paint'
name: 'first-paint' | 'first-contentful-paint'
startTime: number
}

export interface RumPerformanceNavigationTiming {
entryType: 'navigation'
domComplete: number
domContentLoadedEventEnd: number
domInteractive: number
loadEventEnd: number
}

export type RumPerformanceEntry =
| RumPerformanceResourceTiming
| RumPerformanceLongTaskTiming
| RumPerformancePaintTiming
| RumPerformanceNavigationTiming

function supportPerformanceObject() {
return window.performance !== undefined && 'getEntries' in performance
}
Expand All @@ -21,7 +67,7 @@ function supportPerformanceNavigationTimingEvent() {

export function startPerformanceCollection(lifeCycle: LifeCycle) {
retrieveInitialDocumentResourceTiming((timing) => {
handlePerformanceEntries(lifeCycle, [timing])
handleRumPerformanceEntry(lifeCycle, timing)
})
if (supportPerformanceObject()) {
handlePerformanceEntries(lifeCycle, performance.getEntries())
Expand All @@ -47,58 +93,40 @@ export function startPerformanceCollection(lifeCycle: LifeCycle) {
}
if (!supportPerformanceNavigationTimingEvent()) {
retrieveNavigationTimingWhenLoaded((timing) => {
handlePerformanceEntries(lifeCycle, [timing])
handleRumPerformanceEntry(lifeCycle, timing)
})
}
}

interface FakeResourceTiming extends PerformanceEntry {
entryType: 'resource'
initiatorType: string
duration: number
decodedBodySize: number
name: string
redirectStart: number
redirectEnd: number
domainLookupStart: number
domainLookupEnd: number
connectStart: number
connectEnd: number
secureConnectionStart: number
requestStart: number
responseStart: number
responseEnd: number
}

function retrieveInitialDocumentResourceTiming(callback: (timing: PerformanceResourceTiming) => void) {
let timing: Partial<FakeResourceTiming>
function retrieveInitialDocumentResourceTiming(callback: (timing: RumPerformanceResourceTiming) => void) {
let timing: RumPerformanceResourceTiming
const forcedAttributes = {
entryType: 'resource' as const,
initiatorType: FAKE_INITIAL_DOCUMENT,
}
if (supportPerformanceNavigationTimingEvent() && performance.getEntriesByType('navigation').length > 0) {
const navigationEntry = performance.getEntriesByType('navigation')[0]
timing = { ...navigationEntry.toJSON() }
timing = { ...navigationEntry.toJSON(), ...forcedAttributes }
} else {
timing = { ...computeRelativePerformanceTiming(), name: window.location.href, decodedBodySize: 0, startTime: 0 }
const relativePerformanceTiming = computeRelativePerformanceTiming()
timing = {
...relativePerformanceTiming,
decodedBodySize: 0,
duration: relativePerformanceTiming.responseEnd,
name: window.location.href,
startTime: 0,
...forcedAttributes,
}
}
timing.entryType = 'resource'
timing.initiatorType = FAKE_INITIAL_DOCUMENT
timing.duration = timing.responseEnd
callback(timing as PerformanceResourceTiming)
}

interface FakePerformanceNavigationTiming {
entryType: 'navigation'
domComplete: number
domContentLoadedEventEnd: number
domInteractive: number
loadEventEnd: number
callback(timing)
}

function retrieveNavigationTimingWhenLoaded(callback: (timing: PerformanceNavigationTiming) => void) {
function retrieveNavigationTimingWhenLoaded(callback: (timing: RumPerformanceNavigationTiming) => void) {
function sendFakeTiming() {
const timing: FakePerformanceNavigationTiming = {
callback({
...computeRelativePerformanceTiming(),
entryType: 'navigation',
}
callback((timing as unknown) as PerformanceNavigationTiming)
})
}

if (document.readyState === 'complete') {
Expand Down Expand Up @@ -130,18 +158,23 @@ function computeRelativePerformanceTiming() {
}

function handlePerformanceEntries(lifeCycle: LifeCycle, entries: PerformanceEntry[]) {
function notify(entry: PerformanceEntry) {
lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, entry)
}

entries.filter((entry) => entry.entryType === 'resource').forEach(notify)
entries.forEach((entry) => {
if (
entry.entryType === 'resource' ||
entry.entryType === 'navigation' ||
entry.entryType === 'paint' ||
entry.entryType === 'longtask'
) {
handleRumPerformanceEntry(lifeCycle, entry as RumPerformanceEntry)
}
})
}

entries
.filter((entry) => entry.entryType === 'navigation')
// Exclude incomplete navigation entries by filtering out those who have a loadEventEnd at 0
.filter((entry) => (entry as PerformanceNavigationTiming).loadEventEnd > 0)
.forEach(notify)
function handleRumPerformanceEntry(lifeCycle: LifeCycle, entry: RumPerformanceEntry) {
bcaudan marked this conversation as resolved.
Show resolved Hide resolved
// Exclude incomplete navigation entries by filtering out those who have a loadEventEnd at 0
if (entry.entryType === 'navigation' && entry.loadEventEnd <= 0) {
return
}

entries.filter((entry) => entry.entryType === 'paint').forEach(notify)
entries.filter((entry) => entry.entryType === 'longtask').forEach(notify)
lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, entry)
}
11 changes: 7 additions & 4 deletions packages/rum/src/resourceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ResourceKind,
} from '@datadog/browser-core'

import { RumPerformanceResourceTiming } from './performanceCollection'
import { PerformanceResourceDetails } from './rum'

export const FAKE_INITIAL_DOCUMENT = 'initial_document'
Expand All @@ -33,7 +34,7 @@ const RESOURCE_TYPES: Array<[ResourceKind, (initiatorType: string, path: string)
],
]

export function computeResourceKind(timing: PerformanceResourceTiming) {
export function computeResourceKind(timing: RumPerformanceResourceTiming) {
const url = timing.name
if (!isValidUrl(url)) {
addMonitoringMessage(`Failed to construct URL for "${timing.name}"`)
Expand All @@ -57,7 +58,9 @@ function areInOrder(...numbers: number[]) {
return true
}

export function computePerformanceResourceDuration(entry: PerformanceResourceTiming): number {
export function computePerformanceResourceDuration(
entry: RumPerformanceResourceTiming | PerformanceResourceTiming
bcaudan marked this conversation as resolved.
Show resolved Hide resolved
): number {
const { duration, startTime, responseEnd } = entry

// Safari duration is always 0 on timings blocked by cross origin policies.
Expand All @@ -69,7 +72,7 @@ export function computePerformanceResourceDuration(entry: PerformanceResourceTim
}

export function computePerformanceResourceDetails(
entry: PerformanceResourceTiming
entry: RumPerformanceResourceTiming | PerformanceResourceTiming
): PerformanceResourceDetails | undefined {
const {
startTime,
Expand Down Expand Up @@ -158,7 +161,7 @@ function formatTiming(origin: number, start: number, end: number) {
}
}

export function computeSize(entry: PerformanceResourceTiming) {
export function computeSize(entry: RumPerformanceResourceTiming | PerformanceResourceTiming) {
// Make sure a request actually occured
if (entry.startTime < entry.responseStart) {
return entry.decodedBodySize
Expand Down
18 changes: 5 additions & 13 deletions packages/rum/src/rum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { LifeCycle, LifeCycleEventType } from './lifeCycle'
import { matchRequestTiming } from './matchRequestTiming'
import { ActionContext, ParentContexts, startParentContexts, ViewContext } from './parentContexts'
import { RumPerformanceLongTaskTiming, RumPerformanceResourceTiming } from './performanceCollection'
import { RequestCompleteEvent } from './requestCollection'
import {
computePerformanceResourceDetails,
Expand All @@ -37,15 +38,6 @@ import { toDecimalString } from './tracer'
import { UserActionMeasures, UserActionType } from './userActionCollection'
import { startViewCollection, ViewLoadingType, ViewMeasures } from './viewCollection'

export interface PerformancePaintTiming extends PerformanceEntry {
entryType: 'paint'
name: 'first-paint' | 'first-contentful-paint'
startTime: number
duration: 0
}

export type PerformanceLongTaskTiming = PerformanceEntry

export enum RumEventCategory {
USER_ACTION = 'user_action',
ERROR = 'error',
Expand Down Expand Up @@ -491,10 +483,10 @@ function trackPerformanceTiming(
lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, (entry) => {
switch (entry.entryType) {
case 'resource':
handleResourceEntry(configuration, lifeCycle, session, handler, entry as PerformanceResourceTiming)
handleResourceEntry(configuration, lifeCycle, session, handler, entry)
break
case 'longtask':
handleLongTaskEntry(handler, entry as PerformanceLongTaskTiming)
handleLongTaskEntry(handler, entry)
break
default:
break
Expand All @@ -507,7 +499,7 @@ export function handleResourceEntry(
lifeCycle: LifeCycle,
session: RumSession,
handler: (startTime: number, event: RumResourceEvent) => void,
entry: PerformanceResourceTiming
entry: RumPerformanceResourceTiming
) {
if (!session.isTrackedWithResource()) {
return
Expand Down Expand Up @@ -541,7 +533,7 @@ export function handleResourceEntry(

function handleLongTaskEntry(
handler: (startTime: number, event: RumLongTaskEvent) => void,
entry: PerformanceLongTaskTiming
entry: RumPerformanceLongTaskTiming
) {
handler(entry.startTime, {
date: getTimestamp(entry.startTime),
Expand Down
13 changes: 5 additions & 8 deletions packages/rum/src/viewCollection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { DOM_EVENT, generateUUID, monitor, ONE_MINUTE, throttle } from '@datadog/browser-core'

import { LifeCycle, LifeCycleEventType } from './lifeCycle'
import { PerformancePaintTiming } from './rum'
import { EventCounts, trackEventCounts } from './trackEventCounts'
import { waitIdlePageActivity } from './trackPageActivities'

Expand Down Expand Up @@ -208,20 +207,18 @@ function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings) => void
LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
(entry) => {
if (entry.entryType === 'navigation') {
const navigationEntry = entry as PerformanceNavigationTiming
timings = {
...timings,
domComplete: navigationEntry.domComplete,
domContentLoaded: navigationEntry.domContentLoadedEventEnd,
domInteractive: navigationEntry.domInteractive,
loadEventEnd: navigationEntry.loadEventEnd,
domComplete: entry.domComplete,
domContentLoaded: entry.domContentLoadedEventEnd,
domInteractive: entry.domInteractive,
loadEventEnd: entry.loadEventEnd,
}
callback(timings)
} else if (entry.entryType === 'paint' && entry.name === 'first-contentful-paint') {
const paintEntry = entry as PerformancePaintTiming
timings = {
...timings,
firstContentfulPaint: paintEntry.startTime,
firstContentfulPaint: entry.startTime,
}
callback(timings)
}
Expand Down
7 changes: 4 additions & 3 deletions packages/rum/test/resourceUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Configuration, DEFAULT_CONFIGURATION, SPEC_ENDPOINTS } from '@datadog/browser-core'
import { RumPerformanceResourceTiming } from '../src/performanceCollection'
import {
computePerformanceResourceDetails,
computePerformanceResourceDuration,
isValidResource,
} from '../src/resourceUtils'

function generateResourceWith(overrides: Partial<PerformanceResourceTiming>) {
const completeTiming: Partial<PerformanceResourceTiming> = {
function generateResourceWith(overrides: Partial<RumPerformanceResourceTiming>) {
const completeTiming: Partial<RumPerformanceResourceTiming> = {
connectEnd: 17,
connectStart: 15,
domainLookupEnd: 14,
Expand All @@ -24,7 +25,7 @@ function generateResourceWith(overrides: Partial<PerformanceResourceTiming>) {
startTime: 10,
...overrides,
}
return completeTiming as PerformanceResourceTiming
return completeTiming as RumPerformanceResourceTiming
}

describe('computePerformanceResourceDetails', () => {
Expand Down
Loading