@newrelic/browser-agent 0.0.9 → 0.1.229
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -207
- package/dist/cjs/cdn/lite.js +14 -0
- package/dist/cjs/cdn/polyfills/lite.js +4 -0
- package/dist/cjs/cdn/polyfills/pro.js +4 -0
- package/dist/cjs/cdn/polyfills/spa.js +4 -0
- package/dist/cjs/cdn/polyfills.js +14 -0
- package/dist/cjs/cdn/pro.js +19 -0
- package/dist/cjs/cdn/spa.js +19 -0
- package/dist/cjs/cdn/worker.js +16 -0
- package/dist/cjs/common/aggregate/aggregator.js +168 -0
- package/dist/cjs/common/browser-version/firefox-version.js +17 -0
- package/dist/cjs/common/browser-version/ios-version.js +18 -0
- package/dist/cjs/common/config/config.js +76 -0
- package/dist/cjs/common/config/state/configurable.js +32 -0
- package/dist/cjs/common/config/state/info.js +50 -0
- package/dist/cjs/common/config/state/init.js +86 -0
- package/dist/cjs/common/config/state/loader-config.js +28 -0
- package/dist/cjs/common/config/state/originals.js +9 -0
- package/dist/cjs/common/config/state/runtime.js +50 -0
- package/dist/cjs/common/constants/environment-variables.js +20 -0
- package/dist/cjs/common/context/shared-context.js +25 -0
- package/dist/cjs/common/deny-list/deny-list.js +108 -0
- package/dist/cjs/common/drain/drain.js +126 -0
- package/dist/cjs/common/event-emitter/contextual-ee.js +149 -0
- package/dist/cjs/common/event-emitter/handle.js +24 -0
- package/dist/cjs/common/event-emitter/register-handler.js +24 -0
- package/dist/cjs/common/event-listener/event-listener-opts.js +46 -0
- package/dist/cjs/common/harvest/harvest-scheduler.js +111 -0
- package/dist/cjs/common/harvest/harvest.js +236 -0
- package/dist/cjs/common/ids/id.js +30 -0
- package/dist/cjs/common/ids/unique-id.js +84 -0
- package/dist/cjs/common/metrics/framework-detection.js +72 -0
- package/dist/cjs/common/metrics/paint-metrics.js +13 -0
- package/dist/cjs/common/serialize/bel-serializer.js +89 -0
- package/dist/cjs/common/timing/nav-timing.js +77 -0
- package/dist/cjs/common/timing/now.js +15 -0
- package/dist/cjs/common/unload/eol.js +69 -0
- package/dist/cjs/common/url/clean-url.js +16 -0
- package/dist/cjs/common/url/encode.js +79 -0
- package/dist/cjs/common/url/location.js +14 -0
- package/dist/cjs/common/url/parse-url.js +66 -0
- package/dist/cjs/common/url/protocol.js +25 -0
- package/dist/cjs/common/util/console.js +17 -0
- package/dist/cjs/common/util/data-size.js +25 -0
- package/dist/cjs/common/util/feature-flags.js +42 -0
- package/dist/cjs/common/util/get-or-set.js +39 -0
- package/dist/cjs/common/util/global-scope.js +56 -0
- package/dist/cjs/common/util/map-own.js +24 -0
- package/dist/cjs/common/util/obfuscate.js +76 -0
- package/dist/cjs/common/util/reduce.js +22 -0
- package/dist/cjs/common/util/s-hash.js +19 -0
- package/dist/cjs/common/util/single.js +23 -0
- package/dist/cjs/common/util/stringify.js +47 -0
- package/dist/cjs/common/util/submit-data.js +99 -0
- package/dist/cjs/common/util/traverse.js +41 -0
- package/dist/cjs/common/util/user-agent.js +57 -0
- package/dist/cjs/common/window/load.js +19 -0
- package/dist/cjs/common/window/nreum.js +107 -0
- package/dist/cjs/common/window/page-visibility.js +28 -0
- package/dist/cjs/common/window/session-storage.js +42 -0
- package/dist/cjs/common/window/supports-performance-observer.js +15 -0
- package/dist/cjs/common/window/top-level-callers.js +23 -0
- package/dist/cjs/common/wrap/index.js +68 -0
- package/dist/cjs/common/wrap/wrap-events.js +105 -0
- package/dist/cjs/common/wrap/wrap-fetch.js +114 -0
- package/dist/cjs/common/wrap/wrap-function.js +269 -0
- package/dist/cjs/common/wrap/wrap-history.js +56 -0
- package/dist/cjs/common/wrap/wrap-jsonp.js +129 -0
- package/dist/cjs/common/wrap/wrap-mutation.js +61 -0
- package/dist/cjs/common/wrap/wrap-promise.js +160 -0
- package/dist/cjs/common/wrap/wrap-raf.js +55 -0
- package/dist/cjs/common/wrap/wrap-timer.js +70 -0
- package/dist/cjs/common/wrap/wrap-xhr.js +206 -0
- package/dist/cjs/features/ajax/aggregate/index.js +226 -0
- package/dist/cjs/features/ajax/constants.js +9 -0
- package/dist/cjs/features/ajax/index.js +12 -0
- package/dist/cjs/features/ajax/instrument/distributed-tracing.js +145 -0
- package/dist/cjs/features/ajax/instrument/index.js +338 -0
- package/dist/cjs/features/ajax/instrument/response-size.js +26 -0
- package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +18 -0
- package/dist/cjs/features/jserrors/aggregate/canonical-function-name.test.js +30 -0
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +216 -0
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +257 -0
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +36 -0
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.test.js +39 -0
- package/dist/cjs/features/jserrors/aggregate/index.js +267 -0
- package/dist/cjs/features/jserrors/aggregate/string-hash-code.js +23 -0
- package/dist/cjs/features/jserrors/aggregate/string-hash-code.test.js +26 -0
- package/dist/cjs/features/jserrors/constants.js +11 -0
- package/dist/cjs/features/jserrors/index.js +12 -0
- package/dist/cjs/features/jserrors/instrument/debug.js +40 -0
- package/dist/cjs/features/jserrors/instrument/index.js +158 -0
- package/dist/cjs/features/metrics/aggregate/index.js +136 -0
- package/dist/cjs/features/metrics/constants.js +17 -0
- package/dist/cjs/features/metrics/index.js +12 -0
- package/dist/cjs/features/metrics/instrument/index.js +20 -0
- package/dist/cjs/features/metrics/instrument/workers-helper.js +121 -0
- package/dist/cjs/features/page_action/aggregate/index.js +112 -0
- package/dist/cjs/features/page_action/constants.js +9 -0
- package/dist/cjs/features/page_action/index.js +12 -0
- package/dist/cjs/features/page_action/instrument/index.js +21 -0
- package/dist/cjs/features/page_view_event/aggregate/index.js +133 -0
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +39 -0
- package/dist/cjs/features/page_view_event/constants.js +15 -0
- package/dist/cjs/features/page_view_event/index.js +12 -0
- package/dist/cjs/features/page_view_event/instrument/index.js +38 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +265 -0
- package/dist/cjs/features/page_view_timing/constants.js +9 -0
- package/dist/cjs/features/page_view_timing/first-paint.js +50 -0
- package/dist/cjs/features/page_view_timing/index.js +12 -0
- package/dist/cjs/features/page_view_timing/instrument/index.js +36 -0
- package/dist/cjs/features/page_view_timing/long-tasks.js +75 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +375 -0
- package/dist/cjs/features/session_trace/constants.js +32 -0
- package/dist/cjs/features/session_trace/index.js +12 -0
- package/dist/cjs/features/session_trace/instrument/index.js +133 -0
- package/dist/cjs/features/spa/aggregate/index.js +684 -0
- package/dist/cjs/features/spa/aggregate/interaction-node.js +84 -0
- package/dist/cjs/features/spa/aggregate/interaction-node.test.js +16 -0
- package/dist/cjs/features/spa/aggregate/interaction.js +98 -0
- package/dist/cjs/features/spa/aggregate/serializer.js +147 -0
- package/dist/cjs/features/spa/constants.js +53 -0
- package/dist/cjs/features/spa/index.js +12 -0
- package/dist/cjs/features/spa/instrument/index.js +114 -0
- package/dist/cjs/features/utils/aggregate-base.js +13 -0
- package/dist/cjs/features/utils/feature-base.js +58 -0
- package/dist/cjs/features/utils/handler-cache.js +64 -0
- package/dist/cjs/features/utils/instrument-base.js +71 -0
- package/dist/cjs/features/utils/lazy-loader.js +44 -0
- package/dist/cjs/index.js +81 -58
- package/dist/cjs/loaders/agent.js +86 -0
- package/dist/cjs/loaders/api/api.js +109 -0
- package/dist/cjs/loaders/api/apiAsync.js +94 -0
- package/dist/cjs/loaders/browser-agent.js +29 -0
- package/dist/cjs/loaders/configure/configure.js +47 -0
- package/dist/cjs/loaders/features/enabled-features.js +19 -0
- package/dist/cjs/loaders/features/featureDependencies.js +32 -0
- package/dist/cjs/loaders/features/features.js +33 -0
- package/dist/cjs/loaders/micro-agent.js +93 -0
- package/dist/cjs/loaders/worker-agent.js +24 -0
- package/dist/esm/cdn/lite.js +12 -0
- package/dist/esm/cdn/polyfills/lite.js +7 -0
- package/dist/esm/cdn/polyfills/pro.js +7 -0
- package/dist/esm/cdn/polyfills/spa.js +7 -0
- package/dist/esm/cdn/polyfills.js +17 -0
- package/dist/esm/cdn/pro.js +17 -0
- package/dist/esm/cdn/spa.js +17 -0
- package/dist/esm/cdn/worker.js +14 -0
- package/dist/esm/common/aggregate/aggregator.js +161 -0
- package/dist/esm/common/browser-version/firefox-version.js +10 -0
- package/dist/esm/common/browser-version/ios-version.js +10 -0
- package/dist/esm/common/config/config.js +11 -0
- package/dist/esm/common/config/state/configurable.js +25 -0
- package/dist/esm/common/config/state/info.js +42 -0
- package/dist/esm/common/config/state/init.js +78 -0
- package/dist/esm/common/config/state/loader-config.js +21 -0
- package/dist/esm/common/config/state/originals.js +2 -0
- package/dist/esm/common/config/state/runtime.js +41 -0
- package/dist/esm/common/constants/environment-variables.js +11 -0
- package/dist/esm/common/context/shared-context.js +18 -0
- package/dist/esm/common/deny-list/deny-list.js +101 -0
- package/dist/esm/common/drain/drain.js +119 -0
- package/dist/esm/common/event-emitter/contextual-ee.js +142 -0
- package/dist/esm/common/event-emitter/handle.js +16 -0
- package/dist/esm/common/event-emitter/register-handler.js +19 -0
- package/dist/esm/common/event-listener/event-listener-opts.js +39 -0
- package/dist/esm/common/harvest/harvest-scheduler.js +105 -0
- package/dist/esm/common/harvest/harvest.js +228 -0
- package/dist/esm/common/ids/id.js +23 -0
- package/dist/esm/common/ids/unique-id.js +75 -0
- package/dist/esm/common/metrics/framework-detection.js +66 -0
- package/dist/esm/common/metrics/paint-metrics.js +6 -0
- package/dist/esm/common/serialize/bel-serializer.js +80 -0
- package/dist/esm/common/timing/nav-timing.js +67 -0
- package/dist/esm/common/timing/now.js +9 -0
- package/dist/esm/common/unload/eol.js +62 -0
- package/dist/esm/common/url/clean-url.js +10 -0
- package/dist/esm/common/url/encode.js +71 -0
- package/dist/esm/common/url/location.js +8 -0
- package/dist/esm/common/url/parse-url.js +60 -0
- package/dist/esm/common/url/protocol.js +17 -0
- package/dist/esm/common/util/console.js +11 -0
- package/dist/esm/common/util/data-size.js +19 -0
- package/dist/esm/common/util/feature-flags.js +33 -0
- package/dist/esm/common/util/get-or-set.js +33 -0
- package/dist/esm/common/util/global-scope.js +44 -0
- package/dist/esm/common/util/map-own.js +18 -0
- package/dist/esm/common/util/obfuscate.js +67 -0
- package/dist/esm/common/util/reduce.js +16 -0
- package/dist/esm/common/util/s-hash.js +13 -0
- package/dist/esm/common/util/single.js +16 -0
- package/dist/esm/common/util/stringify.js +42 -0
- package/dist/esm/common/util/submit-data.js +91 -0
- package/dist/esm/common/util/traverse.js +35 -0
- package/dist/esm/common/util/user-agent.js +48 -0
- package/dist/esm/common/window/load.js +12 -0
- package/dist/esm/common/window/nreum.js +91 -0
- package/dist/esm/common/window/page-visibility.js +23 -0
- package/dist/esm/common/window/session-storage.js +36 -0
- package/dist/esm/common/window/supports-performance-observer.js +9 -0
- package/dist/esm/common/window/top-level-callers.js +17 -0
- package/dist/esm/common/wrap/index.js +14 -0
- package/dist/esm/common/wrap/wrap-events.js +97 -0
- package/dist/esm/common/wrap/wrap-fetch.js +105 -0
- package/dist/esm/common/wrap/wrap-function.js +257 -0
- package/dist/esm/common/wrap/wrap-history.js +48 -0
- package/dist/esm/common/wrap/wrap-jsonp.js +122 -0
- package/dist/esm/common/wrap/wrap-mutation.js +54 -0
- package/dist/esm/common/wrap/wrap-promise.js +153 -0
- package/dist/esm/common/wrap/wrap-raf.js +48 -0
- package/dist/esm/common/wrap/wrap-timer.js +63 -0
- package/dist/esm/common/wrap/wrap-xhr.js +199 -0
- package/dist/esm/features/ajax/aggregate/index.js +218 -0
- package/dist/esm/features/ajax/constants.js +2 -0
- package/dist/esm/features/ajax/index.js +1 -0
- package/dist/esm/features/ajax/instrument/distributed-tracing.js +137 -0
- package/dist/esm/features/ajax/instrument/index.js +330 -0
- package/dist/esm/features/ajax/instrument/response-size.js +19 -0
- package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -0
- package/dist/esm/features/jserrors/aggregate/canonical-function-name.test.js +28 -0
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +209 -0
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +255 -0
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +29 -0
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.test.js +37 -0
- package/dist/esm/features/jserrors/aggregate/index.js +260 -0
- package/dist/esm/features/jserrors/aggregate/string-hash-code.js +17 -0
- package/dist/esm/features/jserrors/aggregate/string-hash-code.test.js +24 -0
- package/dist/esm/features/jserrors/constants.js +3 -0
- package/dist/esm/features/jserrors/index.js +1 -0
- package/dist/esm/features/jserrors/instrument/debug.js +38 -0
- package/dist/esm/features/jserrors/instrument/index.js +150 -0
- package/dist/esm/features/metrics/aggregate/index.js +129 -0
- package/dist/esm/features/metrics/constants.js +6 -0
- package/dist/esm/features/metrics/index.js +1 -0
- package/dist/esm/features/metrics/instrument/index.js +13 -0
- package/dist/esm/features/metrics/instrument/workers-helper.js +116 -0
- package/dist/esm/features/page_action/aggregate/index.js +105 -0
- package/dist/esm/features/page_action/constants.js +2 -0
- package/dist/esm/features/page_action/index.js +1 -0
- package/dist/esm/features/page_action/instrument/index.js +14 -0
- package/dist/esm/features/page_view_event/aggregate/index.js +124 -0
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +34 -0
- package/dist/esm/features/page_view_event/constants.js +5 -0
- package/dist/esm/features/page_view_event/index.js +1 -0
- package/dist/esm/features/page_view_event/instrument/index.js +29 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +258 -0
- package/dist/esm/features/page_view_timing/constants.js +2 -0
- package/dist/esm/features/page_view_timing/first-paint.js +43 -0
- package/dist/esm/features/page_view_timing/index.js +1 -0
- package/dist/esm/features/page_view_timing/instrument/index.js +28 -0
- package/dist/esm/features/page_view_timing/long-tasks.js +69 -0
- package/dist/esm/features/session_trace/aggregate/index.js +366 -0
- package/dist/esm/features/session_trace/constants.js +14 -0
- package/dist/esm/features/session_trace/index.js +1 -0
- package/dist/esm/features/session_trace/instrument/index.js +123 -0
- package/dist/esm/features/spa/aggregate/index.js +674 -0
- package/dist/esm/features/spa/aggregate/interaction-node.js +78 -0
- package/dist/esm/features/spa/aggregate/interaction-node.test.js +14 -0
- package/dist/esm/features/spa/aggregate/interaction.js +92 -0
- package/dist/esm/features/spa/aggregate/serializer.js +139 -0
- package/dist/esm/features/spa/constants.js +25 -0
- package/dist/esm/features/spa/index.js +1 -0
- package/dist/esm/features/spa/instrument/index.js +104 -0
- package/dist/esm/features/utils/aggregate-base.js +6 -0
- package/dist/esm/features/utils/feature-base.js +51 -0
- package/dist/esm/features/utils/handler-cache.js +57 -0
- package/dist/esm/features/utils/instrument-base.js +69 -0
- package/dist/esm/features/utils/lazy-loader.js +37 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/loaders/agent.js +77 -0
- package/dist/esm/loaders/api/api.js +104 -0
- package/dist/esm/loaders/api/apiAsync.js +88 -0
- package/dist/esm/loaders/browser-agent.js +23 -0
- package/dist/esm/loaders/configure/configure.js +41 -0
- package/dist/esm/loaders/features/enabled-features.js +13 -0
- package/dist/esm/loaders/features/featureDependencies.js +25 -0
- package/dist/esm/loaders/features/features.js +25 -0
- package/dist/esm/loaders/micro-agent.js +86 -0
- package/dist/esm/loaders/worker-agent.js +18 -0
- package/package.json +204 -71
- package/types.ts +221 -0
- package/dist/bundled/es5/index.js +0 -2
- package/dist/bundled/es5/index.js.map +0 -1
- package/dist/bundled/es6/index.js +0 -2
- package/dist/bundled/es6/index.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -19
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/types.d.ts +0 -94
- package/dist/cjs/types.js +0 -28
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/utils/api/api.d.ts +0 -10
- package/dist/cjs/utils/api/api.js +0 -40
- package/dist/cjs/utils/api/api.js.map +0 -1
- package/dist/cjs/utils/config/build-configs.d.ts +0 -6
- package/dist/cjs/utils/config/build-configs.js +0 -68
- package/dist/cjs/utils/config/build-configs.js.map +0 -1
- package/dist/cjs/utils/features/features.d.ts +0 -5
- package/dist/cjs/utils/features/features.js +0 -14
- package/dist/cjs/utils/features/features.js.map +0 -1
- package/dist/cjs/utils/features/initialize.d.ts +0 -5
- package/dist/cjs/utils/features/initialize.js +0 -51
- package/dist/cjs/utils/features/initialize.js.map +0 -1
- package/dist/es/index.d.ts +0 -19
- package/dist/es/index.js +0 -55
- package/dist/es/index.js.map +0 -1
- package/dist/es/types.d.ts +0 -94
- package/dist/es/types.js +0 -24
- package/dist/es/types.js.map +0 -1
- package/dist/es/utils/api/api.d.ts +0 -10
- package/dist/es/utils/api/api.js +0 -36
- package/dist/es/utils/api/api.js.map +0 -1
- package/dist/es/utils/config/build-configs.d.ts +0 -6
- package/dist/es/utils/config/build-configs.js +0 -64
- package/dist/es/utils/config/build-configs.js.map +0 -1
- package/dist/es/utils/features/features.d.ts +0 -5
- package/dist/es/utils/features/features.js +0 -10
- package/dist/es/utils/features/features.js.map +0 -1
- package/dist/es/utils/features/initialize.d.ts +0 -5
- package/dist/es/utils/features/initialize.js +0 -24
- package/dist/es/utils/features/initialize.js.map +0 -1
- package/dist/umd/index.d.ts +0 -19
- package/dist/umd/index.js +0 -69
- package/dist/umd/index.js.map +0 -1
- package/dist/umd/types.d.ts +0 -94
- package/dist/umd/types.js +0 -38
- package/dist/umd/types.js.map +0 -1
- package/dist/umd/utils/api/api.d.ts +0 -10
- package/dist/umd/utils/api/api.js +0 -50
- package/dist/umd/utils/api/api.js.map +0 -1
- package/dist/umd/utils/config/build-configs.d.ts +0 -6
- package/dist/umd/utils/config/build-configs.js +0 -78
- package/dist/umd/utils/config/build-configs.js.map +0 -1
- package/dist/umd/utils/features/features.d.ts +0 -5
- package/dist/umd/utils/features/features.js +0 -24
- package/dist/umd/utils/features/features.js.map +0 -1
- package/dist/umd/utils/features/initialize.d.ts +0 -5
- package/dist/umd/utils/features/initialize.js +0 -63
- package/dist/umd/utils/features/initialize.js.map +0 -1
- package/dist/webpack-analysis.html +0 -38
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
7
|
+
import { now } from '../../../common/timing/now';
|
|
8
|
+
import { getOrSet } from '../../../common/util/get-or-set';
|
|
9
|
+
import { wrapRaf, wrapTimer, wrapEvents, wrapXhr } from '../../../common/wrap';
|
|
10
|
+
import slice from 'lodash._slice';
|
|
11
|
+
import './debug';
|
|
12
|
+
import { InstrumentBase } from '../../utils/instrument-base';
|
|
13
|
+
import { FEATURE_NAME, NR_ERR_PROP } from '../constants';
|
|
14
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
15
|
+
import { globalScope } from '../../../common/util/global-scope';
|
|
16
|
+
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
|
|
17
|
+
import { getRuntime } from '../../../common/config/config';
|
|
18
|
+
import { stringify } from '../../../common/util/stringify';
|
|
19
|
+
export class Instrument extends InstrumentBase {
|
|
20
|
+
static featureName = FEATURE_NAME;
|
|
21
|
+
constructor(agentIdentifier, aggregator, auto = true) {
|
|
22
|
+
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
23
|
+
// skipNext counter to keep track of uncaught
|
|
24
|
+
// errors that will be the same as caught errors.
|
|
25
|
+
this.skipNext = 0;
|
|
26
|
+
this.origOnerror = globalScope.onerror;
|
|
27
|
+
try {
|
|
28
|
+
this.removeOnAbort = new AbortController();
|
|
29
|
+
} // this try-catch can be removed when IE11 is completely unsupported & gone
|
|
30
|
+
catch (e) {}
|
|
31
|
+
const thisInstrument = this;
|
|
32
|
+
thisInstrument.ee.on('fn-start', function (args, obj, methodName) {
|
|
33
|
+
if (thisInstrument.abortHandler) thisInstrument.skipNext += 1;
|
|
34
|
+
});
|
|
35
|
+
thisInstrument.ee.on('fn-err', function (args, obj, err) {
|
|
36
|
+
if (thisInstrument.abortHandler && !err[NR_ERR_PROP]) {
|
|
37
|
+
getOrSet(err, NR_ERR_PROP, function getVal() {
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
this.thrown = true;
|
|
41
|
+
notice(err, undefined, thisInstrument.ee);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
thisInstrument.ee.on('fn-end', function () {
|
|
45
|
+
if (!thisInstrument.abortHandler) return;
|
|
46
|
+
if (!this.thrown && thisInstrument.skipNext > 0) thisInstrument.skipNext -= 1;
|
|
47
|
+
});
|
|
48
|
+
thisInstrument.ee.on('internal-error', function (e) {
|
|
49
|
+
handle('ierr', [e, now(), true], undefined, FEATURE_NAMES.jserrors, thisInstrument.ee);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Tack on our error handler onto the existing global one.
|
|
53
|
+
globalScope.onerror = (...args) => {
|
|
54
|
+
if (this.origOnerror) this.origOnerror(...args);
|
|
55
|
+
this.onerrorHandler(...args);
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
globalScope.addEventListener('unhandledrejection', e => {
|
|
59
|
+
/** rejections can contain data of any type -- this is an effort to keep the message human readable */
|
|
60
|
+
const err = castReasonToError(e.reason);
|
|
61
|
+
handle('err', [err, now(), false, {
|
|
62
|
+
unhandledPromiseRejection: 1
|
|
63
|
+
}], undefined, FEATURE_NAMES.jserrors, this.ee);
|
|
64
|
+
}, eventListenerOpts(false, this.removeOnAbort?.signal));
|
|
65
|
+
wrapRaf(this.ee);
|
|
66
|
+
wrapTimer(this.ee);
|
|
67
|
+
wrapEvents(this.ee);
|
|
68
|
+
if (getRuntime(agentIdentifier).xhrWrappable) wrapXhr(this.ee);
|
|
69
|
+
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
70
|
+
this.importAggregator();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Restoration and resource release tasks to be done if JS error loader is being aborted. Unwind changes to globals. */
|
|
74
|
+
#abort() {
|
|
75
|
+
this.removeOnAbort?.abort();
|
|
76
|
+
this.abortHandler = undefined; // weakly allow this abort op to run only once
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* FF and Android browsers do not provide error info to the 'error' event callback,
|
|
81
|
+
* so we must use window.onerror
|
|
82
|
+
* @param {string} message
|
|
83
|
+
* @param {string} filename
|
|
84
|
+
* @param {number} lineno
|
|
85
|
+
* @param {number} column
|
|
86
|
+
* @param {Error | *} errorObj
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
onerrorHandler(message, filename, lineno, column, errorObj) {
|
|
90
|
+
try {
|
|
91
|
+
if (this.skipNext) this.skipNext -= 1;else notice(errorObj || new UncaughtException(message, filename, lineno), true, this.ee);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
try {
|
|
94
|
+
handle('ierr', [e, now(), true], undefined, FEATURE_NAMES.jserrors, this.ee);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
// do nothing
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (typeof this.origOnerror === 'function') return this.origOnerror.apply(this, slice(arguments));
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param {string} message
|
|
107
|
+
* @param {string} filename
|
|
108
|
+
* @param {number} lineno
|
|
109
|
+
*/
|
|
110
|
+
function UncaughtException(message, filename, lineno) {
|
|
111
|
+
this.message = message || 'Uncaught error with no additional information';
|
|
112
|
+
this.sourceURL = filename;
|
|
113
|
+
this.line = lineno;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Adds a timestamp and emits the 'err' event, which the error aggregator listens for
|
|
118
|
+
* @param {Error} err
|
|
119
|
+
* @param {boolean} doNotStamp
|
|
120
|
+
* @param {ContextualEE} ee
|
|
121
|
+
*/
|
|
122
|
+
function notice(err, doNotStamp, ee) {
|
|
123
|
+
// by default add timestamp, unless specifically told not to
|
|
124
|
+
// this is to preserve existing behavior
|
|
125
|
+
var time = !doNotStamp ? now() : null;
|
|
126
|
+
handle('err', [err, time], undefined, FEATURE_NAMES.jserrors, ee);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Attempts to cast an unhandledPromiseRejection reason (reject(...)) to an Error object
|
|
131
|
+
* @param {*} reason - The reason property from an unhandled promise rejection
|
|
132
|
+
* @returns {Error} - An Error object with the message as the casted reason
|
|
133
|
+
*/
|
|
134
|
+
function castReasonToError(reason) {
|
|
135
|
+
let prefix = 'Unhandled Promise Rejection: ';
|
|
136
|
+
if (reason instanceof Error) {
|
|
137
|
+
try {
|
|
138
|
+
reason.message = prefix + reason.message;
|
|
139
|
+
return reason;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return reason;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (typeof reason === 'undefined') return new Error(prefix);
|
|
145
|
+
try {
|
|
146
|
+
return new Error(prefix + stringify(reason));
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return new Error(prefix);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getRuntime } from '../../../common/config/config';
|
|
2
|
+
import { registerHandler } from '../../../common/event-emitter/register-handler';
|
|
3
|
+
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
4
|
+
import { AggregateBase } from '../../utils/aggregate-base';
|
|
5
|
+
import { FEATURE_NAME, SUPPORTABILITY_METRIC, CUSTOM_METRIC, SUPPORTABILITY_METRIC_CHANNEL, CUSTOM_METRIC_CHANNEL } from '../constants';
|
|
6
|
+
import { drain } from '../../../common/drain/drain';
|
|
7
|
+
import { getFrameworks } from '../../../common/metrics/framework-detection';
|
|
8
|
+
import { protocol } from '../../../common/url/protocol';
|
|
9
|
+
import { getRules, validateRules } from '../../../common/util/obfuscate';
|
|
10
|
+
import { VERSION } from '../../../common/constants/environment-variables';
|
|
11
|
+
import { onDOMContentLoaded } from '../../../common/window/load';
|
|
12
|
+
import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts';
|
|
13
|
+
import { isBrowserScope } from '../../../common/util/global-scope';
|
|
14
|
+
export class Aggregate extends AggregateBase {
|
|
15
|
+
static featureName = FEATURE_NAME;
|
|
16
|
+
constructor(agentIdentifier, aggregator) {
|
|
17
|
+
super(agentIdentifier, aggregator, FEATURE_NAME);
|
|
18
|
+
let scheduler;
|
|
19
|
+
|
|
20
|
+
// If RUM-call's response determines that customer lacks entitlements for the /jserror ingest endpoint, don't harvest at all.
|
|
21
|
+
registerHandler('block-err', () => {
|
|
22
|
+
this.blocked = true;
|
|
23
|
+
if (scheduler) scheduler.aborted = true; // RUM response may or may not have happened already before scheduler initialization below
|
|
24
|
+
}, this.featureName, this.ee);
|
|
25
|
+
|
|
26
|
+
// Allow features external to the metrics feature to capture SMs and CMs through the event emitter
|
|
27
|
+
registerHandler(SUPPORTABILITY_METRIC_CHANNEL, this.storeSupportabilityMetrics.bind(this), this.featureName, this.ee);
|
|
28
|
+
registerHandler(CUSTOM_METRIC_CHANNEL, this.storeEventMetrics.bind(this), this.featureName, this.ee);
|
|
29
|
+
this.singleChecks(); // checks that are run only one time, at script load
|
|
30
|
+
this.eachSessionChecks(); // the start of every time user engages with page
|
|
31
|
+
|
|
32
|
+
// *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
|
|
33
|
+
scheduler = new HarvestScheduler('jserrors', {
|
|
34
|
+
onUnload: () => this.unload()
|
|
35
|
+
}, this);
|
|
36
|
+
scheduler.harvest.on('jserrors', () => ({
|
|
37
|
+
body: this.aggregator.take(['cm', 'sm'])
|
|
38
|
+
}));
|
|
39
|
+
drain(this.agentIdentifier, this.featureName); // regardless if this is blocked or not, drain is needed to unblock other features from harvesting (counteract registerDrain)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
storeSupportabilityMetrics(name, value) {
|
|
43
|
+
if (this.blocked) return;
|
|
44
|
+
const type = SUPPORTABILITY_METRIC;
|
|
45
|
+
const params = {
|
|
46
|
+
name
|
|
47
|
+
};
|
|
48
|
+
this.aggregator.storeMetric(type, name, params, value);
|
|
49
|
+
}
|
|
50
|
+
storeEventMetrics(name, metrics) {
|
|
51
|
+
if (this.blocked) return;
|
|
52
|
+
const type = CUSTOM_METRIC;
|
|
53
|
+
const params = {
|
|
54
|
+
name
|
|
55
|
+
};
|
|
56
|
+
this.aggregator.store(type, name, params, metrics);
|
|
57
|
+
}
|
|
58
|
+
singleChecks() {
|
|
59
|
+
// report generic info about the agent itself
|
|
60
|
+
// note the browser agent version
|
|
61
|
+
this.storeSupportabilityMetrics(`Generic/Version/${VERSION}/Detected`);
|
|
62
|
+
// report loaderType
|
|
63
|
+
const {
|
|
64
|
+
distMethod,
|
|
65
|
+
loaderType
|
|
66
|
+
} = getRuntime(this.agentIdentifier);
|
|
67
|
+
if (loaderType) this.storeSupportabilityMetrics(`Generic/LoaderType/${loaderType}/Detected`);
|
|
68
|
+
if (distMethod) this.storeSupportabilityMetrics(`Generic/DistMethod/${distMethod}/Detected`);
|
|
69
|
+
|
|
70
|
+
// frameworks on page
|
|
71
|
+
if (isBrowserScope) {
|
|
72
|
+
onDOMContentLoaded(() => {
|
|
73
|
+
getFrameworks().forEach(framework => {
|
|
74
|
+
this.storeSupportabilityMetrics('Framework/' + framework + '/Detected');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// file protocol detection
|
|
80
|
+
if (protocol.isFileProtocol()) {
|
|
81
|
+
this.storeSupportabilityMetrics('Generic/FileProtocol/Detected');
|
|
82
|
+
protocol.supportabilityMetricSent = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// obfuscation rules detection
|
|
86
|
+
const rules = getRules(this.agentIdentifier);
|
|
87
|
+
if (rules.length > 0) this.storeSupportabilityMetrics('Generic/Obfuscate/Detected');
|
|
88
|
+
if (rules.length > 0 && !validateRules(rules)) this.storeSupportabilityMetrics('Generic/Obfuscate/Invalid');
|
|
89
|
+
}
|
|
90
|
+
eachSessionChecks() {
|
|
91
|
+
if (!isBrowserScope) return;
|
|
92
|
+
|
|
93
|
+
// [Temporary] Report restores from BFCache to NR1 while feature flag is in place in lieu of sending pageshow events.
|
|
94
|
+
windowAddEventListener('pageshow', evt => {
|
|
95
|
+
if (evt.persisted) {
|
|
96
|
+
this.storeSupportabilityMetrics('Generic/BFCache/PageRestored');
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
unload() {
|
|
102
|
+
// Page Resources detection for estimations with resources feature work
|
|
103
|
+
// TODO - these SMs are to be removed when we implement the actual resources feature
|
|
104
|
+
try {
|
|
105
|
+
if (this.resourcesSent) return;
|
|
106
|
+
// make sure this only gets sent once
|
|
107
|
+
this.resourcesSent = true;
|
|
108
|
+
// differentiate between internal+external and ajax+non-ajax
|
|
109
|
+
const ajaxResources = ['beacon', 'fetch', 'xmlhttprequest'];
|
|
110
|
+
const internalUrls = ['nr-data.net', 'newrelic.com', 'nr-local.net', 'localhost'];
|
|
111
|
+
function isInternal(x) {
|
|
112
|
+
return internalUrls.some(y => x.name.indexOf(y) >= 0);
|
|
113
|
+
}
|
|
114
|
+
function isAjax(x) {
|
|
115
|
+
return ajaxResources.includes(x.initiatorType);
|
|
116
|
+
}
|
|
117
|
+
const allResources = performance?.getEntriesByType('resource') || [];
|
|
118
|
+
allResources.forEach(entry => {
|
|
119
|
+
if (isInternal(entry)) {
|
|
120
|
+
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/Internal');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/Internal');
|
|
121
|
+
} else {
|
|
122
|
+
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/External');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/External');
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} catch (e) {
|
|
126
|
+
// do nothing
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
2
|
+
export const FEATURE_NAME = FEATURE_NAMES.metrics;
|
|
3
|
+
export const SUPPORTABILITY_METRIC = 'sm';
|
|
4
|
+
export const CUSTOM_METRIC = 'cm';
|
|
5
|
+
export const SUPPORTABILITY_METRIC_CHANNEL = 'storeSupportabilityMetrics';
|
|
6
|
+
export const CUSTOM_METRIC_CHANNEL = 'storeEventMetrics';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Instrument as Metrics } from './instrument/index';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InstrumentBase } from '../../utils/instrument-base';
|
|
2
|
+
import { insertSupportMetrics } from './workers-helper';
|
|
3
|
+
import { FEATURE_NAME, SUPPORTABILITY_METRIC_CHANNEL } from '../constants';
|
|
4
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
5
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
6
|
+
export class Instrument extends InstrumentBase {
|
|
7
|
+
static featureName = FEATURE_NAME;
|
|
8
|
+
constructor(agentIdentifier, aggregator, auto = true) {
|
|
9
|
+
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
10
|
+
insertSupportMetrics(tag => handle(SUPPORTABILITY_METRIC_CHANNEL, [tag], undefined, FEATURE_NAMES.metrics, this.ee));
|
|
11
|
+
this.importAggregator();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { globalScope, isWorkerScope } from '../../../common/util/global-scope';
|
|
2
|
+
import { warn } from '../../../common/util/console';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* True for each Worker type supported in browser's execution context. Not all browser versions may support certain Workers or options however.
|
|
6
|
+
* - Warning: service workers are not available on unsecured HTTP sites
|
|
7
|
+
*/
|
|
8
|
+
const workersApiIsSupported = {
|
|
9
|
+
dedicated: Boolean(globalScope?.Worker),
|
|
10
|
+
shared: Boolean(globalScope?.SharedWorker),
|
|
11
|
+
service: Boolean(globalScope?.navigator?.serviceWorker)
|
|
12
|
+
};
|
|
13
|
+
let origWorker, origSharedWorker, origServiceWorkerCreate;
|
|
14
|
+
/**
|
|
15
|
+
* Un-instrument support metrics for Workers.
|
|
16
|
+
* @returns void
|
|
17
|
+
*/
|
|
18
|
+
function resetSupportability() {
|
|
19
|
+
if (origWorker) globalScope.Worker = origWorker; // Worker was changed by this module
|
|
20
|
+
if (origSharedWorker) globalScope.SharedWorker = origSharedWorker;
|
|
21
|
+
if (origServiceWorkerCreate) globalScope.navigator.serviceWorker.register = origServiceWorkerCreate;
|
|
22
|
+
origWorker = origSharedWorker = origServiceWorkerCreate = undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Injects code to report Web Workers supportability and usage as metrics, replacing the native API in global scope as a side effect.
|
|
27
|
+
* @param {Function} report - a cb used to report data
|
|
28
|
+
* @returns void
|
|
29
|
+
*/
|
|
30
|
+
export function insertSupportMetrics(report) {
|
|
31
|
+
// Of the 3, the normal worker is the most widely supported, so we can be sure metric was already inserted w/o checking other 2.
|
|
32
|
+
if (origWorker) return;
|
|
33
|
+
if (!workersApiIsSupported.dedicated) {
|
|
34
|
+
reportUnavailable('All');
|
|
35
|
+
return; // similarly, if dedicated is n/a, none of them are supported so quit
|
|
36
|
+
} else {
|
|
37
|
+
origWorker = Worker;
|
|
38
|
+
try {
|
|
39
|
+
globalScope.Worker = extendWorkerConstructor(origWorker, 'Dedicated');
|
|
40
|
+
} catch (e) {
|
|
41
|
+
handleInsertionError(e, 'Dedicated');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!workersApiIsSupported.shared) {
|
|
45
|
+
reportUnavailable('Shared');
|
|
46
|
+
} else {
|
|
47
|
+
origSharedWorker = SharedWorker;
|
|
48
|
+
try {
|
|
49
|
+
globalScope.SharedWorker = extendWorkerConstructor(origSharedWorker, 'Shared');
|
|
50
|
+
} catch (e) {
|
|
51
|
+
handleInsertionError(e, 'Shared');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!workersApiIsSupported.service) {
|
|
55
|
+
reportUnavailable('Service');
|
|
56
|
+
} else {
|
|
57
|
+
origServiceWorkerCreate = navigator.serviceWorker.register;
|
|
58
|
+
try {
|
|
59
|
+
globalScope.navigator.serviceWorker.register = extendServiceCreation(origServiceWorkerCreate);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
handleInsertionError(e, 'Service');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
|
|
66
|
+
// Internal helpers - Core
|
|
67
|
+
/**
|
|
68
|
+
* Report each time a Worker or SharedWorker is created in page execution. Note the current trap is set for only "new" and Reflect.construct operations.
|
|
69
|
+
* @param {func obj} origClass - Worker() or SharedWorker()
|
|
70
|
+
* @param {string} workerType - 'Dedicated' or 'Shared'
|
|
71
|
+
* @returns Proxy worker that intercepts the original constructor
|
|
72
|
+
*/
|
|
73
|
+
function extendWorkerConstructor(origClass, workerType) {
|
|
74
|
+
if (typeof Proxy === 'undefined') {
|
|
75
|
+
return origClass;
|
|
76
|
+
}
|
|
77
|
+
const newHandler = {
|
|
78
|
+
construct(oConstructor, args) {
|
|
79
|
+
reportWorkerCreationAttempt(workerType, args[1]?.type);
|
|
80
|
+
return new oConstructor(...args);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
// eslint-disable-next-line
|
|
84
|
+
return new Proxy(origClass, newHandler);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Report each time a ServiceWorkerRegistration is created or updated in page execution.
|
|
88
|
+
* @param {func} origFunc - method responsible for creating a ServiceWorker
|
|
89
|
+
* @returns Refer to ServiceWorkerContainer.register()
|
|
90
|
+
*/
|
|
91
|
+
function extendServiceCreation(origFunc) {
|
|
92
|
+
return (...args) => {
|
|
93
|
+
reportWorkerCreationAttempt('Service', args[1]?.type);
|
|
94
|
+
return origFunc.apply(navigator.serviceWorker, args); // register() has to be rebound to the ServiceWorkerContainer object
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Internal helpers - Reporting & logging
|
|
99
|
+
function reportUnavailable(workerType) {
|
|
100
|
+
if (isWorkerScope) return; // assume that the main browser window has already reported unsupported worker APIs (once per page life);
|
|
101
|
+
// on top of that, not all workers are available inside a certain worker per se--e.g. no sharedWorker() inside Worker
|
|
102
|
+
report(`Workers/${workerType}/Unavailable`);
|
|
103
|
+
}
|
|
104
|
+
function reportWorkerCreationAttempt(workerType, optionType) {
|
|
105
|
+
if (optionType === 'module') {
|
|
106
|
+
report(`Workers/${workerType}/Module`);
|
|
107
|
+
} else {
|
|
108
|
+
report(`Workers/${workerType}/Classic`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function handleInsertionError(e, workerType) {
|
|
112
|
+
// indicates the browser version doesn't support how code is injected, such as Proxy API
|
|
113
|
+
report(`Workers/${workerType}/SM/Unsupported`); // expected to be niche & for older borderline-ES6 browser versions
|
|
114
|
+
warn(`NR Agent: Unable to capture ${workerType} workers.`, e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { mapOwn } from '../../../common/util/map-own';
|
|
7
|
+
import { stringify } from '../../../common/util/stringify';
|
|
8
|
+
import { registerHandler as register } from '../../../common/event-emitter/register-handler';
|
|
9
|
+
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
10
|
+
import { cleanURL } from '../../../common/url/clean-url';
|
|
11
|
+
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config';
|
|
12
|
+
import { AggregateBase } from '../../utils/aggregate-base';
|
|
13
|
+
import { FEATURE_NAME } from '../constants';
|
|
14
|
+
import { drain } from '../../../common/drain/drain';
|
|
15
|
+
import { isBrowserScope } from '../../../common/util/global-scope';
|
|
16
|
+
export class Aggregate extends AggregateBase {
|
|
17
|
+
static featureName = FEATURE_NAME;
|
|
18
|
+
constructor(agentIdentifier, aggregator) {
|
|
19
|
+
super(agentIdentifier, aggregator, FEATURE_NAME);
|
|
20
|
+
this.eventsPerMinute = 240;
|
|
21
|
+
this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'page_action.harvestTimeSeconds') || getConfigurationValue(this.agentIdentifier, 'ins.harvestTimeSeconds') || 30;
|
|
22
|
+
this.eventsPerHarvest = this.eventsPerMinute * this.harvestTimeSeconds / 60;
|
|
23
|
+
this.referrerUrl;
|
|
24
|
+
this.currentEvents;
|
|
25
|
+
this.events = [];
|
|
26
|
+
this.att = getInfo(this.agentIdentifier).jsAttributes; // per-agent, aggregators-shared info context
|
|
27
|
+
|
|
28
|
+
if (isBrowserScope && document.referrer) this.referrerUrl = cleanURL(document.referrer);
|
|
29
|
+
register('api-addPageAction', (...args) => this.addPageAction(...args), this.featureName, this.ee);
|
|
30
|
+
var scheduler = new HarvestScheduler('ins', {
|
|
31
|
+
onFinished: (...args) => this.onHarvestFinished(...args)
|
|
32
|
+
}, this);
|
|
33
|
+
scheduler.harvest.on('ins', (...args) => this.onHarvestStarted(...args));
|
|
34
|
+
this.ee.on(`drain-${this.featureName}`, () => {
|
|
35
|
+
if (!this.blocked) scheduler.startTimer(this.harvestTimeSeconds, 0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// if rum response determines that customer lacks entitlements for ins endpoint, block it
|
|
39
|
+
register('block-ins', () => {
|
|
40
|
+
this.blocked = true;
|
|
41
|
+
scheduler.stopTimer(true);
|
|
42
|
+
}, this.featureName, this.ee);
|
|
43
|
+
drain(this.agentIdentifier, this.featureName);
|
|
44
|
+
}
|
|
45
|
+
onHarvestStarted(options) {
|
|
46
|
+
const {
|
|
47
|
+
userAttributes,
|
|
48
|
+
atts
|
|
49
|
+
} = getInfo(this.agentIdentifier);
|
|
50
|
+
var payload = {
|
|
51
|
+
qs: {
|
|
52
|
+
ua: userAttributes,
|
|
53
|
+
at: atts
|
|
54
|
+
},
|
|
55
|
+
body: {
|
|
56
|
+
ins: this.events
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
if (options.retry) {
|
|
60
|
+
this.currentEvents = this.events;
|
|
61
|
+
}
|
|
62
|
+
this.events = [];
|
|
63
|
+
return payload;
|
|
64
|
+
}
|
|
65
|
+
onHarvestFinished(result) {
|
|
66
|
+
if (result && result.sent && result.retry && this.currentEvents) {
|
|
67
|
+
this.events = this.events.concat(this.currentEvents);
|
|
68
|
+
this.currentEvents = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// WARNING: Insights times are in seconds. EXCEPT timestamp, which is in ms.
|
|
73
|
+
addPageAction(t, name, attributes) {
|
|
74
|
+
if (this.events.length >= this.eventsPerHarvest || this.blocked) return;
|
|
75
|
+
var width;
|
|
76
|
+
var height;
|
|
77
|
+
var eventAttributes = {};
|
|
78
|
+
if (isBrowserScope && window.document.documentElement) {
|
|
79
|
+
// Doesn't include the nav bar when it disappears in mobile safari
|
|
80
|
+
// https://github.com/jquery/jquery/blob/10399ddcf8a239acc27bdec9231b996b178224d3/src/dimensions.js#L23
|
|
81
|
+
width = window.document.documentElement.clientWidth;
|
|
82
|
+
height = window.document.documentElement.clientHeight;
|
|
83
|
+
}
|
|
84
|
+
var defaults = {
|
|
85
|
+
timestamp: t + getRuntime(this.agentIdentifier).offset,
|
|
86
|
+
timeSinceLoad: t / 1000,
|
|
87
|
+
browserWidth: width,
|
|
88
|
+
browserHeight: height,
|
|
89
|
+
referrerUrl: this.referrerUrl,
|
|
90
|
+
currentUrl: cleanURL('' + location),
|
|
91
|
+
pageUrl: cleanURL(getRuntime(this.agentIdentifier).origin),
|
|
92
|
+
eventType: 'PageAction'
|
|
93
|
+
};
|
|
94
|
+
mapOwn(defaults, set);
|
|
95
|
+
mapOwn(getInfo(this.agentIdentifier).jsAttributes, set);
|
|
96
|
+
if (attributes && typeof attributes === 'object') {
|
|
97
|
+
mapOwn(attributes, set);
|
|
98
|
+
}
|
|
99
|
+
eventAttributes.actionName = name || '';
|
|
100
|
+
this.events.push(eventAttributes);
|
|
101
|
+
function set(key, val) {
|
|
102
|
+
eventAttributes[key] = val && typeof val === 'object' ? stringify(val) : val;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Instrument as PageAction } from './instrument/index';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { InstrumentBase } from '../../utils/instrument-base';
|
|
7
|
+
import { FEATURE_NAME } from '../constants';
|
|
8
|
+
export class Instrument extends InstrumentBase {
|
|
9
|
+
static featureName = FEATURE_NAME;
|
|
10
|
+
constructor(agentIdentifier, aggregator, auto = true) {
|
|
11
|
+
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
12
|
+
this.importAggregator();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
2
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
3
|
+
import { isiOS } from '../../../common/browser-version/ios-version';
|
|
4
|
+
import { onTTFB } from 'web-vitals';
|
|
5
|
+
import { mapOwn } from '../../../common/util/map-own';
|
|
6
|
+
import { param, fromArray } from '../../../common/url/encode';
|
|
7
|
+
import { addPT, addPN } from '../../../common/timing/nav-timing';
|
|
8
|
+
import { stringify } from '../../../common/util/stringify';
|
|
9
|
+
import { paintMetrics } from '../../../common/metrics/paint-metrics';
|
|
10
|
+
import { submitData } from '../../../common/util/submit-data';
|
|
11
|
+
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config';
|
|
12
|
+
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
13
|
+
import { AggregateBase } from '../../utils/aggregate-base';
|
|
14
|
+
import * as CONSTANTS from '../constants';
|
|
15
|
+
import { getActivatedFeaturesFlags } from './initialized-features';
|
|
16
|
+
import { globalScope, isBrowserScope } from '../../../common/util/global-scope';
|
|
17
|
+
import { drain } from '../../../common/drain/drain';
|
|
18
|
+
const jsonp = 'NREUM.setToken';
|
|
19
|
+
export class Aggregate extends AggregateBase {
|
|
20
|
+
static featureName = CONSTANTS.FEATURE_NAME;
|
|
21
|
+
constructor(agentIdentifier, aggregator) {
|
|
22
|
+
super(agentIdentifier, aggregator, CONSTANTS.FEATURE_NAME);
|
|
23
|
+
if (typeof PerformanceNavigationTiming !== 'undefined' && !isiOS) {
|
|
24
|
+
this.alreadySent = false; // we don't support timings on BFCache restores
|
|
25
|
+
const agentRuntime = getRuntime(agentIdentifier); // we'll store timing values on the runtime obj to be read by the aggregate module
|
|
26
|
+
|
|
27
|
+
/* Time To First Byte
|
|
28
|
+
This listener must record these values *before* PVE's aggregate sends RUM. */
|
|
29
|
+
onTTFB(({
|
|
30
|
+
value,
|
|
31
|
+
entries
|
|
32
|
+
}) => {
|
|
33
|
+
if (this.alreadySent) return;
|
|
34
|
+
this.alreadySent = true;
|
|
35
|
+
agentRuntime[CONSTANTS.TTFB] = Math.round(value); // this is our "backend" duration; web-vitals will ensure it's lower bounded at 0
|
|
36
|
+
|
|
37
|
+
// Similar to what vitals does for ttfb, we have to factor in activation-start when calculating relative timings:
|
|
38
|
+
const navEntry = entries[0];
|
|
39
|
+
const respOrActivStart = Math.max(navEntry.responseStart, navEntry.activationStart || 0);
|
|
40
|
+
agentRuntime[CONSTANTS.FBTWL] = Math.max(Math.round(navEntry.loadEventEnd - respOrActivStart), 0); // our "frontend" duration
|
|
41
|
+
handle('timing', ['load', Math.round(navEntry.loadEventEnd)], undefined, FEATURE_NAMES.pageViewTiming, this.ee);
|
|
42
|
+
agentRuntime[CONSTANTS.FBTDC] = Math.max(Math.round(navEntry.domContentLoadedEventEnd - respOrActivStart), 0); // our "dom processing" duration
|
|
43
|
+
|
|
44
|
+
this.sendRum();
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
this.sendRum(); // timings either already in runtime from instrument or is meant to get 0'd.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getScheme() {
|
|
52
|
+
return getConfigurationValue(this.agentIdentifier, 'ssl') === false ? 'http' : 'https';
|
|
53
|
+
}
|
|
54
|
+
sendRum() {
|
|
55
|
+
const info = getInfo(this.agentIdentifier);
|
|
56
|
+
if (!info.beacon) return;
|
|
57
|
+
if (info.queueTime) this.aggregator.store('measures', 'qt', {
|
|
58
|
+
value: info.queueTime
|
|
59
|
+
});
|
|
60
|
+
if (info.applicationTime) this.aggregator.store('measures', 'ap', {
|
|
61
|
+
value: info.applicationTime
|
|
62
|
+
});
|
|
63
|
+
const agentRuntime = getRuntime(this.agentIdentifier);
|
|
64
|
+
|
|
65
|
+
// These 3 values should've been recorded after load and before this func runs. They are part of the minimum required for PageView events to be created.
|
|
66
|
+
// Following PR #428, which demands that all agents send RUM call, these need to be sent even outside of the main window context where PerformanceTiming
|
|
67
|
+
// or PerformanceNavigationTiming do not exists. Hence, they'll be filled in by 0s instead in, for example, worker threads that still init the PVE module.
|
|
68
|
+
this.aggregator.store('measures', 'be', {
|
|
69
|
+
value: isBrowserScope ? agentRuntime[CONSTANTS.TTFB] : 0
|
|
70
|
+
});
|
|
71
|
+
this.aggregator.store('measures', 'fe', {
|
|
72
|
+
value: isBrowserScope ? agentRuntime[CONSTANTS.FBTWL] : 0
|
|
73
|
+
});
|
|
74
|
+
this.aggregator.store('measures', 'dc', {
|
|
75
|
+
value: isBrowserScope ? agentRuntime[CONSTANTS.FBTDC] : 0
|
|
76
|
+
});
|
|
77
|
+
var measuresMetrics = this.aggregator.get('measures');
|
|
78
|
+
var measuresQueryString = mapOwn(measuresMetrics, function (metricName, measure) {
|
|
79
|
+
return '&' + metricName + '=' + measure.params.value;
|
|
80
|
+
}).join('');
|
|
81
|
+
|
|
82
|
+
// currently we only have one version of our protocol
|
|
83
|
+
// in the future we may add more
|
|
84
|
+
var protocol = '1';
|
|
85
|
+
var scheduler = new HarvestScheduler('page_view_event', {}, this);
|
|
86
|
+
var chunksForQueryString = [scheduler.harvest.baseQueryString()];
|
|
87
|
+
chunksForQueryString.push(measuresQueryString);
|
|
88
|
+
chunksForQueryString.push(param('tt', info.ttGuid));
|
|
89
|
+
chunksForQueryString.push(param('us', info.user));
|
|
90
|
+
chunksForQueryString.push(param('ac', info.account));
|
|
91
|
+
chunksForQueryString.push(param('pr', info.product));
|
|
92
|
+
chunksForQueryString.push(param('af', getActivatedFeaturesFlags(this.agentIdentifier).join(',')));
|
|
93
|
+
if (globalScope.performance && typeof globalScope.performance.timing !== 'undefined') {
|
|
94
|
+
var navTimingApiData = {
|
|
95
|
+
timing: addPT(agentRuntime.offset, globalScope.performance.timing, {}),
|
|
96
|
+
navigation: addPN(globalScope.performance.navigation, {})
|
|
97
|
+
};
|
|
98
|
+
chunksForQueryString.push(param('perf', stringify(navTimingApiData)));
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
// PVTiming sends these too, albeit using web-vitals and slightly different; it's unknown why they're duplicated, but PVT should be the truth
|
|
102
|
+
var entries = globalScope.performance.getEntriesByType('paint');
|
|
103
|
+
entries.forEach(function (entry) {
|
|
104
|
+
if (!entry.startTime || entry.startTime <= 0) return;
|
|
105
|
+
if (entry.name === 'first-paint') {
|
|
106
|
+
chunksForQueryString.push(param('fp', String(Math.floor(entry.startTime))));
|
|
107
|
+
} else if (entry.name === 'first-contentful-paint') {
|
|
108
|
+
chunksForQueryString.push(param('fcp', String(Math.floor(entry.startTime))));
|
|
109
|
+
}
|
|
110
|
+
paintMetrics[entry.name] = Math.floor(entry.startTime); // this is consumed by Spa module
|
|
111
|
+
});
|
|
112
|
+
} catch (e) {}
|
|
113
|
+
chunksForQueryString.push(param('xx', info.extra));
|
|
114
|
+
chunksForQueryString.push(param('ua', info.userAttributes));
|
|
115
|
+
chunksForQueryString.push(param('at', info.atts));
|
|
116
|
+
var customJsAttributes = stringify(info.jsAttributes);
|
|
117
|
+
chunksForQueryString.push(param('ja', customJsAttributes === '{}' ? null : customJsAttributes));
|
|
118
|
+
var queryString = fromArray(chunksForQueryString, agentRuntime.maxBytes);
|
|
119
|
+
const isValidJsonp = submitData.jsonp(this.getScheme() + '://' + info.beacon + '/' + protocol + '/' + info.licenseKey + queryString, jsonp);
|
|
120
|
+
// Usually `drain` is invoked automatically after processing feature flags contained in the JSONP callback from
|
|
121
|
+
// ingest (see `activateFeatures`), so when JSONP cannot execute (as with module workers), we drain manually.
|
|
122
|
+
if (!isValidJsonp) drain(this.agentIdentifier, CONSTANTS.FEATURE_NAME);
|
|
123
|
+
}
|
|
124
|
+
}
|