From cac9bfc0bbd14ad52e998ae95c537591765929b9 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 21 May 2020 11:13:54 +0200 Subject: [PATCH 01/23] ci: bump ES version (7.7.0) (#794) --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0ab68aff1..df92a4927 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -107,7 +107,7 @@ pipeline { name 'STACK_VERSION' values ( '8.0.0-SNAPSHOT', - '7.6.0', + '7.7.0', '7.0.0' ) } From e546e35f78eece3373efbc002c5ec4964407566f Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Tue, 26 May 2020 11:20:17 +0200 Subject: [PATCH 02/23] fix(rum): allow setting labels before initializing (#792) * fix(rum): allow setting labels before initializing * chore: address review --- packages/rum-core/src/common/config-service.js | 5 ++--- .../rum-core/test/common/config-service.spec.js | 1 - .../transaction-service.spec.js | 2 -- packages/rum/test/specs/apm-base.spec.js | 15 +++++++++++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/rum-core/src/common/config-service.js b/packages/rum-core/src/common/config-service.js index f66f29aeb..22cfef492 100644 --- a/packages/rum-core/src/common/config-service.js +++ b/packages/rum-core/src/common/config-service.js @@ -64,8 +64,7 @@ function getDataAttributesFromNode(node) { class Config { constructor() { - this.config = {} - this.defaults = { + this.config = { serviceName: '', serviceVersion: '', environment: '', @@ -180,7 +179,7 @@ class Config { properties.serverUrl = properties.serverUrl.replace(/\/+$/, '') } - this.config = merge({}, this.defaults, this.config, properties) + merge(this.config, properties) this.events.send(CONFIG_CHANGE, [this.config]) } diff --git a/packages/rum-core/test/common/config-service.spec.js b/packages/rum-core/test/common/config-service.spec.js index 591dce43f..6299cc454 100644 --- a/packages/rum-core/test/common/config-service.spec.js +++ b/packages/rum-core/test/common/config-service.spec.js @@ -30,7 +30,6 @@ describe('ConfigService', function() { var configService beforeEach(function() { configService = new ConfigService() - configService.init() }) it('should merge configs with already set configs', function() { expect(configService.get('instrument')).toBe(true) diff --git a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js index 3edf1edac..5c6cf0c86 100644 --- a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js +++ b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js @@ -57,7 +57,6 @@ describe('TransactionService', function() { spyOn(logger, 'debug') config = new Config() - config.init() transactionService = new TransactionService(logger, config) }) @@ -662,7 +661,6 @@ describe('TransactionService', function() { describe('performance entry recorder', () => { const logger = new LoggingService() const config = new Config() - config.init() const trService = new TransactionService(logger, config) const startSpy = jasmine.createSpy() const stopSpy = jasmine.createSpy() diff --git a/packages/rum/test/specs/apm-base.spec.js b/packages/rum/test/specs/apm-base.spec.js index c5e915ef9..4b828b890 100644 --- a/packages/rum/test/specs/apm-base.spec.js +++ b/packages/rum/test/specs/apm-base.spec.js @@ -308,6 +308,21 @@ describe('ApmBase', function() { expect(tr.spans[0].name).toBe('GET /') }) + it('should allow setting labels before calling init', () => { + const labels = { + foo: '1', + bar: 2 + } + apmBase.addLabels(labels) + apmBase.init({ + serviceName, + serverUrl, + disableInstrumentations: [PAGE_LOAD] + }) + const configService = serviceFactory.getService('ConfigService') + expect(configService.get('context.tags')).toEqual(labels) + }) + it('should fetch central config', done => { const apmServer = serviceFactory.getService('ApmServer') const configService = serviceFactory.getService('ConfigService') From af7deb9fbf556dac4966175eaa71ac3a63854db4 Mon Sep 17 00:00:00 2001 From: Hamid Date: Tue, 26 May 2020 15:10:12 +0200 Subject: [PATCH 03/23] feat: add first input delay span (#787) * feat: add first input delay span * chore: refactor perf-entry-recorder * move bootstrap to the core * chore: delete require.cache * chore: remove FID polyfill * chore: address review --- packages/{rum => rum-core}/src/bootstrap.js | 11 +- packages/rum-core/src/common/constants.js | 4 +- packages/rum-core/src/common/metrics.js | 29 --- packages/rum-core/src/index.js | 4 +- .../{perf-entry-recorder.js => metrics.js} | 40 ++- .../transaction-service.js | 9 +- .../performance-monitoring/metrics.spec.js | 230 ++++++++++++++++++ .../perf-entry-recorder.spec.js | 202 --------------- .../transaction-service.spec.js | 11 +- packages/rum/src/index.js | 3 +- packages/rum/test/node/index-export.spec.js | 6 +- packages/rum/test/specs/apm-base.spec.js | 7 +- packages/rum/test/specs/index.spec.js | 8 +- 13 files changed, 306 insertions(+), 258 deletions(-) rename packages/{rum => rum-core}/src/bootstrap.js (85%) delete mode 100644 packages/rum-core/src/common/metrics.js rename packages/rum-core/src/performance-monitoring/{perf-entry-recorder.js => metrics.js} (91%) create mode 100644 packages/rum-core/test/performance-monitoring/metrics.spec.js delete mode 100644 packages/rum-core/test/performance-monitoring/perf-entry-recorder.spec.js diff --git a/packages/rum/src/bootstrap.js b/packages/rum-core/src/bootstrap.js similarity index 85% rename from packages/rum/src/bootstrap.js rename to packages/rum-core/src/bootstrap.js index 4f2b45fd7..42dc19839 100644 --- a/packages/rum/src/bootstrap.js +++ b/packages/rum-core/src/bootstrap.js @@ -23,12 +23,14 @@ * */ -import { isPlatformSupported, patchAll } from '@elastic/apm-rum-core' +import { isPlatformSupported } from './common/utils' +import { patchAll } from './common/patching' +import { bootstrapMetrics } from './performance-monitoring/metrics' -var alreadyBootstrap = false -var enabled = false +let alreadyBootstrap = false +let enabled = false -export default function bootstrap() { +export function bootstrap() { if (alreadyBootstrap) { return enabled } @@ -36,6 +38,7 @@ export default function bootstrap() { if (isPlatformSupported()) { patchAll() + bootstrapMetrics() enabled = true } else if (typeof window !== 'undefined') { /** diff --git a/packages/rum-core/src/common/constants.js b/packages/rum-core/src/common/constants.js index b53df7158..e9fd6ef73 100644 --- a/packages/rum-core/src/common/constants.js +++ b/packages/rum-core/src/common/constants.js @@ -127,6 +127,7 @@ const NAVIGATION = 'navigation' const RESOURCE = 'resource' const FIRST_CONTENTFUL_PAINT = 'first-contentful-paint' const LARGEST_CONTENTFUL_PAINT = 'largest-contentful-paint' +const FIRST_INPUT = 'first-input' /** * Managed transaction configs @@ -199,5 +200,6 @@ export { CONFIG_SERVICE, LOGGING_SERVICE, APM_SERVER, - TRUNCATED_TYPE + TRUNCATED_TYPE, + FIRST_INPUT } diff --git a/packages/rum-core/src/common/metrics.js b/packages/rum-core/src/common/metrics.js deleted file mode 100644 index f25655833..000000000 --- a/packages/rum-core/src/common/metrics.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017-present, Elasticsearch BV - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -export const metrics = { - fcp: 0, - lcp: 0 -} diff --git a/packages/rum-core/src/index.js b/packages/rum-core/src/index.js index 34936f409..3c3d6e2ed 100644 --- a/packages/rum-core/src/index.js +++ b/packages/rum-core/src/index.js @@ -44,6 +44,7 @@ import { import { getInstrumentationFlags } from './common/instrument' import afterFrame from './common/after-frame' import { createTracer } from './opentracing' +import { bootstrap } from './bootstrap' function createServiceFactory() { registerPerfServices() @@ -67,5 +68,6 @@ export { PAGE_LOAD, CONFIG_SERVICE, LOGGING_SERVICE, - APM_SERVER + APM_SERVER, + bootstrap } diff --git a/packages/rum-core/src/performance-monitoring/perf-entry-recorder.js b/packages/rum-core/src/performance-monitoring/metrics.js similarity index 91% rename from packages/rum-core/src/performance-monitoring/perf-entry-recorder.js rename to packages/rum-core/src/performance-monitoring/metrics.js index 589229ec9..a0e78e6b8 100644 --- a/packages/rum-core/src/performance-monitoring/perf-entry-recorder.js +++ b/packages/rum-core/src/performance-monitoring/metrics.js @@ -26,12 +26,16 @@ import { LONG_TASK, LARGEST_CONTENTFUL_PAINT, - FIRST_CONTENTFUL_PAINT + FIRST_CONTENTFUL_PAINT, + FIRST_INPUT } from '../common/constants' import { noop, PERF } from '../common/utils' -import { metrics } from '../common/metrics' import Span from './span' +export const metrics = { + fcp: 0 +} + /** * Create Spans for the long task entries * Spec - https://w3c.github.io/longtasks/ @@ -93,6 +97,18 @@ function createLongTaskSpans(longtasks) { return spans } +export function createFirstInputDelaySpan(fidEntries) { + let firstInput = fidEntries[0] + + if (firstInput && !metrics.wasHidden) { + const { startTime, processingStart } = firstInput + + const span = new Span('First Input Delay', FIRST_INPUT, { startTime }) + span.end(processingStart) + return span + } +} + /** * Calculate Total Blocking Time (TBT) from long tasks */ @@ -215,6 +231,12 @@ export function captureObserverEntries(list, { capturePaint }) { result.spans.push(tbtSpan) } + const fidEntries = list.getEntriesByType(FIRST_INPUT) + const fidSpan = createFirstInputDelaySpan(fidEntries) + if (fidSpan) { + result.spans.push(fidSpan) + } + return result } @@ -269,3 +291,17 @@ export class PerfEntryRecorder { this.po.disconnect() } } + +export function bootstrapMetrics() { + metrics.wasHidden = document.visibilityState === 'hidden' + + window.addEventListener( + 'visibilitychange', + () => { + if (document.visibilityState === 'hidden') { + metrics.wasHidden = true + } + }, + { capture: true, once: true } + ) +} diff --git a/packages/rum-core/src/performance-monitoring/transaction-service.js b/packages/rum-core/src/performance-monitoring/transaction-service.js index d3fca665d..f1f208e74 100644 --- a/packages/rum-core/src/performance-monitoring/transaction-service.js +++ b/packages/rum-core/src/performance-monitoring/transaction-service.js @@ -25,10 +25,7 @@ import { Promise } from '../common/polyfills' import Transaction from './transaction' -import { - PerfEntryRecorder, - captureObserverEntries -} from './perf-entry-recorder' +import { PerfEntryRecorder, captureObserverEntries } from './metrics' import { extend, getEarliestSpan, getLatestNonXHRSpan } from '../common/utils' import { captureNavigation } from './capture-navigation' import { @@ -42,7 +39,8 @@ import { LARGEST_CONTENTFUL_PAINT, LONG_TASK, PAINT, - TRUNCATED_TYPE + TRUNCATED_TYPE, + FIRST_INPUT } from '../common/constants' import { addTransactionContext } from '../common/context' import { __DEV__ } from '../env' @@ -179,6 +177,7 @@ class TransactionService { if (!isRedefined) { this.recorder.start(LARGEST_CONTENTFUL_PAINT) this.recorder.start(PAINT) + this.recorder.start(FIRST_INPUT) } checkBrowserResponsiveness = false if (perfOptions.pageLoadTraceId) { diff --git a/packages/rum-core/test/performance-monitoring/metrics.spec.js b/packages/rum-core/test/performance-monitoring/metrics.spec.js new file mode 100644 index 000000000..9e26edc59 --- /dev/null +++ b/packages/rum-core/test/performance-monitoring/metrics.spec.js @@ -0,0 +1,230 @@ +/** + * MIT License + * + * Copyright (c) 2017-present, Elasticsearch BV + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +import { + captureObserverEntries, + calculateTotalBlockingTime, + createFirstInputDelaySpan, + PerfEntryRecorder, + metrics +} from '../../src/performance-monitoring/metrics' +import { LARGEST_CONTENTFUL_PAINT, LONG_TASK } from '../../src/common/constants' +import { + mockObserverEntryTypes, + mockObserverEntryNames +} from '../utils/globals-mock' +import longtaskEntries from '../fixtures/longtask-entries' + +describe('Metrics', () => { + describe('PerfEntryRecorder', () => { + const list = { + getEntriesByType: jasmine.createSpy(), + getEntriesByName: jasmine.createSpy() + } + + beforeEach(() => { + list.getEntriesByType.and.returnValue([]) + list.getEntriesByName.and.returnValue([]) + }) + + it('should not create long tasks spans if entries are not present', () => { + const { spans } = captureObserverEntries(list, { + capturePaint: false + }) + expect(spans).toEqual([]) + }) + + it('should not mark LCP & FCP if entries are not preset ', () => { + const { marks } = captureObserverEntries(list, { + capturePaint: true + }) + expect(marks).toEqual({}) + }) + + it('should return largest contentful paint if capturePaint is true', () => { + list.getEntriesByType.and.callFake(mockObserverEntryTypes) + const { marks: paintFalse } = captureObserverEntries(list, { + capturePaint: false + }) + expect(paintFalse).toEqual({}) + + const { marks: paintTrue } = captureObserverEntries(list, { + capturePaint: true + }) + + expect(paintTrue).toEqual({ + largestContentfulPaint: 1040 + }) + }) + + it('should set firstContentfulPaint if capturePaint is true ', () => { + list.getEntriesByName.and.callFake(mockObserverEntryNames) + const { marks: paintFalse } = captureObserverEntries(list, { + capturePaint: false + }) + expect(paintFalse).toEqual({}) + + const { marks: paintTrue } = captureObserverEntries(list, { + capturePaint: true + }) + expect(paintTrue).toEqual({ + firstContentfulPaint: jasmine.any(Number) + }) + }) + + it('should create long tasks attribution data in span context', () => { + list.getEntriesByType.and.callFake(mockObserverEntryTypes) + const { spans } = captureObserverEntries(list, { + capturePaint: false + }) + expect(spans.length).toBe(3) + expect(spans).toEqual([ + jasmine.objectContaining({ + name: 'Longtask(self)', + context: { + custom: { + attribution: 'unknown', + type: 'window' + } + } + }), + jasmine.objectContaining({ + name: 'Longtask(same-origin-descendant)', + context: { + custom: { + attribution: 'unknown', + type: 'iframe', + name: 'childA' + } + } + }), + jasmine.objectContaining({ + name: 'Longtask(same-origin-ancestor)', + context: { + custom: { + attribution: 'unknown', + type: 'window' + } + } + }) + ]) + }) + + it('should pass buffered flag based on observed type', () => { + const recorder = new PerfEntryRecorder(() => {}) + const onStartSpy = jasmine.createSpy() + recorder.po = { + observe: onStartSpy + } + recorder.start(LONG_TASK) + + expect(onStartSpy).toHaveBeenCalledWith({ + type: LONG_TASK, + buffered: false + }) + onStartSpy.calls.reset() + + recorder.start(LARGEST_CONTENTFUL_PAINT) + expect(onStartSpy).toHaveBeenCalledWith({ + type: LARGEST_CONTENTFUL_PAINT, + buffered: true + }) + }) + + describe('Total Blocking Time', () => { + it('should create total blocking time as span', () => { + list.getEntriesByType.and.callFake(mockObserverEntryTypes) + const { spans } = captureObserverEntries(list, { + capturePaint: true + }) + const tbtSpans = spans.filter( + span => span.name === 'Total Blocking Time' + ) + expect(tbtSpans[0]).toEqual( + jasmine.objectContaining({ + name: 'Total Blocking Time', + type: LONG_TASK + }) + ) + }) + + it('should calculate total blocking time from long tasks', () => { + const tbt = calculateTotalBlockingTime(longtaskEntries) + expect(tbt).toEqual({ + start: 745.4100000031758, + duration: 249.92999995592982 + }) + }) + + it('should calculate total blocking time based on FCP', () => { + metrics.fcp = 1000 + const tbt = calculateTotalBlockingTime(longtaskEntries) + expect(tbt).toEqual({ + start: 1023.40999995591, + duration: 245.35999997751787 + }) + }) + + it('should return tbt as 0 when entries are not self/same-origin', () => { + const tbt = calculateTotalBlockingTime([ + { + name: 'unknown', + startTime: 10 + }, + { + name: 'cross-origin', + startTime: 20 + } + ]) + expect(tbt).toEqual({ + start: Infinity, + duration: 0 + }) + }) + }) + + it('should create first input delay span', () => { + let span = createFirstInputDelaySpan([ + { + name: 'mousedown', + entryType: 'first-input', + startTime: 5482.669999997597, + duration: 16, + processingStart: 5489.029999997001, + processingEnd: 5489.0550000127405, + cancelable: true + } + ]) + expect(span).toEqual( + jasmine.objectContaining({ + name: 'First Input Delay', + type: 'first-input', + ended: true, + _end: 5489.029999997001, + _start: 5482.669999997597 + }) + ) + }) + }) +}) diff --git a/packages/rum-core/test/performance-monitoring/perf-entry-recorder.spec.js b/packages/rum-core/test/performance-monitoring/perf-entry-recorder.spec.js deleted file mode 100644 index 654e6c7c0..000000000 --- a/packages/rum-core/test/performance-monitoring/perf-entry-recorder.spec.js +++ /dev/null @@ -1,202 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017-present, Elasticsearch BV - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ -import { - captureObserverEntries, - PerfEntryRecorder, - calculateTotalBlockingTime -} from '../../src/performance-monitoring/perf-entry-recorder' -import { LARGEST_CONTENTFUL_PAINT, LONG_TASK } from '../../src/common/constants' -import { metrics } from '../../src/common/metrics' -import { - mockObserverEntryTypes, - mockObserverEntryNames -} from '../utils/globals-mock' -import longtaskEntries from '../fixtures/longtask-entries' - -describe('PerfEntryRecorder', () => { - const list = { - getEntriesByType: jasmine.createSpy(), - getEntriesByName: jasmine.createSpy() - } - - beforeEach(() => { - list.getEntriesByType.and.returnValue([]) - list.getEntriesByName.and.returnValue([]) - }) - - it('should not create long tasks spans if entries are not present', () => { - const { spans } = captureObserverEntries(list, { - capturePaint: false - }) - expect(spans).toEqual([]) - }) - - it('should not mark LCP & FCP if entries are not preset ', () => { - const { marks } = captureObserverEntries(list, { - capturePaint: true - }) - expect(marks).toEqual({}) - }) - - it('should return largest contentful paint if capturePaint is true', () => { - list.getEntriesByType.and.callFake(mockObserverEntryTypes) - const { marks: paintFalse } = captureObserverEntries(list, { - capturePaint: false - }) - expect(paintFalse).toEqual({}) - - const { marks: paintTrue } = captureObserverEntries(list, { - capturePaint: true - }) - - expect(paintTrue).toEqual({ - largestContentfulPaint: 1040 - }) - }) - - it('should set firstContentfulPaint if capturePaint is true ', () => { - list.getEntriesByName.and.callFake(mockObserverEntryNames) - const { marks: paintFalse } = captureObserverEntries(list, { - capturePaint: false - }) - expect(paintFalse).toEqual({}) - - const { marks: paintTrue } = captureObserverEntries(list, { - capturePaint: true - }) - expect(paintTrue).toEqual({ - firstContentfulPaint: jasmine.any(Number) - }) - }) - - it('should create long tasks attribution data in span context', () => { - list.getEntriesByType.and.callFake(mockObserverEntryTypes) - const { spans } = captureObserverEntries(list, { - capturePaint: false - }) - expect(spans.length).toBe(3) - expect(spans).toEqual([ - jasmine.objectContaining({ - name: 'Longtask(self)', - context: { - custom: { - attribution: 'unknown', - type: 'window' - } - } - }), - jasmine.objectContaining({ - name: 'Longtask(same-origin-descendant)', - context: { - custom: { - attribution: 'unknown', - type: 'iframe', - name: 'childA' - } - } - }), - jasmine.objectContaining({ - name: 'Longtask(same-origin-ancestor)', - context: { - custom: { - attribution: 'unknown', - type: 'window' - } - } - }) - ]) - }) - - it('should pass buffered flag based on observed type', () => { - const recorder = new PerfEntryRecorder(() => {}) - const onStartSpy = jasmine.createSpy() - recorder.po = { - observe: onStartSpy - } - recorder.start(LONG_TASK) - - expect(onStartSpy).toHaveBeenCalledWith({ - type: LONG_TASK, - buffered: false - }) - onStartSpy.calls.reset() - - recorder.start(LARGEST_CONTENTFUL_PAINT) - expect(onStartSpy).toHaveBeenCalledWith({ - type: LARGEST_CONTENTFUL_PAINT, - buffered: true - }) - }) - - describe('Total Blocking Time', () => { - it('should create total blocking time as span', () => { - list.getEntriesByType.and.callFake(mockObserverEntryTypes) - const { spans } = captureObserverEntries(list, { - capturePaint: true - }) - const tbtSpans = spans.filter(span => span.name === 'Total Blocking Time') - expect(tbtSpans[0]).toEqual( - jasmine.objectContaining({ - name: 'Total Blocking Time', - type: LONG_TASK - }) - ) - }) - - it('should calculate total blocking time from long tasks', () => { - const tbt = calculateTotalBlockingTime(longtaskEntries) - expect(tbt).toEqual({ - start: 745.4100000031758, - duration: 249.92999995592982 - }) - }) - - it('should calculate total blocking time based on FCP', () => { - metrics.fcp = 1000 - const tbt = calculateTotalBlockingTime(longtaskEntries) - expect(tbt).toEqual({ - start: 1023.40999995591, - duration: 245.35999997751787 - }) - }) - - it('should return tbt as 0 when entries are not self/same-origin', () => { - const tbt = calculateTotalBlockingTime([ - { - name: 'unknown', - startTime: 10 - }, - { - name: 'cross-origin', - startTime: 20 - } - ]) - expect(tbt).toEqual({ - start: Infinity, - duration: 0 - }) - }) - }) -}) diff --git a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js index 5c6cf0c86..ef7803a70 100644 --- a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js +++ b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js @@ -36,7 +36,8 @@ import { LONG_TASK, LARGEST_CONTENTFUL_PAINT, PAINT, - TRUNCATED_TYPE + TRUNCATED_TYPE, + FIRST_INPUT } from '../../src/common/constants' describe('TransactionService', function() { @@ -680,10 +681,11 @@ describe('TransactionService', function() { const pageLoadTr = trService.startTransaction('test', PAGE_LOAD, { managed: true }) - expect(startSpy).toHaveBeenCalledTimes(3) + expect(startSpy).toHaveBeenCalledTimes(4) expect(startSpy.calls.allArgs()).toEqual([ [LARGEST_CONTENTFUL_PAINT], [PAINT], + [FIRST_INPUT], [LONG_TASK] ]) await pageLoadTr.end() @@ -739,10 +741,11 @@ describe('TransactionService', function() { const pageLoadTr = trService.startTransaction('test', PAGE_LOAD, { managed: true }) - expect(startSpy).toHaveBeenCalledTimes(2) + expect(startSpy).toHaveBeenCalledTimes(3) expect(startSpy.calls.allArgs()).toEqual([ [LARGEST_CONTENTFUL_PAINT], - [PAINT] + [PAINT], + [FIRST_INPUT] ]) await pageLoadTr.end() expect(stopSpy).toHaveBeenCalled() diff --git a/packages/rum/src/index.js b/packages/rum/src/index.js index 8cc06cc69..4c03d193f 100644 --- a/packages/rum/src/index.js +++ b/packages/rum/src/index.js @@ -23,8 +23,7 @@ * */ -import bootstrap from './bootstrap' -import { createServiceFactory } from '@elastic/apm-rum-core' +import { createServiceFactory, bootstrap } from '@elastic/apm-rum-core' import ApmBase from './apm-base' const enabled = bootstrap() diff --git a/packages/rum/test/node/index-export.spec.js b/packages/rum/test/node/index-export.spec.js index 2a0831eb7..876bfe464 100644 --- a/packages/rum/test/node/index-export.spec.js +++ b/packages/rum/test/node/index-export.spec.js @@ -41,8 +41,10 @@ describe('apm base', () => { /** * Delete module cache and run bootstrap again */ - delete require.cache[require.resolve('@elastic/apm-rum')] - delete require.cache[require.resolve('@elastic/apm-rum/dist/lib/bootstrap')] + let cache = require.cache + for (let moduleId in cache) { + delete cache[moduleId] + } require('@elastic/apm-rum') expect(console.log).not.toHaveBeenCalled() diff --git a/packages/rum/test/specs/apm-base.spec.js b/packages/rum/test/specs/apm-base.spec.js index 4b828b890..cc69e7443 100644 --- a/packages/rum/test/specs/apm-base.spec.js +++ b/packages/rum/test/specs/apm-base.spec.js @@ -24,9 +24,12 @@ */ import ApmBase from '../../src/apm-base' -import { createServiceFactory, PAGE_LOAD } from '@elastic/apm-rum-core' +import { + createServiceFactory, + bootstrap, + PAGE_LOAD +} from '@elastic/apm-rum-core' import { TRANSACTION_END } from '@elastic/apm-rum-core/src/common/constants' -import bootstrap from '../../src/bootstrap' import { getGlobalConfig } from '../../../../dev-utils/test-config' import Promise from 'promise-polyfill' import { scheduleTaskCycles } from '../../../rum-core/test' diff --git a/packages/rum/test/specs/index.spec.js b/packages/rum/test/specs/index.spec.js index 07947f58e..41829d8d4 100644 --- a/packages/rum/test/specs/index.spec.js +++ b/packages/rum/test/specs/index.spec.js @@ -36,6 +36,10 @@ describe('index', function() { beforeEach(function() { originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000 + let cache = require.cache + for (let moduleId in cache) { + delete cache[moduleId] + } }) afterEach(function() { @@ -49,8 +53,6 @@ describe('index', function() { spyOn(console, 'log') - delete require.cache[require.resolve('../../src/')] - delete require.cache[require.resolve('../../src/bootstrap')] require('../../src/') expect(console.log).toHaveBeenCalledWith( @@ -129,10 +131,8 @@ describe('index', function() { } /** - * Delete bootstrap and init module cache and * execute module again to check if global promise is overriden */ - delete require.cache[require.resolve('../../src/')] require('../../src/') /** From d585324d56494684ee58005ab43e075e8267da8a Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Thu, 28 May 2020 10:17:46 +0200 Subject: [PATCH 04/23] fix(rum): use single instance of apm across all packages (#796) * fix(rum): use single instance of apm across all packages * chore: address review and add test --- packages/rum-core/src/bootstrap.js | 11 ++---- packages/rum-core/src/common/polyfills.js | 3 +- packages/rum-core/src/common/url.js | 4 ++- packages/rum-core/src/common/utils.js | 11 +++--- packages/rum-core/src/index.js | 6 ++-- packages/rum-core/test/bootstrap.spec.js | 42 +++++++++++++++++++++++ packages/rum/src/apm-base.js | 4 +-- packages/rum/src/index.js | 29 ++++++++++++---- packages/rum/test/specs/index.spec.js | 23 ++++--------- 9 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 packages/rum-core/test/bootstrap.spec.js diff --git a/packages/rum-core/src/bootstrap.js b/packages/rum-core/src/bootstrap.js index 42dc19839..1ab94e636 100644 --- a/packages/rum-core/src/bootstrap.js +++ b/packages/rum-core/src/bootstrap.js @@ -23,24 +23,17 @@ * */ -import { isPlatformSupported } from './common/utils' +import { isPlatformSupported, isBrowser } from './common/utils' import { patchAll } from './common/patching' import { bootstrapMetrics } from './performance-monitoring/metrics' -let alreadyBootstrap = false let enabled = false - export function bootstrap() { - if (alreadyBootstrap) { - return enabled - } - alreadyBootstrap = true - if (isPlatformSupported()) { patchAll() bootstrapMetrics() enabled = true - } else if (typeof window !== 'undefined') { + } else if (isBrowser) { /** * Print this error message only on the browser console * on unsupported browser versions diff --git a/packages/rum-core/src/common/polyfills.js b/packages/rum-core/src/common/polyfills.js index 5cad35ebb..7c5a01cb6 100644 --- a/packages/rum-core/src/common/polyfills.js +++ b/packages/rum-core/src/common/polyfills.js @@ -24,13 +24,14 @@ */ import PromisePollyfill from 'promise-polyfill' +import { isBrowser } from './utils' /** * Use the globally available promise if it exists and * fallback to using the polyfilled Promise */ let local = {} -if (typeof window !== 'undefined') { +if (isBrowser) { local = window } else if (typeof self !== 'undefined') { local = self diff --git a/packages/rum-core/src/common/url.js b/packages/rum-core/src/common/url.js index 677af54bd..efd5ce8b7 100644 --- a/packages/rum-core/src/common/url.js +++ b/packages/rum-core/src/common/url.js @@ -45,6 +45,8 @@ * */ +import { isBrowser } from './utils' + /** * Add default ports for other protocols(ws, wss) after * RUM agent starts instrumenting those @@ -197,7 +199,7 @@ class Url { getLocation() { var globalVar = {} - if (typeof window !== 'undefined') { + if (isBrowser) { globalVar = window } diff --git a/packages/rum-core/src/common/utils.js b/packages/rum-core/src/common/utils.js index 3d41384b0..ae68b00b3 100644 --- a/packages/rum-core/src/common/utils.js +++ b/packages/rum-core/src/common/utils.js @@ -26,10 +26,8 @@ import { Promise } from './polyfills' const slice = [].slice -const PERF = - typeof window !== 'undefined' && typeof performance !== 'undefined' - ? performance - : {} +const isBrowser = typeof window !== 'undefined' +const PERF = isBrowser && typeof performance !== 'undefined' ? performance : {} function isCORSSupported() { var xhr = new window.XMLHttpRequest() @@ -135,7 +133,7 @@ function checkSameOrigin(source, target) { function isPlatformSupported() { return ( - typeof window !== 'undefined' && + isBrowser && typeof Array.prototype.forEach === 'function' && typeof JSON.stringify === 'function' && typeof Function.bind === 'function' && @@ -390,5 +388,6 @@ export { find, removeInvalidChars, PERF, - isPerfTimelineSupported + isPerfTimelineSupported, + isBrowser } diff --git a/packages/rum-core/src/index.js b/packages/rum-core/src/index.js index 3c3d6e2ed..a2a1546de 100644 --- a/packages/rum-core/src/index.js +++ b/packages/rum-core/src/index.js @@ -31,7 +31,8 @@ import { ServiceFactory } from './common/service-factory' import { isPlatformSupported, scheduleMicroTask, - scheduleMacroTask + scheduleMacroTask, + isBrowser } from './common/utils' import { patchAll, patchEventHandler } from './common/patching' import { @@ -43,8 +44,8 @@ import { } from './common/constants' import { getInstrumentationFlags } from './common/instrument' import afterFrame from './common/after-frame' -import { createTracer } from './opentracing' import { bootstrap } from './bootstrap' +import { createTracer } from './opentracing' function createServiceFactory() { registerPerfServices() @@ -59,6 +60,7 @@ export { patchAll, patchEventHandler, isPlatformSupported, + isBrowser, getInstrumentationFlags, createTracer, scheduleMicroTask, diff --git a/packages/rum-core/test/bootstrap.spec.js b/packages/rum-core/test/bootstrap.spec.js new file mode 100644 index 000000000..8ad251ed1 --- /dev/null +++ b/packages/rum-core/test/bootstrap.spec.js @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2017-present, Elasticsearch BV + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +import { bootstrap } from '../src/bootstrap' + +describe('bootstrap', function() { + it('should log warning on unsupported environments', () => { + // Pass unsupported check + const nowFn = window.performance.now + window.performance.now = undefined + + spyOn(console, 'log') + bootstrap() + + expect(console.log).toHaveBeenCalledWith( + '[Elastic APM] platform is not supported!' + ) + window.performance.now = nowFn + }) +}) diff --git a/packages/rum/src/apm-base.js b/packages/rum/src/apm-base.js index 0f6b17fa8..afab0789e 100644 --- a/packages/rum/src/apm-base.js +++ b/packages/rum/src/apm-base.js @@ -32,7 +32,7 @@ import { APM_SERVER } from '@elastic/apm-rum-core' -class ApmBase { +export default class ApmBase { constructor(serviceFactory, disable) { this._disable = disable this.serviceFactory = serviceFactory @@ -277,5 +277,3 @@ class ApmBase { configService.addFilter(fn) } } - -export default ApmBase diff --git a/packages/rum/src/index.js b/packages/rum/src/index.js index 4c03d193f..2bf79f0c1 100644 --- a/packages/rum/src/index.js +++ b/packages/rum/src/index.js @@ -23,17 +23,34 @@ * */ -import { createServiceFactory, bootstrap } from '@elastic/apm-rum-core' +import { + createServiceFactory, + bootstrap, + isBrowser +} from '@elastic/apm-rum-core' import ApmBase from './apm-base' -const enabled = bootstrap() -const serviceFactory = createServiceFactory() -const apmBase = new ApmBase(serviceFactory, !enabled) +/** + * Use a single instance of ApmBase across all instance of the agent + * including the instanes used in framework specific integrations + */ +function getApmBase() { + if (isBrowser && window.elasticApm) { + return window.elasticApm + } + const enabled = bootstrap() + const serviceFactory = createServiceFactory() + const apmBase = new ApmBase(serviceFactory, !enabled) -if (typeof window !== 'undefined') { - window.elasticApm = apmBase + if (isBrowser) { + window.elasticApm = apmBase + } + + return apmBase } +const apmBase = getApmBase() + const init = apmBase.init.bind(apmBase) export default init diff --git a/packages/rum/test/specs/index.spec.js b/packages/rum/test/specs/index.spec.js index 41829d8d4..8f322551d 100644 --- a/packages/rum/test/specs/index.spec.js +++ b/packages/rum/test/specs/index.spec.js @@ -46,22 +46,6 @@ describe('index', function() { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout }) - it('should log only on browser environments', () => { - // Pass unsupported check - const nowFn = window.performance.now - window.performance.now = undefined - - spyOn(console, 'log') - - require('../../src/') - - expect(console.log).toHaveBeenCalledWith( - '[Elastic APM] platform is not supported!' - ) - - window.performance.now = nowFn - }) - it('should init ApmBase', function(done) { var apmServer = apmBase.serviceFactory.getService('ApmServer') if (globalConfig.useMocks) { @@ -120,6 +104,13 @@ describe('index', function() { } }) + it('should return same instance when loaded multiple times', () => { + require('../../src/') + expect(window.elasticApm).toEqual(apmBase) + const exportsObj = require('../../src/') + expect(exportsObj.apmBase).toEqual(window.elasticApm) + }) + it('should not throw error on global Promise patching', () => { window.count = 0 window.Promise = { From b1d247c3654978187e9491079208c18267161e98 Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Thu, 28 May 2020 10:26:51 +0200 Subject: [PATCH 05/23] fix(rum-core): consider user defined type of high precedence (#798) * fix(rum-core): consider user defined type of high precedence * chore: address review --- .../transaction-service.js | 29 +++++++++---------- .../src/performance-monitoring/transaction.js | 2 +- .../transaction-service.spec.js | 19 +++++++----- .../test/e2e/with-router/general.e2e-spec.js | 2 +- packages/rum/test/specs/apm-base.spec.js | 2 +- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/rum-core/src/performance-monitoring/transaction-service.js b/packages/rum-core/src/performance-monitoring/transaction-service.js index f1f208e74..c5718b69e 100644 --- a/packages/rum-core/src/performance-monitoring/transaction-service.js +++ b/packages/rum-core/src/performance-monitoring/transaction-service.js @@ -137,28 +137,27 @@ class TransactionService { * allow a redefinition until there's a call that doesn't have that * or the threshold is exceeded. */ + let redefineType = tr.type + const currentTypeOrder = TRANSACTION_TYPE_ORDER.indexOf(tr.type) + const redefineTypeOrder = TRANSACTION_TYPE_ORDER.indexOf(type) + + /** + * Update type based on precedence defined in TRANSACTION_TYPE_ORDER. + * 1. If both orders doesn't exist, we don't redefine the type. + * 2. If only the redefined type is not present in predefined order, that implies + * it's a user defined type and it is of higher precedence + */ + if (currentTypeOrder >= 0 && redefineTypeOrder < currentTypeOrder) { + redefineType = type + } if (__DEV__) { this._logger.debug( `redefining transaction(${tr.id}, ${tr.name}, ${tr.type})`, 'to', - `(${name}, ${type})`, + `(${name || tr.name}, ${redefineType})`, tr ) } - /** - * We only update based precedence defined in TRANSACTION_TYPE_ORDER. - * If either orders don't exist we also don't redefine the type. - */ - let redefineType - let currentTypeOrder = TRANSACTION_TYPE_ORDER.indexOf(tr.type) - let redefineTypeOrder = TRANSACTION_TYPE_ORDER.indexOf(type) - if ( - currentTypeOrder !== -1 && - redefineTypeOrder !== -1 && - redefineTypeOrder < currentTypeOrder - ) { - redefineType = type - } tr.redefine(name, redefineType, perfOptions) isRedefined = true } else { diff --git a/packages/rum-core/src/performance-monitoring/transaction.js b/packages/rum-core/src/performance-monitoring/transaction.js index e480f7cbd..76b471f83 100644 --- a/packages/rum-core/src/performance-monitoring/transaction.js +++ b/packages/rum-core/src/performance-monitoring/transaction.js @@ -84,7 +84,7 @@ class Transaction extends SpanBase { } if (options) { - this.options = extend(this.options, options) + extend(this.options, options) } } diff --git a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js index ef7803a70..c4c71914b 100644 --- a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js +++ b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js @@ -621,23 +621,23 @@ describe('TransactionService', function() { let tr = transactionService.getCurrentTransaction() expect(tr.type).toBe('temporary') - transactionService.startTransaction('test 1', 'random-type', { + transactionService.startTransaction('test 1', 'route-change', { managed: true, canReuse: true }) tr = transactionService.getCurrentTransaction() - expect(tr.type).toBe('temporary') + expect(tr.type).toBe('route-change') - transactionService.startTransaction('test 1', 'route-change', { + transactionService.startTransaction('test 1', 'page-load', { managed: true, canReuse: true }) tr = transactionService.getCurrentTransaction() - expect(tr.type).toBe('route-change') + expect(tr.type).toBe('page-load') - transactionService.startTransaction('test 1', 'page-load', { + transactionService.startTransaction('test 1', 'route-change', { managed: true, canReuse: true }) @@ -645,18 +645,21 @@ describe('TransactionService', function() { tr = transactionService.getCurrentTransaction() expect(tr.type).toBe('page-load') - transactionService.startTransaction('test 1', 'route-change', { + transactionService.startTransaction('test 1', 'custom-type', { managed: true, canReuse: true }) - transactionService.startTransaction('test 1', 'random-type', { + tr = transactionService.getCurrentTransaction() + expect(tr.type).toBe('custom-type') + + transactionService.startTransaction('test 1', 'page-load', { managed: true, canReuse: true }) tr = transactionService.getCurrentTransaction() - expect(tr.type).toBe('page-load') + expect(tr.type).toBe('custom-type') }) describe('performance entry recorder', () => { diff --git a/packages/rum-react/test/e2e/with-router/general.e2e-spec.js b/packages/rum-react/test/e2e/with-router/general.e2e-spec.js index 207122eec..347e78620 100644 --- a/packages/rum-react/test/e2e/with-router/general.e2e-spec.js +++ b/packages/rum-react/test/e2e/with-router/general.e2e-spec.js @@ -88,7 +88,7 @@ describe('General usecase with react-router', function() { const routeTransaction = transactions[1] expect(routeTransaction.name).toBe('ManualComponent') - expect(routeTransaction.type).toBe('route-change') + expect(routeTransaction.type).toBe('component') const spanTypes = ['app', 'resource', 'external'] const foundSpans = routeTransaction.spans.filter( diff --git a/packages/rum/test/specs/apm-base.spec.js b/packages/rum/test/specs/apm-base.spec.js index cc69e7443..c16a3e933 100644 --- a/packages/rum/test/specs/apm-base.spec.js +++ b/packages/rum/test/specs/apm-base.spec.js @@ -174,7 +174,7 @@ describe('ApmBase', function() { }) expect(tr).toBeDefined() expect(tr.name).toBe('test-transaction') - expect(tr.type).toBe('page-load') + expect(tr.type).toBe('test-type') spyOn(tr, 'startSpan').and.callThrough() apmBase.startSpan('test-span', 'test-type') From f5283afcdbd77d97d58890310dd9725e6ab7eb31 Mon Sep 17 00:00:00 2001 From: apmmachine Date: Thu, 28 May 2020 09:59:35 +0000 Subject: [PATCH 06/23] chore(release): publish - @elastic/apm-rum-angular@1.1.2 - @elastic/apm-rum-core@5.3.0 - @elastic/apm-rum-react@1.1.2 - @elastic/apm-rum-vue@1.1.2 - @elastic/apm-rum@5.2.0 --- packages/rum-angular/CHANGELOG.md | 11 +++++++++++ packages/rum-angular/package.json | 2 +- packages/rum-core/CHANGELOG.md | 27 +++++++++++++++++++++++++++ packages/rum-core/package.json | 2 +- packages/rum-react/CHANGELOG.md | 16 ++++++++++++++++ packages/rum-react/package.json | 2 +- packages/rum-vue/CHANGELOG.md | 11 +++++++++++ packages/rum-vue/package.json | 2 +- packages/rum/CHANGELOG.md | 23 +++++++++++++++++++++++ packages/rum/package.json | 2 +- packages/rum/src/apm-base.js | 2 +- 11 files changed, 94 insertions(+), 6 deletions(-) diff --git a/packages/rum-angular/CHANGELOG.md b/packages/rum-angular/CHANGELOG.md index 7e015eed3..ab2cbe110 100644 --- a/packages/rum-angular/CHANGELOG.md +++ b/packages/rum-angular/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-angular@1.1.1...@elastic/apm-rum-angular@1.1.2) (2020-05-28) + + +### Performance Improvements + +* refactor ServiceFactory to use constant service names ([#764](https://github.com/elastic/apm-agent-rum-js/issues/764)) ([fdda235](https://github.com/elastic/apm-agent-rum-js/commit/fdda23555b418166727d85f143e84a16079d83e6)), closes [#238](https://github.com/elastic/apm-agent-rum-js/issues/238) + + + + + ## [1.1.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-angular@1.1.0...@elastic/apm-rum-angular@1.1.1) (2020-04-15) **Note:** Version bump only for package @elastic/apm-rum-angular diff --git a/packages/rum-angular/package.json b/packages/rum-angular/package.json index e9735a210..f285b2c67 100644 --- a/packages/rum-angular/package.json +++ b/packages/rum-angular/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-angular", - "version": "1.1.1", + "version": "1.1.2", "description": "Elastic APM Real User Monitoring for Angular applications", "homepage": "https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html", "license": "MIT", diff --git a/packages/rum-core/CHANGELOG.md b/packages/rum-core/CHANGELOG.md index d074e4476..c464adec7 100644 --- a/packages/rum-core/CHANGELOG.md +++ b/packages/rum-core/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.3.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-core@5.2.0...@elastic/apm-rum-core@5.3.0) (2020-05-28) + + +### Bug Fixes + +* **rum:** allow setting labels before initializing ([#792](https://github.com/elastic/apm-agent-rum-js/issues/792)) ([e546e35](https://github.com/elastic/apm-agent-rum-js/commit/e546e35f78eece3373efbc002c5ec4964407566f)) +* **rum:** use single instance of apm across all packages ([#796](https://github.com/elastic/apm-agent-rum-js/issues/796)) ([d585324](https://github.com/elastic/apm-agent-rum-js/commit/d585324d56494684ee58005ab43e075e8267da8a)) +* **rum-core:** add subtype span info without camelcasing ([#753](https://github.com/elastic/apm-agent-rum-js/issues/753)) ([8b97075](https://github.com/elastic/apm-agent-rum-js/commit/8b9707561f20ceb44caeb8ab6f6cf6cfe97e4f40)) +* **rum-core:** consider user defined type of high precedence ([#798](https://github.com/elastic/apm-agent-rum-js/issues/798)) ([b1d247c](https://github.com/elastic/apm-agent-rum-js/commit/b1d247c3654978187e9491079208c18267161e98)) +* **rum-core:** treat truncated spans as actual in breakdown ([#777](https://github.com/elastic/apm-agent-rum-js/issues/777)) ([93d8fc2](https://github.com/elastic/apm-agent-rum-js/commit/93d8fc2ff1b506aeeb7e4f6b7268f065922c21cc)) + + +### Features + +* add first input delay span ([#787](https://github.com/elastic/apm-agent-rum-js/issues/787)) ([af7deb9](https://github.com/elastic/apm-agent-rum-js/commit/af7deb9fbf556dac4966175eaa71ac3a63854db4)) +* **rum-core:** capture total blocking time for navigation ([#788](https://github.com/elastic/apm-agent-rum-js/issues/788)) ([a48980b](https://github.com/elastic/apm-agent-rum-js/commit/a48980b5819d92854923f9ebe5b89f2b28b25b61)) + + +### Performance Improvements + +* **rum-core:** compress server payload for all events ([#771](https://github.com/elastic/apm-agent-rum-js/issues/771)) ([5a3c604](https://github.com/elastic/apm-agent-rum-js/commit/5a3c6048482b47a9ac6ef565e88ff3eff786cabf)) +* refactor ServiceFactory to use constant service names ([#764](https://github.com/elastic/apm-agent-rum-js/issues/764)) ([fdda235](https://github.com/elastic/apm-agent-rum-js/commit/fdda23555b418166727d85f143e84a16079d83e6)), closes [#238](https://github.com/elastic/apm-agent-rum-js/issues/238) + + + + + # [5.2.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-core@5.1.0...@elastic/apm-rum-core@5.2.0) (2020-04-15) diff --git a/packages/rum-core/package.json b/packages/rum-core/package.json index b9d402143..88d1ce2e6 100644 --- a/packages/rum-core/package.json +++ b/packages/rum-core/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-core", - "version": "5.2.0", + "version": "5.3.0", "description": "Elastic apm core", "license": "MIT", "main": "dist/lib/index.js", diff --git a/packages/rum-react/CHANGELOG.md b/packages/rum-react/CHANGELOG.md index 831d1ebc7..599e0581e 100644 --- a/packages/rum-react/CHANGELOG.md +++ b/packages/rum-react/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-react@1.1.1...@elastic/apm-rum-react@1.1.2) (2020-05-28) + + +### Bug Fixes + +* **rum-core:** consider user defined type of high precedence ([#798](https://github.com/elastic/apm-agent-rum-js/issues/798)) ([b1d247c](https://github.com/elastic/apm-agent-rum-js/commit/b1d247c3654978187e9491079208c18267161e98)) + + +### Performance Improvements + +* refactor ServiceFactory to use constant service names ([#764](https://github.com/elastic/apm-agent-rum-js/issues/764)) ([fdda235](https://github.com/elastic/apm-agent-rum-js/commit/fdda23555b418166727d85f143e84a16079d83e6)), closes [#238](https://github.com/elastic/apm-agent-rum-js/issues/238) + + + + + ## [1.1.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-react@1.1.0...@elastic/apm-rum-react@1.1.1) (2020-04-15) diff --git a/packages/rum-react/package.json b/packages/rum-react/package.json index 2f1ce11de..a8e51c37e 100644 --- a/packages/rum-react/package.json +++ b/packages/rum-react/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-react", - "version": "1.1.1", + "version": "1.1.2", "description": "Elastic APM Real User Monitoring for React applications", "author": "Hamid ", "homepage": "https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html", diff --git a/packages/rum-vue/CHANGELOG.md b/packages/rum-vue/CHANGELOG.md index 9e9567a0d..443d755b7 100644 --- a/packages/rum-vue/CHANGELOG.md +++ b/packages/rum-vue/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-vue@1.1.1...@elastic/apm-rum-vue@1.1.2) (2020-05-28) + + +### Performance Improvements + +* refactor ServiceFactory to use constant service names ([#764](https://github.com/elastic/apm-agent-rum-js/issues/764)) ([fdda235](https://github.com/elastic/apm-agent-rum-js/commit/fdda23555b418166727d85f143e84a16079d83e6)), closes [#238](https://github.com/elastic/apm-agent-rum-js/issues/238) + + + + + ## [1.1.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-vue@1.1.0...@elastic/apm-rum-vue@1.1.1) (2020-04-15) **Note:** Version bump only for package @elastic/apm-rum-vue diff --git a/packages/rum-vue/package.json b/packages/rum-vue/package.json index ea29b59e4..1faa291b8 100644 --- a/packages/rum-vue/package.json +++ b/packages/rum-vue/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-vue", - "version": "1.1.1", + "version": "1.1.2", "description": "Elastic APM Real User Monitoring for Vue applications", "keywords": [ "elastic", diff --git a/packages/rum/CHANGELOG.md b/packages/rum/CHANGELOG.md index e17b64c52..c28c41152 100644 --- a/packages/rum/CHANGELOG.md +++ b/packages/rum/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.2.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum@5.1.1...@elastic/apm-rum@5.2.0) (2020-05-28) + + +### Bug Fixes + +* **rum:** allow setting labels before initializing ([#792](https://github.com/elastic/apm-agent-rum-js/issues/792)) ([e546e35](https://github.com/elastic/apm-agent-rum-js/commit/e546e35f78eece3373efbc002c5ec4964407566f)) +* **rum:** use single instance of apm across all packages ([#796](https://github.com/elastic/apm-agent-rum-js/issues/796)) ([d585324](https://github.com/elastic/apm-agent-rum-js/commit/d585324d56494684ee58005ab43e075e8267da8a)) +* **rum-core:** consider user defined type of high precedence ([#798](https://github.com/elastic/apm-agent-rum-js/issues/798)) ([b1d247c](https://github.com/elastic/apm-agent-rum-js/commit/b1d247c3654978187e9491079208c18267161e98)) + + +### Features + +* add first input delay span ([#787](https://github.com/elastic/apm-agent-rum-js/issues/787)) ([af7deb9](https://github.com/elastic/apm-agent-rum-js/commit/af7deb9fbf556dac4966175eaa71ac3a63854db4)) + + +### Performance Improvements + +* refactor ServiceFactory to use constant service names ([#764](https://github.com/elastic/apm-agent-rum-js/issues/764)) ([fdda235](https://github.com/elastic/apm-agent-rum-js/commit/fdda23555b418166727d85f143e84a16079d83e6)), closes [#238](https://github.com/elastic/apm-agent-rum-js/issues/238) + + + + + ## [5.1.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum@5.1.0...@elastic/apm-rum@5.1.1) (2020-04-15) **Note:** Version bump only for package @elastic/apm-rum diff --git a/packages/rum/package.json b/packages/rum/package.json index 7d4a13999..e73ad3362 100644 --- a/packages/rum/package.json +++ b/packages/rum/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum", - "version": "5.1.1", + "version": "5.2.0", "description": "Elastic APM JavaScript agent", "main": "dist/lib/index.js", "module": "dist/es/index.js", diff --git a/packages/rum/src/apm-base.js b/packages/rum/src/apm-base.js index afab0789e..6d2c4689b 100644 --- a/packages/rum/src/apm-base.js +++ b/packages/rum/src/apm-base.js @@ -49,7 +49,7 @@ export default class ApmBase { /** * Set Agent version to be sent as part of metadata to the APM Server */ - configService.setVersion('5.1.1') + configService.setVersion('5.2.0') this.config(config) /** * Deactive agent when the active config flag is set to false From 7643cc00556f73d803ccd0135fa68473dc11623a Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Thu, 28 May 2020 13:09:35 +0200 Subject: [PATCH 07/23] docs: add release notes for 5.2.0 (#801) * docs: add release notes for 5.2.0 * chore: fix heading --- CHANGELOG.asciidoc | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index df0cf5940..c253176ec 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -29,6 +29,32 @@ See Conventional Commits (https://conventionalcommits.org) for commit guidelines [[release-notes-5.x]] === RUM JS Agent version 5.x +[[release-notes-5.2.0]] +==== 5.2.0 (2020-05-28) + +[float] +===== Features +* Agent now supports compressing events payload sent to the APM server + via new configuration <>. It yeilds a huge reduction of + around ~45% in the payload size for average sized web pages: {issue}768[#768] +* Capture First Input Delay(FID) as Span for page-load transaction: {issue}732[#732] +* Capture Total Blocking Time(TBT) as Span for page-load transaction: {issue}781[#781] + +[float] +===== Bug fixes +* Allow setting labels before agent is initialized: {issue}780[#780] +* Use single instance of apm across all packages: {issue}791[#791] +* User defined types for managed transactions are considered of + high precedence: {issue}758[#758] +* Add span subtype information in payload without camelcasing: {issue}753[#753] +* Treat truncated spans percentage as regular span in + breakdown calculation: {issue}776[#776] + +[float] +===== Performance Improvements +* Refactor ServiceFactory class to use constant service names: {issue}238[#238] + + [[release-notes-5.1.1]] ==== 5.1.1 (2020-04-15) @@ -42,7 +68,6 @@ See Conventional Commits (https://conventionalcommits.org) for commit guidelines `ApmRoute` inside child components: {issue}748[#748] - [[release-notes-5.1.0]] ==== 5.1.0 (2020-04-08) From 96fa2bba4b83a26c17a4f32ec38d3504fc455d09 Mon Sep 17 00:00:00 2001 From: Ivan Fernandez Calvo Date: Mon, 1 Jun 2020 11:46:48 +0200 Subject: [PATCH 08/23] fix: workarount for https://github.com/elastic/beats/issues/18858 (#807) --- dev-utils/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-utils/docker-compose.yml b/dev-utils/docker-compose.yml index 797934f65..ca2af3ade 100644 --- a/dev-utils/docker-compose.yml +++ b/dev-utils/docker-compose.yml @@ -37,6 +37,8 @@ services: image: docker.elastic.co/apm/apm-server:${STACK_VERSION} ports: - "127.0.0.1:${APM_SERVER_PORT:-8200}:8200" + environment: + BEAT_STRICT_PERMS: false command: > apm-server -e -E apm-server.rum.enabled=true From f6a8b341c2d4fb2016ac5b44f051f44abb5800f1 Mon Sep 17 00:00:00 2001 From: Ivan Fernandez Calvo Date: Mon, 1 Jun 2020 14:12:25 +0200 Subject: [PATCH 09/23] fix: env var invalid type (#809) --- dev-utils/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-utils/docker-compose.yml b/dev-utils/docker-compose.yml index ca2af3ade..e571f3439 100644 --- a/dev-utils/docker-compose.yml +++ b/dev-utils/docker-compose.yml @@ -38,7 +38,7 @@ services: ports: - "127.0.0.1:${APM_SERVER_PORT:-8200}:8200" environment: - BEAT_STRICT_PERMS: false + BEAT_STRICT_PERMS: "false" command: > apm-server -e -E apm-server.rum.enabled=true From 7ff7026b64ad8632d86b0e303f6c668a86c791d9 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 4 Jun 2020 08:13:04 +0100 Subject: [PATCH 10/23] ci: use dockerLogs step (#810) --- Jenkinsfile | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index df92a4927..d8d85acf6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -389,16 +389,22 @@ def runScript(Map args = [:]){ sleep randomNumber(min: 5, max: 10) sh(label: 'Pull and build docker infra', script: '.ci/scripts/pull_and_build.sh') } - // Another retry in case there are any environmental issues - retry(3) { - sleep randomNumber(min: 5, max: 10) - if(env.MODE == 'saucelabs'){ - withSaucelabsEnv(){ + try { + // Another retry in case there are any environmental issues + retry(3) { + sleep randomNumber(min: 5, max: 10) + if(env.MODE == 'saucelabs'){ + withSaucelabsEnv(){ + sh(label: "Start Elastic Stack ${stack} - ${scope} - ${env.MODE}", script: '.ci/scripts/test.sh') + } + } else { sh(label: "Start Elastic Stack ${stack} - ${scope} - ${env.MODE}", script: '.ci/scripts/test.sh') } - } else { - sh(label: "Start Elastic Stack ${stack} - ${scope} - ${env.MODE}", script: '.ci/scripts/test.sh') } + } catch(e) { + throw e + } finally { + dockerLogs(step: "${label}-${stack}", failNever: true) } } } From cc56d8580c8e57fb986adeb286ebef24f3918566 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Fri, 5 Jun 2020 07:45:07 +0100 Subject: [PATCH 11/23] ci: enable benchmark on a PR basis (#812) --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index d8d85acf6..2cc99a7ee 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,7 +33,7 @@ pipeline { quietPeriod(10) } triggers { - issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*') + issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?(?:benchmark\\W+)?tests(?:\\W+please)?.*') } parameters { booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.') @@ -220,6 +220,7 @@ pipeline { tag pattern: 'v\\d+\\.\\d+\\.\\d+.*', comparator: 'REGEXP' expression { return params.Run_As_Master_Branch } expression { return env.BENCHMARK_UPDATED != "false" } + expression { return env.GITHUB_COMMENT?.contains('benchmark tests') } } expression { return params.bench_ci } expression { return env.ONLY_DOCS == "false" } From 18ee0bf28a8896975c940442f67a99e11089f41e Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Mon, 15 Jun 2020 14:20:13 +0200 Subject: [PATCH 12/23] feat(rum-react): use correct path when route is path array (#800) * feat(rum-react): use correct path when route is path array * feat: move logic in apmroute * chore: address review and add cb test * fix: change name only if defined --- package-lock.json | 139 +++++++++++------- package.json | 4 +- packages/rum-react/src/get-apm-route.js | 26 +++- .../rum-react/src/get-with-transaction.js | 11 +- .../test/specs/get-apm-route.spec.js | 37 +++++ .../test/specs/get-with-transaction.spec.js | 19 ++- 6 files changed, 170 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index 26e12fc50..6e4b07825 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2236,6 +2236,23 @@ } } }, + "@babel/runtime": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.0.tgz", + "integrity": "sha512-tgYb3zVApHbLHYOPWtVwg25sBqHhfBXRKeKoTIyoheIxln1nA7oBl7SfHfiTG2GhDPI8EUBkOD/0wJCP/3HN4Q==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + } + } + }, "@babel/template": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", @@ -12896,27 +12913,17 @@ } }, "history": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", "dev": true, "requires": { - "invariant": "^2.2.1", + "@babel/runtime": "^7.1.2", "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" - }, - "dependencies": { - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - } + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" } }, "hmac-drbg": { @@ -12931,10 +12938,13 @@ } }, "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", - "dev": true + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + } }, "homedir-polyfill": { "version": "1.0.3", @@ -15983,6 +15993,16 @@ "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==", "dev": true }, + "mini-create-react-context": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz", + "integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "tiny-warning": "^1.0.3" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -18173,18 +18193,21 @@ "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" }, "react-router": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", - "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", "dev": true, "requires": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.2.4", + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.1", - "warning": "^4.0.1" + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" }, "dependencies": { "isarray": { @@ -18194,9 +18217,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -18205,17 +18228,18 @@ } }, "react-router-dom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", - "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", "dev": true, "requires": { - "history": "^4.7.2", - "invariant": "^2.2.4", + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", "loose-envify": "^1.3.1", - "prop-types": "^15.6.1", - "react-router": "^4.3.1", - "warning": "^4.0.1" + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" } }, "react-test-renderer": { @@ -18660,9 +18684,9 @@ } }, "resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", "dev": true }, "resolve-url": { @@ -20725,6 +20749,18 @@ "setimmediate": "^1.0.4" } }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==", + "dev": true + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, "title-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", @@ -21287,9 +21323,9 @@ } }, "value-equal": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", "dev": true }, "vary": { @@ -21378,15 +21414,6 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", diff --git a/package.json b/package.json index 8c20637c9..9685f07c5 100644 --- a/package.json +++ b/package.json @@ -149,8 +149,8 @@ "puppeteer": "^1.20.0", "react": "^16.2.0", "react-dom": "^16.2.0", - "react-router": "^4.2.0", - "react-router-dom": "^4.2.2", + "react-router": "^5.2.0", + "react-router-dom": "^5.2.0", "regenerator-runtime": "^0.13.3", "rimraf": "^3.0.0", "serve-index": "^1.9.1", diff --git a/packages/rum-react/src/get-apm-route.js b/packages/rum-react/src/get-apm-route.js index 9fac0af0d..dd3db7cae 100644 --- a/packages/rum-react/src/get-apm-route.js +++ b/packages/rum-react/src/get-apm-route.js @@ -27,6 +27,19 @@ import React from 'react' import { Route } from 'react-router-dom' import { getWithTransaction } from './get-with-transaction' +/** + * If the path/name is given as array, use the computed path + * to get the current transaction name + */ +function getTransactionName(name, props) { + const { match = {} } = props + + if (Array.isArray(name) && match.path) { + return match.path + } + return name +} + function getApmRoute(apm) { const withTransaction = getWithTransaction(apm) @@ -48,8 +61,17 @@ function getApmRoute(apm) { */ if (initial || pathChanged) { return { - apmComponent: withTransaction(path, 'route-change')(component), - path + path, + apmComponent: withTransaction( + path, + 'route-change', + (transaction, props) => { + if (transaction) { + const name = getTransactionName(path, props) + name && (transaction.name = name) + } + } + )(component) } } return null diff --git a/packages/rum-react/src/get-with-transaction.js b/packages/rum-react/src/get-with-transaction.js index a39e4e224..3f45b88a1 100644 --- a/packages/rum-react/src/get-with-transaction.js +++ b/packages/rum-react/src/get-with-transaction.js @@ -46,7 +46,7 @@ function isReactClassComponent(Component) { * - As a decorator: `@withTransaction('name','route-change')` */ function getWithTransaction(apm) { - return function withTransaction(name, type) { + return function withTransaction(name, type, callback = () => {}) { return function(Component) { const configService = apm.serviceFactory.getService('ConfigService') if (!configService.isActive()) { @@ -89,12 +89,14 @@ function getWithTransaction(apm) { * want this piece of code to run on every render instead we want to * start the transaction only on component mounting */ - const [transaction] = React.useState(() => - apm.startTransaction(name, type, { + const [transaction] = React.useState(() => { + const tr = apm.startTransaction(name, type, { managed: true, canReuse: true }) - ) + callback(tr, props) + return tr + }) /** * React guarantees the parent component effects are run after the child components effects @@ -131,6 +133,7 @@ function getWithTransaction(apm) { managed: true, canReuse: true }) + callback(this.transaction, props) } componentDidMount() { diff --git a/packages/rum-react/test/specs/get-apm-route.spec.js b/packages/rum-react/test/specs/get-apm-route.spec.js index b52557209..7a3f3d262 100644 --- a/packages/rum-react/test/specs/get-apm-route.spec.js +++ b/packages/rum-react/test/specs/get-apm-route.spec.js @@ -105,6 +105,43 @@ describe('ApmRoute', function() { ) }) + it('should work correctly with path array in props', function() { + const ApmRoute = getApmRoute(apmBase) + + const transactionService = serviceFactory.getService('TransactionService') + const dummyTr = { + name: 'test', + detectFinish: () => {} + } + spyOn(transactionService, 'startTransaction').and.returnValue(dummyTr) + + const Home = () => 'home' + class Topics extends React.Component { + render() { + return 'Topics' + } + } + + const rendered = mount( + + + + + ) + expect(dummyTr.name).toEqual('/') + expect(rendered.text()).toBe('home') + const history = rendered.find(Home).props().history + + history.push({ pathname: '/topic2' }) + expect(dummyTr.name).toEqual('/topic2') + expect(transactionService.startTransaction).toHaveBeenCalledTimes(2) + /** + * Should not create transaction as component is not rerendered + */ + history.push({ pathname: '/topic1' }) + expect(transactionService.startTransaction).toHaveBeenCalledTimes(2) + }) + it('should not trigger full rerender on query change', function() { const ApmRoute = getApmRoute(apmBase) const transactionService = serviceFactory.getService('TransactionService') diff --git a/packages/rum-react/test/specs/get-with-transaction.spec.js b/packages/rum-react/test/specs/get-with-transaction.spec.js index 5d840d6f7..da06a95c2 100644 --- a/packages/rum-react/test/specs/get-with-transaction.spec.js +++ b/packages/rum-react/test/specs/get-with-transaction.spec.js @@ -34,14 +34,15 @@ import { ApmBase } from '@elastic/apm-rum' import { createServiceFactory, afterFrame } from '@elastic/apm-rum-core' import { getGlobalConfig } from '../../../../dev-utils/test-config' -function TestComponent(apm) { +function TestComponent(apm, cb) { const withTransaction = getWithTransaction(apm) function Component(props) { return

Testing, {props.name}

} const WrappedComponent = withTransaction( 'test-transaction', - 'test-type' + 'test-type', + cb )(Component) expect(typeof WrappedComponent).toBe('function') const wrapped = mount() @@ -145,6 +146,20 @@ describe('withTransaction', function() { expect(wrapper.text()).toBe('Testing, new-props') }) + it('should accept callback function for withTransaction', () => { + const transactionService = serviceFactory.getService('TransactionService') + const labels = { + foo: 'bar' + } + TestComponent(apmBase, tr => { + tr.name = 'name-changed' + tr.addLabels(labels) + }) + const transaction = transactionService.getCurrentTransaction() + expect(transaction.name).toBe('name-changed') + expect(transaction.context.tags).toEqual(labels) + }) + it('should end transaction when component unmounts', done => { const transactionService = serviceFactory.getService('TransactionService') const detectFinishSpy = jasmine.createSpy('detectFinish') From 5ec41bab4ac6861a8211ce12abbed347d26c9214 Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Mon, 15 Jun 2020 15:59:44 +0200 Subject: [PATCH 13/23] fix(rum-core): capture tbt after all task entries are observed (#803) * fix(rum-core): capture tbt after all task entries are observed * chore: add test for different dispatch --- .../src/performance-monitoring/metrics.js | 52 +++++++++---------- .../transaction-service.js | 14 ++++- .../performance-monitoring/metrics.spec.js | 49 +++++++++++------ 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/packages/rum-core/src/performance-monitoring/metrics.js b/packages/rum-core/src/performance-monitoring/metrics.js index a0e78e6b8..8d8b5a14a 100644 --- a/packages/rum-core/src/performance-monitoring/metrics.js +++ b/packages/rum-core/src/performance-monitoring/metrics.js @@ -33,9 +33,14 @@ import { noop, PERF } from '../common/utils' import Span from './span' export const metrics = { - fcp: 0 + fcp: 0, + tbt: { + start: Infinity, + duration: 0 + } } +const LONG_TASK_THRESHOLD = 50 /** * Create Spans for the long task entries * Spec - https://w3c.github.io/longtasks/ @@ -109,23 +114,27 @@ export function createFirstInputDelaySpan(fidEntries) { } } +export function createTotalBlockingTimeSpan(tbtObject) { + const { start, duration } = tbtObject + const tbtSpan = new Span('Total Blocking Time', LONG_TASK, { + startTime: start + }) + tbtSpan.end(start + duration) + return tbtSpan +} + /** * Calculate Total Blocking Time (TBT) from long tasks */ export function calculateTotalBlockingTime(longtaskEntries) { - const threshold = 50 - const totalBlockingTime = { - start: Infinity, - duration: 0 - } - for (let i = 0; i < longtaskEntries.length; i++) { - const { name, startTime, duration } = longtaskEntries[i] + longtaskEntries.forEach(entry => { + const { name, startTime, duration } = entry /** * FCP is picked as the lower bound because there is little risk of user input happening * before FCP and it will not harm user experience. */ if (startTime < metrics.fcp) { - continue + return } /** * Account for long task originated only from the current browsing context @@ -133,25 +142,24 @@ export function calculateTotalBlockingTime(longtaskEntries) { * https://w3c.github.io/longtasks/#performancelongtasktiming */ if (name !== 'self' && name.indexOf('same-origin') === -1) { - continue + return } /** * Calcualte the start time of the first long task so we can add it * as span start */ - totalBlockingTime.start = Math.min(totalBlockingTime.start, startTime) + metrics.tbt.start = Math.min(metrics.tbt.start, startTime) - const blockingTime = duration - threshold /** * Theoretically blocking time would always be greater than 0 as Long tasks are * tasks that exceeds 50ms, but this would be configurable in the future so * the > 0 check acts as an extra guard */ + const blockingTime = duration - LONG_TASK_THRESHOLD if (blockingTime > 0) { - totalBlockingTime.duration += blockingTime + metrics.tbt.duration += blockingTime } - } - return totalBlockingTime + }) } /** @@ -219,24 +227,16 @@ export function captureObserverEntries(list, { capturePaint }) { } /** - * Capture TBT as Span only for page load navigation + * Capture First Input Delay (FID) as span */ - const { duration, start } = calculateTotalBlockingTime(longtaskEntries) - - if (duration > 0) { - const tbtSpan = new Span('Total Blocking Time', LONG_TASK, { - startTime: start - }) - tbtSpan.end(start + duration) - result.spans.push(tbtSpan) - } - const fidEntries = list.getEntriesByType(FIRST_INPUT) const fidSpan = createFirstInputDelaySpan(fidEntries) if (fidSpan) { result.spans.push(fidSpan) } + calculateTotalBlockingTime(longtaskEntries) + return result } diff --git a/packages/rum-core/src/performance-monitoring/transaction-service.js b/packages/rum-core/src/performance-monitoring/transaction-service.js index c5718b69e..b6a3c18ff 100644 --- a/packages/rum-core/src/performance-monitoring/transaction-service.js +++ b/packages/rum-core/src/performance-monitoring/transaction-service.js @@ -25,7 +25,12 @@ import { Promise } from '../common/polyfills' import Transaction from './transaction' -import { PerfEntryRecorder, captureObserverEntries } from './metrics' +import { + PerfEntryRecorder, + captureObserverEntries, + metrics, + createTotalBlockingTimeSpan +} from './metrics' import { extend, getEarliestSpan, getLatestNonXHRSpan } from '../common/utils' import { captureNavigation } from './capture-navigation' import { @@ -278,6 +283,13 @@ class TransactionService { if (name === NAME_UNKNOWN && pageLoadTransactionName) { tr.name = pageLoadTransactionName } + /** + * Capture the TBT as span after observing for all long task entries + * and once performance observer is disconnected + */ + if (tr.captureTimings && metrics.tbt.duration > 0) { + tr.spans.push(createTotalBlockingTimeSpan(metrics.tbt)) + } } captureNavigation(tr) diff --git a/packages/rum-core/test/performance-monitoring/metrics.spec.js b/packages/rum-core/test/performance-monitoring/metrics.spec.js index 9e26edc59..c480f67c4 100644 --- a/packages/rum-core/test/performance-monitoring/metrics.spec.js +++ b/packages/rum-core/test/performance-monitoring/metrics.spec.js @@ -26,6 +26,7 @@ import { captureObserverEntries, calculateTotalBlockingTime, createFirstInputDelaySpan, + createTotalBlockingTimeSpan, PerfEntryRecorder, metrics } from '../../src/performance-monitoring/metrics' @@ -46,6 +47,7 @@ describe('Metrics', () => { beforeEach(() => { list.getEntriesByType.and.returnValue([]) list.getEntriesByName.and.returnValue([]) + metrics.tbt = { start: Infinity, duration: 0 } }) it('should not create long tasks spans if entries are not present', () => { @@ -154,40 +156,57 @@ describe('Metrics', () => { describe('Total Blocking Time', () => { it('should create total blocking time as span', () => { - list.getEntriesByType.and.callFake(mockObserverEntryTypes) - const { spans } = captureObserverEntries(list, { - capturePaint: true - }) - const tbtSpans = spans.filter( - span => span.name === 'Total Blocking Time' - ) - expect(tbtSpans[0]).toEqual( + calculateTotalBlockingTime(longtaskEntries) + const tbtSpan = createTotalBlockingTimeSpan(metrics.tbt) + expect(tbtSpan).toEqual( jasmine.objectContaining({ name: 'Total Blocking Time', - type: LONG_TASK + type: LONG_TASK, + _start: 745.4100000031758, + _end: 995.3399999591056 }) ) }) it('should calculate total blocking time from long tasks', () => { - const tbt = calculateTotalBlockingTime(longtaskEntries) - expect(tbt).toEqual({ + calculateTotalBlockingTime(longtaskEntries) + expect(metrics.tbt).toEqual({ start: 745.4100000031758, duration: 249.92999995592982 }) }) + it('should update tbt when longtasks are dispatched at different times', () => { + list.getEntriesByType.and.callFake(mockObserverEntryTypes) + captureObserverEntries(list, { + capturePaint: true + }) + expect(metrics.tbt).toEqual({ + start: 745.4100000031758, + duration: 249.92999995592982 + }) + + // simulating second longtask entry list + captureObserverEntries(list, { + capturePaint: true + }) + expect(metrics.tbt).toEqual({ + start: 745.4100000031758, + duration: 499.85999991185963 + }) + }) + it('should calculate total blocking time based on FCP', () => { metrics.fcp = 1000 - const tbt = calculateTotalBlockingTime(longtaskEntries) - expect(tbt).toEqual({ + calculateTotalBlockingTime(longtaskEntries) + expect(metrics.tbt).toEqual({ start: 1023.40999995591, duration: 245.35999997751787 }) }) it('should return tbt as 0 when entries are not self/same-origin', () => { - const tbt = calculateTotalBlockingTime([ + calculateTotalBlockingTime([ { name: 'unknown', startTime: 10 @@ -197,7 +216,7 @@ describe('Metrics', () => { startTime: 20 } ]) - expect(tbt).toEqual({ + expect(metrics.tbt).toEqual({ start: Infinity, duration: 0 }) From 7ee82dae3b53bc439e07d3e2baafd91a00841643 Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Mon, 15 Jun 2020 16:56:14 +0200 Subject: [PATCH 14/23] chore(rum-core): use startTime for LCP marks (#815) --- packages/rum-core/src/performance-monitoring/metrics.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rum-core/src/performance-monitoring/metrics.js b/packages/rum-core/src/performance-monitoring/metrics.js index 8d8b5a14a..583a49872 100644 --- a/packages/rum-core/src/performance-monitoring/metrics.js +++ b/packages/rum-core/src/performance-monitoring/metrics.js @@ -198,10 +198,11 @@ export function captureObserverEntries(list, { capturePaint }) { if (lastLcpEntry) { /** + * `startTime` - equals to renderTime if it's nonzero, otherwise equal to loadTime. * `renderTime` will not be available for Image element and for the element * that is loaded cross-origin without the `Timing-Allow-Origin` header. */ - const lcp = parseInt(lastLcpEntry.renderTime || lastLcpEntry.loadTime) + const lcp = parseInt(lastLcpEntry.startTime) metrics.lcp = lcp result.marks.largestContentfulPaint = lcp } From 94594792e35f824c8733dd1c8ee5cb9bdb73059f Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Tue, 23 Jun 2020 14:00:59 +0200 Subject: [PATCH 15/23] fix(rum-core): protect aganist buggy navigation timing data (#819) --- .../capture-navigation.js | 36 ++++++++----- .../capture-navigation.spec.js | 52 ++++++++++++++++++- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/packages/rum-core/src/performance-monitoring/capture-navigation.js b/packages/rum-core/src/performance-monitoring/capture-navigation.js index 1093335de..0d96eb4e6 100644 --- a/packages/rum-core/src/performance-monitoring/capture-navigation.js +++ b/packages/rum-core/src/performance-monitoring/capture-navigation.js @@ -255,9 +255,18 @@ const COMPRESSED_NAV_TIMING_MARKS = [ 'ee' ] -function getNavigationTimingMarks() { - const timing = PERF.timing - const fetchStart = timing.fetchStart +function getNavigationTimingMarks(timing) { + const { fetchStart, navigationStart } = timing + /** + * Detect if NavigationTiming data is buggy and discard + * capturing navigation marks for the transaction + * + * Webkit bug - https://bugs.webkit.org/show_bug.cgi?id=168057 + */ + if (navigationStart && fetchStart < navigationStart) { + return null + } + const marks = {} NAVIGATION_TIMING_MARKS.forEach(function(timingKey) { const m = timing[timingKey] @@ -268,16 +277,18 @@ function getNavigationTimingMarks() { return marks } -function getPageLoadMarks() { - const marks = getNavigationTimingMarks() - const agent = { - timeToFirstByte: marks.responseStart, - domInteractive: marks.domInteractive, - domComplete: marks.domComplete +function getPageLoadMarks(timing) { + const marks = getNavigationTimingMarks(timing) + if (marks == null) { + return null } return { navigationTiming: marks, - agent + agent: { + timeToFirstByte: marks.responseStart, + domInteractive: marks.domInteractive, + domComplete: marks.domComplete + } } } @@ -334,9 +345,9 @@ function captureNavigation(transaction) { }) /** - * Page load marks that are gathered from navigation and paint timing API + * Page load marks that are gathered from NavigationTiming API */ - transaction.addMarks(getPageLoadMarks()) + transaction.addMarks(getPageLoadMarks(timings)) } if (isPerfTimelineSupported()) { @@ -365,6 +376,7 @@ function captureNavigation(transaction) { } export { + getPageLoadMarks, captureNavigation, createNavigationTimingSpans, createResourceTimingSpans, 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 a4db49848..0f5b02c99 100644 --- a/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js +++ b/packages/rum-core/test/performance-monitoring/capture-navigation.spec.js @@ -27,15 +27,17 @@ import { createNavigationTimingSpans, createResourceTimingSpans, createUserTimingSpans, - captureNavigation + captureNavigation, + getPageLoadMarks } from '../../src/performance-monitoring/capture-navigation' import Transaction from '../../src/performance-monitoring/transaction' +import { PAGE_LOAD, ROUTE_CHANGE } from '../../src/common/constants' +import { extend } from '../../src/common/utils' import resourceEntries from '../fixtures/resource-entries' import userTimingEntries from '../fixtures/user-timing-entries' import navTimingSpans from '../fixtures/navigation-timing-span-snapshot' import { TIMING_LEVEL1_ENTRY as timings } from '../fixtures/navigation-entries' import { mockGetEntriesByType } from '../utils/globals-mock' -import { PAGE_LOAD, ROUTE_CHANGE } from '../../src/common/constants' const spanSnapshot = navTimingSpans.map(mapSpan) @@ -306,4 +308,50 @@ describe('Capture hard navigation', function() { unMock() }) + + it('should capture page load navigation marks', () => { + const marks = getPageLoadMarks(timings) + expect(marks.navigationTiming).toEqual( + jasmine.objectContaining({ + responseEnd: 209, + domInteractive: 542, + domComplete: 962 + }) + ) + }) + + describe('Buggy Navigation Timing data', () => { + it('when timestamps are 0-based instead of unix epoch', () => { + /** + * navigationStart and DOM timings in Unix epoch, other timings 0-based for Back-Forward navigations + * https://bugs.webkit.org/show_bug.cgi?id=168057 + */ + const timingCopy = extend({}, timings) + timingCopy.fetchStart = 0 + timingCopy.requestStart = 10 + timingCopy.responseStart = 25 + timingCopy.responseEnd = 0 + timingCopy.loadEventStart = 0 + + const marks = getPageLoadMarks(timingCopy) + expect(marks).toBe(null) + }) + + it('requestStart & responseStart before fetchStart', () => { + /** + * requestStart, responseStart before navigationStart & fetchStart + * https://bugs.webkit.org/show_bug.cgi?id=168055 + * https://bugs.chromium.org/p/chromium/issues/detail?id=127644 + */ + const timingCopy = extend({}, timings) + timingCopy.requestStart = timingCopy.fetchStart - 200 + timingCopy.responseStart = timingCopy.fetchStart - 500 + + const marks = getPageLoadMarks(timingCopy) + + expect(marks.navigationTiming.responseStart).toBe(undefined) + expect(marks.navigationTiming.requestStart).toBe(undefined) + expect(marks.agent.timeToFirstByte).toBe(undefined) + }) + }) }) From fa90b8d0c7c06ec40bd66f789039b1fe81e04c89 Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Tue, 23 Jun 2020 14:01:13 +0200 Subject: [PATCH 16/23] fix(rum-core): protect aganist buggy navigation timing data (#819) From 91de44a75fd780f5dcd39fce60b028422a626a6c Mon Sep 17 00:00:00 2001 From: apmmachine Date: Wed, 24 Jun 2020 09:42:58 +0000 Subject: [PATCH 17/23] chore(release): publish - @elastic/apm-rum-angular@1.1.3 - @elastic/apm-rum-core@5.3.1 - @elastic/apm-rum-react@1.2.0 - @elastic/apm-rum-vue@1.1.3 - @elastic/apm-rum@5.2.1 --- packages/rum-angular/CHANGELOG.md | 8 ++++++++ packages/rum-angular/package.json | 2 +- packages/rum-core/CHANGELOG.md | 12 ++++++++++++ packages/rum-core/package.json | 2 +- packages/rum-react/CHANGELOG.md | 11 +++++++++++ packages/rum-react/package.json | 2 +- packages/rum-vue/CHANGELOG.md | 8 ++++++++ packages/rum-vue/package.json | 2 +- packages/rum/CHANGELOG.md | 8 ++++++++ packages/rum/package.json | 2 +- packages/rum/src/apm-base.js | 2 +- 11 files changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/rum-angular/CHANGELOG.md b/packages/rum-angular/CHANGELOG.md index ab2cbe110..3235e71fa 100644 --- a/packages/rum-angular/CHANGELOG.md +++ b/packages/rum-angular/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.3](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-angular@1.1.2...@elastic/apm-rum-angular@1.1.3) (2020-06-24) + +**Note:** Version bump only for package @elastic/apm-rum-angular + + + + + ## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-angular@1.1.1...@elastic/apm-rum-angular@1.1.2) (2020-05-28) diff --git a/packages/rum-angular/package.json b/packages/rum-angular/package.json index f285b2c67..f2ccf6dc7 100644 --- a/packages/rum-angular/package.json +++ b/packages/rum-angular/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-angular", - "version": "1.1.2", + "version": "1.1.3", "description": "Elastic APM Real User Monitoring for Angular applications", "homepage": "https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html", "license": "MIT", diff --git a/packages/rum-core/CHANGELOG.md b/packages/rum-core/CHANGELOG.md index c464adec7..9a9ccd202 100644 --- a/packages/rum-core/CHANGELOG.md +++ b/packages/rum-core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.3.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-core@5.3.0...@elastic/apm-rum-core@5.3.1) (2020-06-24) + + +### Bug Fixes + +* **rum-core:** capture tbt after all task entries are observed ([#803](https://github.com/elastic/apm-agent-rum-js/issues/803)) ([5ec41ba](https://github.com/elastic/apm-agent-rum-js/commit/5ec41bab4ac6861a8211ce12abbed347d26c9214)) +* **rum-core:** protect aganist buggy navigation timing data ([#819](https://github.com/elastic/apm-agent-rum-js/issues/819)) ([9459479](https://github.com/elastic/apm-agent-rum-js/commit/94594792e35f824c8733dd1c8ee5cb9bdb73059f)) + + + + + # [5.3.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-core@5.2.0...@elastic/apm-rum-core@5.3.0) (2020-05-28) diff --git a/packages/rum-core/package.json b/packages/rum-core/package.json index 88d1ce2e6..a340c8d2d 100644 --- a/packages/rum-core/package.json +++ b/packages/rum-core/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-core", - "version": "5.3.0", + "version": "5.3.1", "description": "Elastic apm core", "license": "MIT", "main": "dist/lib/index.js", diff --git a/packages/rum-react/CHANGELOG.md b/packages/rum-react/CHANGELOG.md index 599e0581e..d5ccbbbe8 100644 --- a/packages/rum-react/CHANGELOG.md +++ b/packages/rum-react/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.2.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-react@1.1.2...@elastic/apm-rum-react@1.2.0) (2020-06-24) + + +### Features + +* **rum-react:** use correct path when route is path array ([#800](https://github.com/elastic/apm-agent-rum-js/issues/800)) ([18ee0bf](https://github.com/elastic/apm-agent-rum-js/commit/18ee0bf28a8896975c940442f67a99e11089f41e)) + + + + + ## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-react@1.1.1...@elastic/apm-rum-react@1.1.2) (2020-05-28) diff --git a/packages/rum-react/package.json b/packages/rum-react/package.json index a8e51c37e..60cd87e95 100644 --- a/packages/rum-react/package.json +++ b/packages/rum-react/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-react", - "version": "1.1.2", + "version": "1.2.0", "description": "Elastic APM Real User Monitoring for React applications", "author": "Hamid ", "homepage": "https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html", diff --git a/packages/rum-vue/CHANGELOG.md b/packages/rum-vue/CHANGELOG.md index 443d755b7..7366f1360 100644 --- a/packages/rum-vue/CHANGELOG.md +++ b/packages/rum-vue/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.3](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-vue@1.1.2...@elastic/apm-rum-vue@1.1.3) (2020-06-24) + +**Note:** Version bump only for package @elastic/apm-rum-vue + + + + + ## [1.1.2](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum-vue@1.1.1...@elastic/apm-rum-vue@1.1.2) (2020-05-28) diff --git a/packages/rum-vue/package.json b/packages/rum-vue/package.json index 1faa291b8..14df36c61 100644 --- a/packages/rum-vue/package.json +++ b/packages/rum-vue/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum-vue", - "version": "1.1.2", + "version": "1.1.3", "description": "Elastic APM Real User Monitoring for Vue applications", "keywords": [ "elastic", diff --git a/packages/rum/CHANGELOG.md b/packages/rum/CHANGELOG.md index c28c41152..4f2690e66 100644 --- a/packages/rum/CHANGELOG.md +++ b/packages/rum/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.2.1](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum@5.2.0...@elastic/apm-rum@5.2.1) (2020-06-24) + +**Note:** Version bump only for package @elastic/apm-rum + + + + + # [5.2.0](https://github.com/elastic/apm-agent-rum-js/compare/@elastic/apm-rum@5.1.1...@elastic/apm-rum@5.2.0) (2020-05-28) diff --git a/packages/rum/package.json b/packages/rum/package.json index e73ad3362..cb372d883 100644 --- a/packages/rum/package.json +++ b/packages/rum/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/apm-rum", - "version": "5.2.0", + "version": "5.2.1", "description": "Elastic APM JavaScript agent", "main": "dist/lib/index.js", "module": "dist/es/index.js", diff --git a/packages/rum/src/apm-base.js b/packages/rum/src/apm-base.js index 6d2c4689b..f63edeb1b 100644 --- a/packages/rum/src/apm-base.js +++ b/packages/rum/src/apm-base.js @@ -49,7 +49,7 @@ export default class ApmBase { /** * Set Agent version to be sent as part of metadata to the APM Server */ - configService.setVersion('5.2.0') + configService.setVersion('5.2.1') this.config(config) /** * Deactive agent when the active config flag is set to false From f38d6fe46c75c9e5a44e5389d257286a53fb6f1a Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Wed, 24 Jun 2020 12:21:43 +0200 Subject: [PATCH 18/23] docs: release notes for 5.2.1 (#824) --- CHANGELOG.asciidoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index c253176ec..a898c6b51 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -29,6 +29,22 @@ See Conventional Commits (https://conventionalcommits.org) for commit guidelines [[release-notes-5.x]] === RUM JS Agent version 5.x +[[release-notes-5.2.1]] +==== 5.2.1 (2020-06-24) + +[float] +===== Features +* Added support for path array in `` React component that associates + the transaction based on the mounted path: {issue}702[#702] + +[float] +===== Bug fixes +* Capture Total Blocking Time (TBT) only after all longtask entries + are observed: {issue}803[#803] +* Do not capture page load transaction marks when the NavigationTiming data from + the browsers are not trustable: {issue}818[#818] + + [[release-notes-5.2.0]] ==== 5.2.0 (2020-05-28) From 79370d32fec42fee404db0f321aacfc4507a6337 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 29 Jun 2020 13:53:36 +0100 Subject: [PATCH 19/23] ci(jenkins): report bundlesize as a GitHub comment (#826) * ci(jenkins): report bundlesize as a GitHub comment TODO: Create markdown file on the fly * ci: stash outside of the shell step * ci: revert changes * ci: use current * ci: use step * ci: for testing purposes * ci: call the step * ci: debugging purposes * ci: use the worker instead * ci: no in the docker container * ci: use current branch and change comment --- Jenkinsfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2cc99a7ee..b25456384 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { BASE_DIR = "src/github.com/elastic/${env.REPO}" NOTIFY_TO = credentials('notify-to') JOB_GCS_BUCKET = credentials('gcs-bucket') - PIPELINE_LOG_LEVEL='INFO' + PIPELINE_LOG_LEVEL = 'INFO' CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-rum-codecov' SAUCELABS_SECRET_CORE = 'secret/apm-team/ci/apm-agent-rum-saucelabs@elastic/apm-rum-core' SAUCELABS_SECRET = 'secret/apm-team/ci/apm-agent-rum-saucelabs@elastic/apm-rum' @@ -91,6 +91,10 @@ pipeline { } stash allowEmpty: true, name: 'cache', includes: "${BASE_DIR}/.npm/**", useDefaultExcludes: false } + dir("${BASE_DIR}"){ + // To run in the worker otherwise some tools won't be in place when using the above docker container + generateReport(id: 'bundlesize', input: 'packages/rum/reports/apm-*-report.html', template: true, compare: true, templateFormat: 'md') + } } } } @@ -364,7 +368,8 @@ pipeline { } post { cleanup { - notifyBuildResult() + // bundlesize id was generated previously with the generateReport step in the lint stage. + notifyBuildResult(prComment: true, newPRComment: [ 'bundlesize': 'bundlesize.md' ]) } } } From 9de8344b3e994c06f5d2d72b6e6a3fd736a7c039 Mon Sep 17 00:00:00 2001 From: Hamid Date: Mon, 29 Jun 2020 22:55:32 +0200 Subject: [PATCH 20/23] feat: use page visibilityState for browser responsiveness check (#813) * feat: use page visibilityState for browser responsiveness check * chore: merge state and env merge bootstrap files * chore: address review --- packages/rum-core/src/bootstrap.js | 20 +++++- packages/rum-core/src/common/apm-server.js | 2 +- packages/rum-core/src/common/constants.js | 6 -- .../rum-core/src/common/service-factory.js | 2 +- packages/rum-core/src/opentracing/tracer.js | 2 +- .../src/performance-monitoring/metrics.js | 16 +---- .../performance-monitoring.js | 39 +---------- .../transaction-service.js | 39 +++-------- .../src/performance-monitoring/transaction.js | 1 - packages/rum-core/src/{env.js => state.js} | 2 + .../performance-monitoring.spec.js | 52 -------------- .../transaction-service.spec.js | 70 +++++-------------- 12 files changed, 56 insertions(+), 195 deletions(-) rename packages/rum-core/src/{env.js => state.js} (98%) diff --git a/packages/rum-core/src/bootstrap.js b/packages/rum-core/src/bootstrap.js index 1ab94e636..a3b448c83 100644 --- a/packages/rum-core/src/bootstrap.js +++ b/packages/rum-core/src/bootstrap.js @@ -25,13 +25,13 @@ import { isPlatformSupported, isBrowser } from './common/utils' import { patchAll } from './common/patching' -import { bootstrapMetrics } from './performance-monitoring/metrics' +import { state } from './state' let enabled = false export function bootstrap() { if (isPlatformSupported()) { patchAll() - bootstrapMetrics() + bootstrapPerf() enabled = true } else if (isBrowser) { /** @@ -43,3 +43,19 @@ export function bootstrap() { return enabled } + +export function bootstrapPerf() { + if (document.visibilityState === 'hidden') { + state.lastHiddenStart = 0 + } + + window.addEventListener( + 'visibilitychange', + () => { + if (document.visibilityState === 'hidden') { + state.lastHiddenStart = performance.now() + } + }, + { capture: true } + ) +} diff --git a/packages/rum-core/src/common/apm-server.js b/packages/rum-core/src/common/apm-server.js index 50425503b..d6bbb0d25 100644 --- a/packages/rum-core/src/common/apm-server.js +++ b/packages/rum-core/src/common/apm-server.js @@ -36,7 +36,7 @@ import { compressTransaction, compressError } from './compress' -import { __DEV__ } from '../env' +import { __DEV__ } from '../state' /** * Throttling interval defaults to 60 seconds diff --git a/packages/rum-core/src/common/constants.js b/packages/rum-core/src/common/constants.js index e9fd6ef73..2462cc2d3 100644 --- a/packages/rum-core/src/common/constants.js +++ b/packages/rum-core/src/common/constants.js @@ -129,11 +129,6 @@ const FIRST_CONTENTFUL_PAINT = 'first-contentful-paint' const LARGEST_CONTENTFUL_PAINT = 'largest-contentful-paint' const FIRST_INPUT = 'first-input' -/** - * Managed transaction configs - */ -const BROWSER_RESPONSIVENESS_INTERVAL = 500 - /** * Event types sent to APM Server on the queue */ @@ -194,7 +189,6 @@ export { TEMPORARY_TYPE, USER_INTERACTION, TRANSACTION_TYPE_ORDER, - BROWSER_RESPONSIVENESS_INTERVAL, ERRORS, TRANSACTIONS, CONFIG_SERVICE, diff --git a/packages/rum-core/src/common/service-factory.js b/packages/rum-core/src/common/service-factory.js index ed1d9eb33..d656b125f 100644 --- a/packages/rum-core/src/common/service-factory.js +++ b/packages/rum-core/src/common/service-factory.js @@ -32,7 +32,7 @@ import { LOGGING_SERVICE, APM_SERVER } from './constants' -import { __DEV__ } from '../env' +import { __DEV__ } from '../state' const serviceCreators = { [CONFIG_SERVICE]: () => new ConfigService(), diff --git a/packages/rum-core/src/opentracing/tracer.js b/packages/rum-core/src/opentracing/tracer.js index a95b04743..993466aec 100644 --- a/packages/rum-core/src/opentracing/tracer.js +++ b/packages/rum-core/src/opentracing/tracer.js @@ -32,7 +32,7 @@ import { } from 'opentracing/lib/constants' import { Span as NoopSpan } from 'opentracing/lib/span' import { getTimeOrigin, find } from '../common/utils' -import { __DEV__ } from '../env' +import { __DEV__ } from '../state' import Span from './span' class Tracer extends otTracer { diff --git a/packages/rum-core/src/performance-monitoring/metrics.js b/packages/rum-core/src/performance-monitoring/metrics.js index 583a49872..e5d0755a2 100644 --- a/packages/rum-core/src/performance-monitoring/metrics.js +++ b/packages/rum-core/src/performance-monitoring/metrics.js @@ -105,7 +105,7 @@ function createLongTaskSpans(longtasks) { export function createFirstInputDelaySpan(fidEntries) { let firstInput = fidEntries[0] - if (firstInput && !metrics.wasHidden) { + if (firstInput) { const { startTime, processingStart } = firstInput const span = new Span('First Input Delay', FIRST_INPUT, { startTime }) @@ -292,17 +292,3 @@ export class PerfEntryRecorder { this.po.disconnect() } } - -export function bootstrapMetrics() { - metrics.wasHidden = document.visibilityState === 'hidden' - - window.addEventListener( - 'visibilitychange', - () => { - if (document.visibilityState === 'hidden') { - metrics.wasHidden = true - } - }, - { capture: true, once: true } - ) -} diff --git a/packages/rum-core/src/performance-monitoring/performance-monitoring.js b/packages/rum-core/src/performance-monitoring/performance-monitoring.js index 175335ce2..ded33772a 100644 --- a/packages/rum-core/src/performance-monitoring/performance-monitoring.js +++ b/packages/rum-core/src/performance-monitoring/performance-monitoring.js @@ -43,21 +43,18 @@ import { XMLHTTPREQUEST, EVENT_TARGET, HTTP_REQUEST_TYPE, - USER_INTERACTION, - PAGE_LOAD, - BROWSER_RESPONSIVENESS_INTERVAL + USER_INTERACTION } from '../common/constants' import { truncateModel, SPAN_MODEL, TRANSACTION_MODEL } from '../common/truncate' -import { __DEV__ } from '../env' +import { __DEV__ } from '../state' /** * Parameters used for Managed Transactions */ -const BROWSER_RESPONSIVENESS_BUFFER = 3 const SIMILAR_SPAN_TO_TRANSACTION_RATIO = 0.05 const TRANSACTION_DURATION_THRESHOLD = 60000 @@ -141,14 +138,6 @@ export function adjustTransactionSpans(transaction) { return transaction } -export function checkBrowserResponsiveness(transaction, interval, buffer) { - const counter = transaction.browserResponsivenessCounter - const duration = transaction.duration() - const expectedCount = Math.floor(duration / interval) - - return counter + buffer >= expectedCount -} - export default class PerformanceMonitoring { constructor(apmServer, configService, loggingService, transactionService) { this._apmServer = apmServer @@ -378,30 +367,6 @@ export default class PerformanceMonitoring { } return false } - - /** - * TODO: Refactor the type check with better logic - */ - if (tr.type !== PAGE_LOAD) { - const wasBrowserResponsive = checkBrowserResponsiveness( - tr, - BROWSER_RESPONSIVENESS_INTERVAL, - BROWSER_RESPONSIVENESS_BUFFER - ) - - if (!wasBrowserResponsive) { - if (__DEV__) { - this._logginService.debug( - `transaction(${tr.id}, ${tr.name}) was discarded! Browser was not responsive enough during the transaction.`, - ' duration:', - duration, - ' browserResponsivenessCounter:', - tr.browserResponsivenessCounter - ) - } - return false - } - } } return true } diff --git a/packages/rum-core/src/performance-monitoring/transaction-service.js b/packages/rum-core/src/performance-monitoring/transaction-service.js index b6a3c18ff..9359fe310 100644 --- a/packages/rum-core/src/performance-monitoring/transaction-service.js +++ b/packages/rum-core/src/performance-monitoring/transaction-service.js @@ -38,7 +38,6 @@ import { NAME_UNKNOWN, TRANSACTION_START, TRANSACTION_END, - BROWSER_RESPONSIVENESS_INTERVAL, TEMPORARY_TYPE, TRANSACTION_TYPE_ORDER, LARGEST_CONTENTFUL_PAINT, @@ -48,7 +47,7 @@ import { FIRST_INPUT } from '../common/constants' import { addTransactionContext } from '../common/context' -import { __DEV__ } from '../env' +import { __DEV__, state } from '../state' class TransactionService { constructor(logger, config) { @@ -91,28 +90,6 @@ class TransactionService { this.currentTransaction = value } - ensureRespInterval(checkBrowserResponsiveness) { - const clearRespInterval = () => { - clearInterval(this.respIntervalId) - this.respIntervalId = undefined - } - - if (checkBrowserResponsiveness) { - if (typeof this.respIntervalId === 'undefined') { - this.respIntervalId = setInterval(() => { - let tr = this.getCurrentTransaction() - if (tr) { - tr.browserResponsivenessCounter++ - } else { - clearRespInterval() - } - }, BROWSER_RESPONSIVENESS_INTERVAL) - } - } else if (typeof this.respIntervalId !== 'undefined') { - clearRespInterval() - } - } - createOptions(options) { const config = this._config.config let presetOptions = { transactionSampleRate: config.transactionSampleRate } @@ -176,14 +153,12 @@ class TransactionService { tr = this.ensureCurrentTransaction(name, type, perfOptions) } - let checkBrowserResponsiveness = true if (tr.type === PAGE_LOAD) { if (!isRedefined) { this.recorder.start(LARGEST_CONTENTFUL_PAINT) this.recorder.start(PAINT) this.recorder.start(FIRST_INPUT) } - checkBrowserResponsiveness = false if (perfOptions.pageLoadTraceId) { tr.traceId = perfOptions.pageLoadTraceId } @@ -212,8 +187,6 @@ class TransactionService { tr.captureTimings = true } - this.ensureRespInterval(checkBrowserResponsiveness) - return tr } @@ -262,6 +235,16 @@ class TransactionService { return Promise.resolve().then( () => { const { name, type } = tr + let { lastHiddenStart } = state + + if (lastHiddenStart >= tr._start) { + if (__DEV__) { + this._logger.debug( + `transaction(${tr.id}, ${tr.name}, ${tr.type}) was discarded! The page was hidden during the transaction!` + ) + } + return + } if (this.shouldIgnoreTransaction(name) || type === TEMPORARY_TYPE) { if (__DEV__) { diff --git a/packages/rum-core/src/performance-monitoring/transaction.js b/packages/rum-core/src/performance-monitoring/transaction.js index 76b471f83..f17c0314d 100644 --- a/packages/rum-core/src/performance-monitoring/transaction.js +++ b/packages/rum-core/src/performance-monitoring/transaction.js @@ -53,7 +53,6 @@ class Transaction extends SpanBase { this.breakdownTimings = [] this.sampled = Math.random() <= this.options.transactionSampleRate - this.browserResponsivenessCounter = 0 } addMarks(obj) { diff --git a/packages/rum-core/src/env.js b/packages/rum-core/src/state.js similarity index 98% rename from packages/rum-core/src/env.js rename to packages/rum-core/src/state.js index abcd23f4a..b6047b935 100644 --- a/packages/rum-core/src/env.js +++ b/packages/rum-core/src/state.js @@ -26,3 +26,5 @@ const __DEV__ = process.env.NODE_ENV !== 'production' export { __DEV__ } + +export const state = {} diff --git a/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js b/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js index e84a3ef38..e6658213d 100644 --- a/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js +++ b/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js @@ -28,7 +28,6 @@ import Transaction from '../../src/performance-monitoring/transaction' import Span from '../../src/performance-monitoring/span' import { groupSmallContinuouslySimilarSpans, - checkBrowserResponsiveness, adjustTransactionSpans } from '../../src/performance-monitoring/performance-monitoring' import { getGlobalConfig } from '../../../../dev-utils/test-config' @@ -121,31 +120,6 @@ describe('PerformanceMonitoring', function() { ) }) - it('should filter transactions based on browser responsiveness', function() { - spyOn(logger, 'debug').and.callThrough() - expect(logger.debug).not.toHaveBeenCalled() - var tr = new Transaction('transaction', 'transaction', { - id: 212, - transactionSampleRate: 1, - managed: true, - startTime: 1 - }) - var span = tr.startSpan('test span', 'test span type') - span.end() - tr.end(3501) - - tr.browserResponsivenessCounter = 3 - var wasBrowserResponsive = performanceMonitoring.filterTransaction(tr) - expect(wasBrowserResponsive).toBe(false) - expect(logger.debug).toHaveBeenCalledWith( - 'transaction(212, transaction) was discarded! Browser was not responsive enough during the transaction.', - ' duration:', - 3500, - ' browserResponsivenessCounter:', - 3 - ) - }) - it('should initialize and add transaction to the queue', async () => { performanceMonitoring.init() spyOn(apmServer, 'addTransaction') @@ -816,32 +790,6 @@ describe('PerformanceMonitoring', function() { expect(grouped[1].name).toBe('another-name') }) - it('should calculate browser responsiveness', function() { - const tr = new Transaction('transaction', 'transaction', { - startTime: 1 - }) - tr.end(400) - - tr.browserResponsivenessCounter = 0 - var resp = checkBrowserResponsiveness(tr, 500, 2) - expect(resp).toBe(true) - - tr._end = 1001 - tr.browserResponsivenessCounter = 2 - resp = checkBrowserResponsiveness(tr, 500, 2) - expect(resp).toBe(true) - - tr._end = 1601 - tr.browserResponsivenessCounter = 2 - resp = checkBrowserResponsiveness(tr, 500, 2) - expect(resp).toBe(true) - - tr._end = 3001 - tr.browserResponsivenessCounter = 3 - resp = checkBrowserResponsiveness(tr, 500, 2) - expect(resp).toBe(false) - }) - it('should reset spans for unsampled transactions', function() { const tr = new Transaction('unsampled', 'test', { transactionSampleRate: 0, diff --git a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js index c4c71914b..f4729e1c3 100644 --- a/packages/rum-core/test/performance-monitoring/transaction-service.spec.js +++ b/packages/rum-core/test/performance-monitoring/transaction-service.spec.js @@ -39,6 +39,7 @@ import { TRUNCATED_TYPE, FIRST_INPUT } from '../../src/common/constants' +import { state } from '../../src/state' describe('TransactionService', function() { var transactionService @@ -565,57 +566,6 @@ describe('TransactionService', function() { expect(tr.type).toBe('temporary') }) - it('should only call setInterval once for current transaction', () => { - let origSetInterval = window.setInterval - let origClearInterval = window.clearInterval - let count = 0 - let callback - - window.setInterval = cb => { - callback = cb - return count++ - } - - let clearCount = 0 - - window.clearInterval = () => { - clearCount++ - } - - var tr = transactionService.startTransaction('test', 'test', { - managed: true, - canReuse: true - }) - expect(transactionService.getCurrentTransaction()).toBe(tr) - expect(count).toBe(1) - expect(transactionService.respIntervalId).toBe(0) - - transactionService.startTransaction('test 1', 'test', { - managed: true, - canReuse: true - }) - expect(transactionService.getCurrentTransaction()).toBe(tr) - expect(tr.name).toBe('test 1') - expect(count).toBe(1) - - transactionService.startTransaction('test 2', 'test', { - managed: true, - canReuse: false - }) - expect(transactionService.getCurrentTransaction()).not.toBe(tr) - expect(count).toBe(1) - expect(clearCount).toBe(0) - tr = transactionService.getCurrentTransaction() - tr.end() - - callback() - expect(transactionService.respIntervalId).toBe(undefined) - expect(clearCount).toBe(1) - - window.clearInterval = origClearInterval - window.setInterval = origSetInterval - }) - it('should redefine type based on the defined order', () => { transactionService.startSpan('span 1', 'span-type') let tr = transactionService.getCurrentTransaction() @@ -662,6 +612,24 @@ describe('TransactionService', function() { expect(tr.type).toBe('custom-type') }) + it('should discard transaction if page has been hidden', async () => { + let { lastHiddenStart } = state + state.lastHiddenStart = performance.now() + 1000 + let tr = transactionService.startTransaction('test-name', 'test-type') + await tr.end() + expect(logger.debug).toHaveBeenCalledWith( + `transaction(${tr.id}, ${tr.name}, ${tr.type}) was discarded! The page was hidden during the transaction!` + ) + + state.lastHiddenStart = performance.now() - 1000 + tr = transactionService.startTransaction('test-name', 'test-type') + await tr.end() + expect(logger.debug).not.toHaveBeenCalledWith( + `transaction(${tr.id}, ${tr.name}, ${tr.type}) was discarded! The page was hidden during the transaction!` + ) + state.lastHiddenStart = lastHiddenStart + }) + describe('performance entry recorder', () => { const logger = new LoggingService() const config = new Config() From 00dad862d1eadf6a1465db144043d619b606a216 Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Tue, 30 Jun 2020 12:23:51 +0200 Subject: [PATCH 21/23] chore: remove compressed size gh workflow (#828) --- .github/workflows/bundle-size.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/bundle-size.yml diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml deleted file mode 100644 index d3241fee8..000000000 --- a/.github/workflows/bundle-size.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Compressed Size - -on: [pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - uses: preactjs/compressed-size-action@v1 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - pattern: "{packages/rum/dist/bundles/*.js}" From c8803822ed0aebbfe4a1a2456b9c110d979e7c44 Mon Sep 17 00:00:00 2001 From: Christian Haller <42344734+christianhaller3000@users.noreply.github.com> Date: Tue, 30 Jun 2020 18:33:46 +0200 Subject: [PATCH 22/23] docs: update set-up.asciidoc (#814) * Update set-up.asciidoc I think it should be in quotes * Update set-up.asciidoc --- docs/set-up.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/set-up.asciidoc b/docs/set-up.asciidoc index 7965d4e8c..b918a8b30 100644 --- a/docs/set-up.asciidoc +++ b/docs/set-up.asciidoc @@ -138,7 +138,7 @@ const replace = require('rollup-plugin-replace') plugins: [ replace({ - 'process.env.NODE_ENV': 'production' + 'process.env.NODE_ENV': JSON.stringify('production') }) ] ---- From 5983e712578c0fef2c3c46827c8aeb618e5a1bbf Mon Sep 17 00:00:00 2001 From: Vignesh Shanmugam Date: Wed, 1 Jul 2020 14:31:26 +0200 Subject: [PATCH 23/23] feat(rum-core): capture XHR/Fetch spans using resource timing (#825) * feat(rum-core): capture XHR/Fetch using resource timing * chore: add tests and update fixtures * chore: filter intake api from spans * chore: handle duplicate url fetch before patch * chore: add more tests for fitering logic * chore: modify the logic as per review * chore: fix test * chore: move to state object and fix test * chore: add guard for patch time * chore: address review --- packages/rum-core/src/bootstrap.js | 3 +- .../rum-core/src/common/patching/index.js | 4 +- .../capture-navigation.js | 101 +++++------- .../performance-monitoring.js | 4 +- packages/rum-core/src/state.js | 10 +- .../navigation-timing-span-snapshot.js | 89 +++++++---- .../test/fixtures/resource-entries.js | 148 +----------------- .../capture-navigation.spec.js | 70 ++++++++- .../performance-monitoring.spec.js | 9 ++ 9 files changed, 186 insertions(+), 252 deletions(-) diff --git a/packages/rum-core/src/bootstrap.js b/packages/rum-core/src/bootstrap.js index a3b448c83..573e0ce63 100644 --- a/packages/rum-core/src/bootstrap.js +++ b/packages/rum-core/src/bootstrap.js @@ -23,7 +23,7 @@ * */ -import { isPlatformSupported, isBrowser } from './common/utils' +import { isPlatformSupported, isBrowser, now } from './common/utils' import { patchAll } from './common/patching' import { state } from './state' @@ -32,6 +32,7 @@ export function bootstrap() { if (isPlatformSupported()) { patchAll() bootstrapPerf() + state.bootstrapTime = now() enabled = true } else if (isBrowser) { /** diff --git a/packages/rum-core/src/common/patching/index.js b/packages/rum-core/src/common/patching/index.js index bc33e6b34..e639cdd18 100644 --- a/packages/rum-core/src/common/patching/index.js +++ b/packages/rum-core/src/common/patching/index.js @@ -28,12 +28,11 @@ import { patchFetch } from './fetch-patch' import { patchHistory } from './history-patch' import { patchEventTarget } from './event-target-patch' import EventHandler from '../event-handler' - import { HISTORY, FETCH, XMLHTTPREQUEST, EVENT_TARGET } from '../constants' const patchEventHandler = new EventHandler() - let alreadyPatched = false + function patchAll() { if (!alreadyPatched) { alreadyPatched = true @@ -46,7 +45,6 @@ function patchAll() { patchHistory(function(event, task) { patchEventHandler.send(HISTORY, [event, task]) }) - patchEventTarget(function(event, task) { patchEventHandler.send(EVENT_TARGET, [event, task]) }) diff --git a/packages/rum-core/src/performance-monitoring/capture-navigation.js b/packages/rum-core/src/performance-monitoring/capture-navigation.js index 0d96eb4e6..bdf8e63f8 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 { state } from '../state' /** * Navigation Timing Spans @@ -119,60 +120,57 @@ function createResourceTimingSpan(resourceTimingEntry) { return span } -function createResourceTimingSpans(entries, filterUrls, trStart, trEnd) { +/** + * Checks if the span is already captured via XHR/Fetch patch by + * comparing the given resource startTime(fetchStart) aganist the + * patch code execution time. + */ +function isCapturedByPatching(resourceStartTime, requestPatchTime) { + return requestPatchTime != null && resourceStartTime > requestPatchTime +} + +/** + * Check if the given url matches APM Server's Intake + * API endpoint and ignore it from Spans + */ +function isIntakeAPIEndpoint(url) { + return /intake\/v\d+\/rum\/events/.test(url) +} + +function createResourceTimingSpans(entries, requestPatchTime, trStart, trEnd) { const spans = [] for (let i = 0; i < entries.length; i++) { - let { initiatorType, name, startTime, responseEnd } = entries[i] + const { initiatorType, name, startTime, responseEnd } = entries[i] /** - * Skipping the timing information of API calls because of auto patching XHR and Fetch + * Skip span creation if initiatorType is other than known types specified as part of RESOURCE_INITIATOR_TYPES + * The reason being, there are other types like embed, video, audio, navigation etc + * + * Check the below webplatform test to know more + * https://github.com/web-platform-tests/wpt/blob/b0020d5df18998609b38786878f7a0b92cc680aa/resource-timing/resource_initiator_types.html#L93 */ if ( - initiatorType === 'xmlhttprequest' || - initiatorType === 'fetch' || - !name + RESOURCE_INITIATOR_TYPES.indexOf(initiatorType) === -1 || + name == null ) { continue } + /** - * Create spans for all known resource initiator types + * Create Spans for API calls (XHR, Fetch) only if its not captured by the patch + * + * This would happen if our agent is downlaoded asyncrhonously and page does + * API requests before the agent patches the required modules. */ - if (RESOURCE_INITIATOR_TYPES.indexOf(initiatorType) !== -1) { - if (!shouldCreateSpan(startTime, responseEnd, trStart, trEnd)) { - continue - } - spans.push(createResourceTimingSpan(entries[i])) - } else { - /** - * Since IE does not support initiatorType in Resource timing entry, - * We have to manually filter the API calls from creating duplicate Spans - * - * Skip span creation if initiatorType is other than known types specified as part of RESOURCE_INITIATOR_TYPES - * The reason being, there are other types like embed, video, audio, navigation etc - * - * Check the below webplatform test to know more - * https://github.com/web-platform-tests/wpt/blob/b0020d5df18998609b38786878f7a0b92cc680aa/resource-timing/resource_initiator_types.html#L93 - */ - if (initiatorType != null) { - continue - } + if ( + (initiatorType === 'xmlhttprequest' || initiatorType === 'fetch') && + (isIntakeAPIEndpoint(name) || + isCapturedByPatching(startTime, requestPatchTime)) + ) { + continue + } - let foundAjaxReq = false - for (let j = 0; j < filterUrls.length; j++) { - const idx = name.lastIndexOf(filterUrls[j]) - if (idx > -1 && idx === name.length - filterUrls[j].length) { - foundAjaxReq = true - break - } - } - /** - * Create span if its not an ajax request - */ - if ( - !foundAjaxReq && - shouldCreateSpan(startTime, responseEnd, trStart, trEnd) - ) { - spans.push(createResourceTimingSpan(entries[i])) - } + if (shouldCreateSpan(startTime, responseEnd, trStart, trEnd)) { + spans.push(createResourceTimingSpan(entries[i])) } } return spans @@ -200,18 +198,6 @@ function createUserTimingSpans(entries, trStart, trEnd) { return userTimingSpans } -function getApiSpanNames({ spans }) { - const apiCalls = [] - - for (let i = 0; i < spans.length; i++) { - const span = spans[i] - if (span.type === 'external' && span.subtype === 'http') { - apiCalls.push(span.name.split(' ')[1]) - } - } - return apiCalls -} - /** * Navigation timing marks are reported only for page-load transactions * @@ -312,7 +298,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 @@ -356,11 +341,9 @@ function captureNavigation(transaction) { * Capture resource timing information as spans */ const resourceEntries = PERF.getEntriesByType(RESOURCE) - const apiCalls = getApiSpanNames(transaction) - createResourceTimingSpans( resourceEntries, - apiCalls, + state.bootstrapTime, trStart, trEnd ).forEach(span => transaction.spans.push(span)) diff --git a/packages/rum-core/src/performance-monitoring/performance-monitoring.js b/packages/rum-core/src/performance-monitoring/performance-monitoring.js index ded33772a..72b2ea5d4 100644 --- a/packages/rum-core/src/performance-monitoring/performance-monitoring.js +++ b/packages/rum-core/src/performance-monitoring/performance-monitoring.js @@ -27,8 +27,8 @@ import { checkSameOrigin, isDtHeaderValid, parseDtHeaderValue, - stripQueryStringFromUrl, - getDtHeaderValue + getDtHeaderValue, + stripQueryStringFromUrl } from '../common/utils' import Url from '../common/url' import { patchEventHandler } from '../common/patching' diff --git a/packages/rum-core/src/state.js b/packages/rum-core/src/state.js index b6047b935..4fe8b5329 100644 --- a/packages/rum-core/src/state.js +++ b/packages/rum-core/src/state.js @@ -24,7 +24,11 @@ */ const __DEV__ = process.env.NODE_ENV !== 'production' +const state = { + // Time when agent is bootstrapped and patching of modules happens + bootstrapTime: null, + // Time when the document is last backgrounded + lastHiddenStart: Number.MIN_SAFE_INTEGER +} -export { __DEV__ } - -export const state = {} +export { __DEV__, state } diff --git a/packages/rum-core/test/fixtures/navigation-timing-span-snapshot.js b/packages/rum-core/test/fixtures/navigation-timing-span-snapshot.js index 6b039b931..8df61d727 100644 --- a/packages/rum-core/test/fixtures/navigation-timing-span-snapshot.js +++ b/packages/rum-core/test/fixtures/navigation-timing-span-snapshot.js @@ -26,128 +26,149 @@ export default [ { name: 'http://beacon.test', - type: 'resource.beacon', - ended: true, + type: 'resource', + subtype: 'beacon', _end: 168.25, _start: 25.220000000000002 }, { name: 'http://testing.com', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 168.25, _start: 25.220000000000002 }, { name: 'http://localhost:9876/base/node_modules/karma-jasmine/lib/boot.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 172.8, _start: 25.385 }, { name: 'http://localhost:9876/base/node_modules/karma-jasmine/lib/adapter.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 174.04500000000002, _start: 25.515000000000004 }, { name: 'http://localhost:9876/base/tmp/globals.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 175.33, _start: 25.640000000000004 }, { name: 'http://localhost:9876/base/node_modules/elastic-apm-js-zone/dist/zone.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 176.12000000000003, _start: 25.76 }, { name: 'http://localhost:9876/base/test/utils/polyfill.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 176.865, _start: 25.880000000000003 }, { name: 'http://localhost:9876/base/test/common/apm-server.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 340.65500000000003, _start: 26.000000000000004 }, { name: 'http://localhost:9876/base/test/common/config-service.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 178.85000000000002, _start: 26.150000000000002 }, { name: 'http://localhost:9876/base/test/common/service-factory.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 180.935, _start: 26.285000000000004 }, { name: 'http://localhost:9876/base/test/common/utils.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 181.735, _start: 26.405 }, { name: 'http://localhost:9876/base/test/error-logging/error-logging.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 349.685, _start: 26.520000000000003 }, { name: 'http://localhost:9876/base/test/error-logging/stack-trace-service.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 350.44000000000005, _start: 26.635 }, { name: 'http://localhost:9876/base/test/performance-monitoring/performance-monitoring.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 351.72, _start: 26.755000000000003 }, { name: 'http://localhost:9876/base/test/performance-monitoring/transaction-service.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 257.22, _start: 26.875000000000004 }, { name: 'http://localhost:9876/base/test/performance-monitoring/transaction.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 187.60500000000002, _start: 27.035000000000004 }, { name: 'http://localhost:9876/base/test/performance-monitoring/zone-service.spec.js', - type: 'resource.script', - ended: true, + type: 'resource', + subtype: 'script', _end: 188.72000000000003, _start: 27.180000000000003 + }, + { + name: 'http://non-existing.com/v1/client-side/transactions', + type: 'resource', + subtype: 'xmlhttprequest', + _end: 857.8950000000001, + _start: 707.23 + }, + { + name: 'http://localhost:8200/v1/client-side/errors', + type: 'resource', + subtype: 'xmlhttprequest', + _end: 1625.49, + _start: 1619.825 + }, + { + name: 'http://localhost:8200/v1/client-side/transactions', + type: 'resource', + subtype: 'fetch', + _end: 2832.545, + _start: 2796.3600000000006 } ] diff --git a/packages/rum-core/test/fixtures/resource-entries.js b/packages/rum-core/test/fixtures/resource-entries.js index e089faade..1225bc9f9 100644 --- a/packages/rum-core/test/fixtures/resource-entries.js +++ b/packages/rum-core/test/fixtures/resource-entries.js @@ -549,132 +549,12 @@ export default [ decodedBodySize: 0, serverTiming: [] }, - { - name: 'http://localhost:8200/v1/client-side/errors', - entryType: 'resource', - startTime: 1625.88, - duration: 4.135000000000218, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 1625.88, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 1630.0150000000003, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, - { - name: 'http://localhost:8200/v1/client-side/errors', - entryType: 'resource', - startTime: 1633.93, - duration: 5.349999999999909, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 1633.93, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 1639.28, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, - { - name: 'http://localhost:8200/v1/client-side/errors', - entryType: 'resource', - startTime: 1639.69, - duration: 6.430000000000064, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 1639.69, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 1646.1200000000001, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, - { - name: 'http://localhost:8200/v1/client-side/errors', - entryType: 'resource', - startTime: 1648.775, - duration: 8.569999999999936, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 1648.775, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 1657.345, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, - { - name: 'http://localhost:8200/v1/client-side/errors', - entryType: 'resource', - startTime: 1657.7600000000002, - duration: 8.294999999999845, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 1657.7600000000002, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 1666.055, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, { name: 'http://localhost:8200/v1/client-side/transactions', entryType: 'resource', startTime: 2796.3600000000006, duration: 36.18499999999949, - initiatorType: 'xmlhttprequest', + initiatorType: 'fetch', nextHopProtocol: 'http/1.1', workerStart: 0, redirectStart: 0, @@ -688,31 +568,7 @@ export default [ requestStart: 0, responseStart: 0, responseEnd: 2832.545, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [] - }, - { - name: 'http://localhost:8200/v1/client-side/transactions', - entryType: 'resource', - startTime: 2833.295, - duration: 5.945000000000164, - initiatorType: 'xmlhttprequest', - nextHopProtocol: 'http/1.1', - workerStart: 0, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 2833.295, - domainLookupStart: 0, - domainLookupEnd: 0, - connectStart: 0, - connectEnd: 0, - secureConnectionStart: 0, - requestStart: 0, - responseStart: 0, - responseEnd: 2839.2400000000002, - transferSize: 0, + transferSize: 3805, encodedBodySize: 0, decodedBodySize: 0, serverTiming: [] 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 0f5b02c99..93c1e3122 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 ) @@ -164,9 +164,9 @@ describe('Capture hard navigation', function() { http: { url: jasmine.any(String), response: { - transfer_size: 420580, - encoded_body_size: 420379, - decoded_body_size: 420379 + transfer_size: 3805, + encoded_body_size: 0, + decoded_body_size: 0 } } }) @@ -175,6 +175,68 @@ describe('Capture hard navigation', function() { expect(spans.map(mapSpan)).toEqual(spanSnapshot) }) + it('should filter intake API calls from resource timing', function() { + const entries = [ + { + name: 'http://localhost:8200/intake/v2/rum/events', + initiatorType: 'fetch', + entryType: 'resource', + startTime: 25, + responseEnd: 120 + }, + { + name: 'http://apm-server:8200/intake/v3/rum/events', + initiatorType: 'xmlhttprequest', + entryType: 'resource', + startTime: 100, + responseEnd: 150 + } + ] + const spans = createResourceTimingSpans( + entries, + 150, + transactionStart, + transactionEnd + ) + expect(spans).toEqual([]) + }) + + it('should add/filter XHR/Fetch spans from RT data based on patch time', function() { + const entries = [ + { + name: 'http://localhost:8000/fetch', + initiatorType: 'fetch', + entryType: 'resource', + startTime: 25, + responseEnd: 120 + }, + { + name: 'http://localhost:8000/data?query=foo', + initiatorType: 'xmlhttprequest', + entryType: 'resource', + startTime: 100, + responseEnd: 150 + } + ] + const getCount = requestPatchTime => + createResourceTimingSpans( + entries, + requestPatchTime, + transactionStart, + transactionEnd + ).length + + expect(getCount(null)).toBe(2) + // same time as start of 1st resource + expect(getCount(25)).toBe(1) + // after first res start time + expect(getCount(30)).toBe(1) + // after both resources + expect(getCount(101)).toBe(2) + // before both resources + expect(getCount(10)).toBe(0) + }) + it('should createUserTimingSpans', function() { const spans = createUserTimingSpans( userTimingEntries, diff --git a/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js b/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js index e6658213d..495c4c6ba 100644 --- a/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js +++ b/packages/rum-core/test/performance-monitoring/performance-monitoring.spec.js @@ -46,6 +46,7 @@ import { TRANSACTION_END, TRANSACTIONS } from '../../src/common/constants' +import { state } from '../../src/state' import patchEventHandler from '../common/patch' import { mockGetEntriesByType } from '../utils/globals-mock' import resourceEntries from '../fixtures/resource-entries' @@ -300,6 +301,9 @@ describe('PerformanceMonitoring', function() { if (window.fetch) { it('should create fetch spans', function(done) { + const origBootstrapTime = state.bootstrapTime + // ignore capturing resource timing spans + state.bootstrapTime = 0 var fn = performanceMonitoring.getFetchSub() var dTHeaderValue const cancelFetchSub = patchEventHandler.observe(FETCH, function( @@ -343,6 +347,7 @@ describe('PerformanceMonitoring', function() { }) expect(dTHeaderValue).toBeDefined() cancelFetchSub() + state.bootstrapTime = origBootstrapTime done() }) }) @@ -372,6 +377,9 @@ describe('PerformanceMonitoring', function() { }) it('should not duplicate xhr spans if fetch is a polyfill', function(done) { + const origBootstrapTime = state.bootstrapTime + // ignore capturing resource timing spans + state.bootstrapTime = 0 const xhrFn = performanceMonitoring.getXHRSub() const fetchFn = performanceMonitoring.getFetchSub() @@ -449,6 +457,7 @@ describe('PerformanceMonitoring', function() { ]) cancelXHRSub() cancelFetchSub() + state.bootstrapTime = origBootstrapTime done() }) })