diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index f70a1ccca9..45a935143d 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -158,3 +158,9 @@ export function includes(candidate: unknown[], search: unknown) { export function isPercentage(value: unknown) { return typeof value === 'number' && value >= 0 && value <= 100 } + +export function getRelativePerformanceTiming(performance: Performance, timestamp: number) { + // performance.timeOrigin is undefined in WebKit, see https://bugs.webkit.org/show_bug.cgi?id=174862 + const timeOrigin = performance.timeOrigin !== undefined ? performance.timeOrigin : performance.timing.navigationStart + return timestamp - timeOrigin +} diff --git a/packages/rum/src/startPerformanceCollection.ts b/packages/rum/src/performanceCollection.ts similarity index 63% rename from packages/rum/src/startPerformanceCollection.ts rename to packages/rum/src/performanceCollection.ts index 532b4e5bad..e6ffa94e32 100644 --- a/packages/rum/src/startPerformanceCollection.ts +++ b/packages/rum/src/performanceCollection.ts @@ -1,5 +1,6 @@ import { monitor } from '@browser-agent/core/src/internalMonitoring' import { Observable } from '@browser-agent/core/src/observable' +import { getRelativePerformanceTiming } from '@browser-agent/core/src/utils' import { RumSession } from './rumSession' declare global { @@ -13,20 +14,19 @@ function supportPerformanceObject() { } function supportPerformanceNavigationTimingEvent() { - if (PerformanceObserver.supportedEntryTypes) { - return PerformanceObserver.supportedEntryTypes.includes('navigation') - } - - return supportPerformanceObject() && performance.getEntriesByType('navigation').length > 0 + return ( + PerformanceObserver.supportedEntryTypes !== undefined && + PerformanceObserver.supportedEntryTypes.includes('navigation') + ) } export function startPerformanceCollection(performanceObservable: Observable, session: RumSession) { if (supportPerformanceObject()) { - handlePerformanceEntries(session, performanceObservable, performance) + handlePerformanceEntries(session, performanceObservable, performance.getEntries()) } if (window.PerformanceObserver) { const observer = new PerformanceObserver( - monitor((entries) => handlePerformanceEntries(session, performanceObservable, entries)) + monitor((entries) => handlePerformanceEntries(session, performanceObservable, entries.getEntries())) ) observer.observe({ entryTypes: ['resource', 'navigation', 'paint', 'longtask'] }) @@ -37,7 +37,7 @@ export function startPerformanceCollection(performanceObservable: Observable>( - performance: Performance, - name: T -) { - // performance.timeOrigin is undefined in WebKit, see https://bugs.webkit.org/show_bug.cgi?id=174862 - const timeOrigin = performance.timeOrigin !== undefined ? performance.timeOrigin : performance.timing.navigationStart - return performance.timing[name] - timeOrigin -} - function emulatePerformanceNavigationTiming( + session: RumSession, performanceObservable: Observable, performance: Performance ) { @@ -71,12 +63,10 @@ function emulatePerformanceNavigationTiming( } for (const timingName of performanceNavigationTimingNames) { - event[timingName] = getRelativePerformanceTiming(performance, timingName) + event[timingName] = getRelativePerformanceTiming(performance, performance.timing[timingName]) } - event.duration = event.loadEventEnd - - performanceObservable.notify(event as PerformanceNavigationTiming) + handlePerformanceEntries(session, performanceObservable, [event as PerformanceNavigationTiming]) } if (document.readyState === 'complete') { @@ -95,17 +85,22 @@ function emulatePerformanceNavigationTiming( function handlePerformanceEntries( session: RumSession, performanceObservable: Observable, - entries: Performance | PerformanceObserverEntryList + entries: PerformanceEntry[] ) { + function notify(entry: PerformanceEntry) { + performanceObservable.notify(entry) + } + if (session.isTrackedWithResource()) { - entries.getEntriesByType('resource').forEach((entry) => performanceObservable.notify(entry)) + entries.filter((entry) => entry.entryType === 'resource').forEach(notify) } + entries - .getEntriesByType('navigation') - .forEach((entry) => (entry as PerformanceNavigationTiming).loadEventEnd > 0 && performanceObservable.notify(entry)) - entries.getEntriesByType('paint').forEach((entry) => performanceObservable.notify(entry)) + .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) - if (entries !== window.performance) { - entries.getEntriesByType('longtask').forEach((entry) => performanceObservable.notify(entry)) - } + entries.filter((entry) => entry.entryType === 'paint').forEach(notify) + entries.filter((entry) => entry.entryType === 'longtask').forEach(notify) } diff --git a/packages/rum/src/rum.ts b/packages/rum/src/rum.ts index fdb20b095c..c0e8bb3232 100644 --- a/packages/rum/src/rum.ts +++ b/packages/rum/src/rum.ts @@ -9,10 +9,10 @@ import { Batch, HttpRequest } from '@browser-agent/core/src/transport' import { Context, ContextValue, includes, msToNs, ResourceKind, withSnakeCaseKeys } from '@browser-agent/core/src/utils' import { matchRequestTiming } from './matchRequestTiming' import { pageViewId, PageViewPerformance, PageViewSummary, trackPageView } from './pageViewTracker' +import { startPerformanceCollection } from './performanceCollection' import { computePerformanceResourceDetails, computeResourceKind, computeSize, isValidResource } from './resourceUtils' import { RumGlobal } from './rum.entry' import { RumSession } from './rumSession' -import { startPerformanceCollection } from './startPerformanceCollection' export interface PerformancePaintTiming extends PerformanceEntry { entryType: 'paint'