diff --git a/packages/rum-core/src/common/patching/index.js b/packages/rum-core/src/common/patching/index.js index bc33e6b34..59ca773db 100644 --- a/packages/rum-core/src/common/patching/index.js +++ b/packages/rum-core/src/common/patching/index.js @@ -28,12 +28,13 @@ import { patchFetch } from './fetch-patch' import { patchHistory } from './history-patch' import { patchEventTarget } from './event-target-patch' import EventHandler from '../event-handler' - +import { now } from '../utils' import { HISTORY, FETCH, XMLHTTPREQUEST, EVENT_TARGET } from '../constants' const patchEventHandler = new EventHandler() - +let patchedTime = null let alreadyPatched = false + function patchAll() { if (!alreadyPatched) { alreadyPatched = true @@ -50,8 +51,9 @@ function patchAll() { patchEventTarget(function(event, task) { patchEventHandler.send(EVENT_TARGET, [event, task]) }) + patchedTime = now() } return patchEventHandler } -export { patchAll, patchEventHandler } +export { patchAll, patchEventHandler, patchedTime } diff --git a/packages/rum-core/src/performance-monitoring/capture-navigation.js b/packages/rum-core/src/performance-monitoring/capture-navigation.js index 103e961b9..cf908439e 100644 --- a/packages/rum-core/src/performance-monitoring/capture-navigation.js +++ b/packages/rum-core/src/performance-monitoring/capture-navigation.js @@ -37,6 +37,7 @@ import { PERF, isPerfTimelineSupported } from '../common/utils' +import { patchedTime } from '../common/patching' /** * Navigation Timing Spans @@ -119,36 +120,13 @@ function createResourceTimingSpan(resourceTimingEntry) { return span } -function isBetween(spanStart, resourceStart) { - /** - * Delta used for comparing the timings between patched - * API span timing and ResourceTiming data - */ - const deltaTimeInMs = 2 - - return ( - spanStart >= resourceStart - deltaTimeInMs && - spanStart <= resourceStart + deltaTimeInMs - ) -} - /** * Checks if the span is already captured via XHR/Fetch patch by - * comparing the given resource URL aganist the list of span urls - * including the query parameters + * comparing the given resource startTime(fetchStart) aganist the + * patched time. */ -function isAlreadyCaptured(apiSpans, entry) { - let captured = false - - for (let i = 0; i < apiSpans.length; i++) { - const { url, start } = apiSpans[i] - - if (url === entry.name && isBetween(start, entry.startTime)) { - captured = true - break - } - } - return captured +function isCapturedByPatching(resourceStartTime, requestPatchTime) { + return requestPatchTime != null && resourceStartTime < requestPatchTime } /** @@ -159,7 +137,7 @@ function isIntakeAPIEndpoint(url) { return /intake\/v\d+\/rum\/events/.test(url) } -function createResourceTimingSpans(entries, apiSpans, trStart, trEnd) { +function createResourceTimingSpans(entries, requestPatchTime, trStart, trEnd) { const spans = [] for (let i = 0; i < entries.length; i++) { const { initiatorType, name, startTime, responseEnd } = entries[i] @@ -186,7 +164,8 @@ function createResourceTimingSpans(entries, apiSpans, trStart, trEnd) { if ( (initiatorType === RESOURCE_INITIATOR_TYPES[0] || initiatorType === RESOURCE_INITIATOR_TYPES[1]) && - (isIntakeAPIEndpoint(name) || isAlreadyCaptured(apiSpans, entries[i])) + (isIntakeAPIEndpoint(name) || + isCapturedByPatching(startTime, requestPatchTime)) ) { continue } @@ -220,24 +199,6 @@ function createUserTimingSpans(entries, trStart, trEnd) { return userTimingSpans } -function getAPISpans({ spans }) { - const apiSpans = [] - - for (let i = 0; i < spans.length; i++) { - const span = spans[i] - if (span.type === 'external' && span.subtype === 'http') { - const context = span.context - if (context) { - apiSpans.push({ - url: context.http.url, - start: span._start - }) - } - } - } - return apiSpans -} - /** * Navigation timing marks are reported only for page-load transactions * @@ -328,6 +289,7 @@ function captureNavigation(transaction) { return } + let requestPatchTime = null /** * Both start and end threshold decides if a span must be * captured as part of the transaction @@ -338,7 +300,6 @@ function captureNavigation(transaction) { * for few extra spans than soft navigations which * happens on single page applications */ - if (transaction.type === PAGE_LOAD) { /** * Adjust custom marks properly to fit in the transaction timeframe @@ -349,6 +310,12 @@ function captureNavigation(transaction) { customMarks[key] += transaction._start }) } + /** + * Patching logic would have kicked in after the page-load, + * so our filtering logic is applied only for page load transactions + * as an optimisation + */ + requestPatchTime = patchedTime /** * must be zero otherwise the calculated relative _start time would be wrong */ @@ -382,11 +349,9 @@ function captureNavigation(transaction) { * Capture resource timing information as spans */ const resourceEntries = PERF.getEntriesByType(RESOURCE) - const apiSpans = getAPISpans(transaction) - createResourceTimingSpans( resourceEntries, - apiSpans, + requestPatchTime, trStart, trEnd ).forEach(span => transaction.spans.push(span)) diff --git a/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js b/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js index dff383c71..6360055fb 100644 --- a/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js +++ b/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js @@ -154,7 +154,7 @@ describe('Capture hard navigation', function() { it('should createResourceTimingSpans', function() { const spans = createResourceTimingSpans( resourceEntries, - ['http://ajax-filter.test'], + null, transactionStart, transactionEnd ) @@ -194,14 +194,14 @@ describe('Capture hard navigation', function() { ] const spans = createResourceTimingSpans( entries, - [], + 150, transactionStart, transactionEnd ) expect(spans).toEqual([]) }) - it('should add/filter XHR/Fetch spans from RT data', function() { + it('should add/filter XHR/Fetch spans from RT data based on patch time', function() { const entries = [ { name: 'http://localhost:8000/fetch', @@ -218,51 +218,22 @@ describe('Capture hard navigation', function() { responseEnd: 150 } ] - const getCount = (apiCalls = []) => + const getCount = requestPatchTime => createResourceTimingSpans( entries, - apiCalls, + requestPatchTime, transactionStart, transactionEnd ).length expect(getCount()).toBe(2) - // different url same time - expect( - getCount([ - { - url: 'http://localhost:8000/data', - start: 100 - } - ]) - ).toBe(2) - // same url with different query params - expect( - getCount([ - { - url: 'http://localhost:8000/data?query=bar', - start: 99 - } - ]) - ).toBe(2) - // same url different time - expect( - getCount([ - { - url: 'http://localhost:8000/fetch', - start: 10 - } - ]) - ).toBe(2) - // same url same time - expect( - getCount([ - { - url: 'http://localhost:8000/fetch', - start: 24 - } - ]) - ).toBe(1) + // same time as start of 1st resource + expect(getCount(25)).toBe(2) + // after first res start time + expect(getCount(30)).toBe(1) + expect(getCount(100)).toBe(1) + // after both resources + expect(getCount(101)).toBe(0) }) it('should createUserTimingSpans', function() {