@newrelic/browser-agent 1.277.0 → 1.278.1
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/CHANGELOG.md +14 -0
- package/dist/cjs/common/aggregate/event-aggregator.js +1 -1
- package/dist/cjs/common/config/init.js +1 -10
- package/dist/cjs/common/config/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/harvest/harvester.js +255 -0
- package/dist/cjs/common/harvest/types.js +5 -21
- package/dist/cjs/features/ajax/aggregate/index.js +2 -11
- package/dist/cjs/features/generic_events/aggregate/index.js +3 -10
- package/dist/cjs/features/jserrors/aggregate/index.js +3 -14
- package/dist/cjs/features/logging/aggregate/index.js +4 -12
- package/dist/cjs/features/metrics/aggregate/index.js +7 -15
- package/dist/cjs/features/page_view_event/aggregate/index.js +46 -48
- package/dist/cjs/features/page_view_timing/aggregate/index.js +0 -9
- package/dist/cjs/features/session_replay/aggregate/index.js +21 -43
- package/dist/cjs/features/session_replay/instrument/index.js +2 -1
- package/dist/cjs/features/session_replay/shared/recorder.js +6 -6
- package/dist/cjs/features/session_trace/aggregate/index.js +9 -24
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +8 -2
- package/dist/cjs/features/soft_navigations/aggregate/index.js +4 -11
- package/dist/cjs/features/spa/aggregate/index.js +7 -10
- package/dist/cjs/features/utils/aggregate-base.js +66 -27
- package/dist/cjs/features/utils/event-buffer.js +0 -1
- package/dist/cjs/features/utils/event-store-manager.js +109 -0
- package/dist/cjs/features/utils/instrument-base.js +1 -10
- package/dist/cjs/loaders/features/features.js +16 -10
- package/dist/cjs/loaders/micro-agent.js +1 -0
- package/dist/esm/common/aggregate/event-aggregator.js +1 -1
- package/dist/esm/common/config/init.js +1 -10
- package/dist/esm/common/config/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/harvest/harvester.js +249 -0
- package/dist/esm/common/harvest/types.js +5 -21
- package/dist/esm/features/ajax/aggregate/index.js +3 -12
- package/dist/esm/features/generic_events/aggregate/index.js +3 -10
- package/dist/esm/features/jserrors/aggregate/index.js +4 -15
- package/dist/esm/features/logging/aggregate/index.js +4 -12
- package/dist/esm/features/metrics/aggregate/index.js +7 -15
- package/dist/esm/features/page_view_event/aggregate/index.js +46 -48
- package/dist/esm/features/page_view_timing/aggregate/index.js +1 -10
- package/dist/esm/features/session_replay/aggregate/index.js +22 -44
- package/dist/esm/features/session_replay/instrument/index.js +2 -1
- package/dist/esm/features/session_replay/shared/recorder.js +6 -6
- package/dist/esm/features/session_trace/aggregate/index.js +9 -24
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +8 -2
- package/dist/esm/features/soft_navigations/aggregate/index.js +5 -12
- package/dist/esm/features/spa/aggregate/index.js +8 -11
- package/dist/esm/features/utils/aggregate-base.js +66 -27
- package/dist/esm/features/utils/event-buffer.js +0 -1
- package/dist/esm/features/utils/event-store-manager.js +103 -0
- package/dist/esm/features/utils/instrument-base.js +1 -10
- package/dist/esm/loaders/features/features.js +15 -9
- package/dist/esm/loaders/micro-agent.js +1 -0
- package/dist/types/common/aggregate/event-aggregator.d.ts +1 -1
- package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvester.d.ts +16 -0
- package/dist/types/common/harvest/harvester.d.ts.map +1 -0
- package/dist/types/common/harvest/types.d.ts +8 -45
- package/dist/types/common/harvest/types.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +0 -3
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +0 -3
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +6 -2
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +12 -15
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +0 -5
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +8 -5
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +0 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +12 -7
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/event-buffer.d.ts +1 -2
- package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
- package/dist/types/features/utils/event-store-manager.d.ts +43 -0
- package/dist/types/features/utils/event-store-manager.d.ts.map +1 -0
- package/dist/types/features/utils/instrument-base.d.ts +0 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +15 -12
- package/dist/types/loaders/features/features.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/common/aggregate/event-aggregator.js +1 -1
- package/src/common/config/init.js +9 -10
- package/src/common/config/runtime.js +2 -1
- package/src/common/harvest/__mocks__/harvester.js +6 -0
- package/src/common/harvest/harvester.js +230 -0
- package/src/common/harvest/types.js +5 -21
- package/src/features/ajax/aggregate/index.js +3 -14
- package/src/features/generic_events/aggregate/index.js +3 -13
- package/src/features/jserrors/aggregate/index.js +4 -11
- package/src/features/logging/aggregate/index.js +4 -12
- package/src/features/metrics/aggregate/index.js +5 -12
- package/src/features/page_view_event/aggregate/index.js +38 -38
- package/src/features/page_view_timing/aggregate/index.js +1 -12
- package/src/features/session_replay/aggregate/index.js +19 -42
- package/src/features/session_replay/instrument/index.js +1 -1
- package/src/features/session_replay/shared/recorder.js +6 -6
- package/src/features/session_trace/aggregate/index.js +8 -25
- package/src/features/session_trace/aggregate/trace/storage.js +5 -2
- package/src/features/soft_navigations/aggregate/index.js +4 -12
- package/src/features/spa/aggregate/index.js +8 -11
- package/src/features/utils/aggregate-base.js +59 -27
- package/src/features/utils/event-buffer.js +0 -1
- package/src/features/utils/event-store-manager.js +101 -0
- package/src/features/utils/instrument-base.js +2 -8
- package/src/loaders/features/features.js +16 -9
- package/src/loaders/micro-agent.js +1 -0
- package/dist/cjs/common/harvest/harvest-scheduler.js +0 -168
- package/dist/cjs/common/harvest/harvest.js +0 -295
- package/dist/esm/common/harvest/harvest-scheduler.js +0 -160
- package/dist/esm/common/harvest/harvest.js +0 -286
- package/dist/types/common/harvest/harvest-scheduler.d.ts +0 -50
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +0 -1
- package/dist/types/common/harvest/harvest.d.ts +0 -65
- package/dist/types/common/harvest/harvest.d.ts.map +0 -1
- package/src/common/harvest/__mocks__/harvest.js +0 -13
- package/src/common/harvest/harvest-scheduler.js +0 -166
- package/src/common/harvest/harvest.js +0 -282
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { record as recorder } from 'rrweb'
|
|
2
2
|
import { stringify } from '../../../common/util/stringify'
|
|
3
3
|
import { AVG_COMPRESSION, CHECKOUT_MS, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, SR_EVENT_EMITTER_TYPES } from '../constants'
|
|
4
|
-
import { getConfigurationValue } from '../../../common/config/init'
|
|
5
4
|
import { RecorderEvents } from './recorder-events'
|
|
6
5
|
import { MODE } from '../../../common/session/constants'
|
|
7
6
|
import { stylesheetEvaluator } from './stylesheet-evaluator'
|
|
@@ -10,6 +9,7 @@ import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
|
|
|
10
9
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
11
10
|
import { buildNRMetaNode } from './utils'
|
|
12
11
|
import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
|
|
12
|
+
import { AggregateBase } from '../../utils/aggregate-base'
|
|
13
13
|
|
|
14
14
|
export class Recorder {
|
|
15
15
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
@@ -36,7 +36,7 @@ export class Recorder {
|
|
|
36
36
|
/** The parent class that instantiated the recorder */
|
|
37
37
|
this.parent = parent
|
|
38
38
|
/** A flag that can be set to false by failing conversions to stop the fetching process */
|
|
39
|
-
this.shouldFix =
|
|
39
|
+
this.shouldFix = this.parent.agentRef.init.session_replay.fix_stylesheets
|
|
40
40
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
41
41
|
this.stopRecording = () => { /* no-op until set by rrweb initializer */ }
|
|
42
42
|
}
|
|
@@ -73,7 +73,7 @@ export class Recorder {
|
|
|
73
73
|
/** Begin recording using configured recording lib */
|
|
74
74
|
startRecording () {
|
|
75
75
|
this.recording = true
|
|
76
|
-
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, collect_fonts } =
|
|
76
|
+
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, collect_fonts } = this.parent.agentRef.init.session_replay
|
|
77
77
|
const customMasker = (text, element) => {
|
|
78
78
|
try {
|
|
79
79
|
if (typeof element?.type === 'string' && element.type.toLowerCase() !== 'password' && (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask'))) return text
|
|
@@ -151,7 +151,7 @@ export class Recorder {
|
|
|
151
151
|
store (event, isCheckout) {
|
|
152
152
|
if (!event) return
|
|
153
153
|
|
|
154
|
-
if (!this.parent
|
|
154
|
+
if (!(this.parent instanceof AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1]
|
|
155
155
|
else this.currentBufferTarget = this.#events
|
|
156
156
|
|
|
157
157
|
if (this.parent.blocked) return
|
|
@@ -193,8 +193,8 @@ export class Recorder {
|
|
|
193
193
|
// it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
|
|
194
194
|
if (((event.type === RRWEB_EVENT_TYPES.FullSnapshot && this.currentBufferTarget.hasMeta) || payloadSize > IDEAL_PAYLOAD_SIZE) && this.parent.mode === MODE.FULL) {
|
|
195
195
|
// if we've made it to the ideal size of ~64kb before the interval timer, we should send early.
|
|
196
|
-
if (this.parent
|
|
197
|
-
this.parent.
|
|
196
|
+
if (this.parent instanceof AggregateBase) {
|
|
197
|
+
this.parent.agentRef.runtime.harvester.triggerHarvestFor(this.parent)
|
|
198
198
|
} else {
|
|
199
199
|
// we are still in "preload" and it triggered a "stop point". Make a new set, which will get pointed at on next cycle
|
|
200
200
|
this.#preloaded.push(new RecorderEvents())
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
2
|
-
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
3
2
|
import { FEATURE_NAME } from '../constants'
|
|
4
3
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
5
4
|
import { TraceStorage } from './trace/storage'
|
|
@@ -7,7 +6,6 @@ import { obj as encodeObj } from '../../../common/url/encode'
|
|
|
7
6
|
import { globalScope } from '../../../common/constants/runtime'
|
|
8
7
|
import { MODE, SESSION_EVENTS } from '../../../common/session/constants'
|
|
9
8
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
10
|
-
import { FEATURE_TO_ENDPOINT } from '../../../loaders/features/features'
|
|
11
9
|
import { cleanURL } from '../../../common/url/clean-url'
|
|
12
10
|
|
|
13
11
|
const ERROR_MODE_SECONDS_WINDOW = 30 * 1000 // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
|
|
@@ -18,8 +16,8 @@ export class Aggregate extends AggregateBase {
|
|
|
18
16
|
|
|
19
17
|
constructor (agentRef) {
|
|
20
18
|
super(agentRef, FEATURE_NAME)
|
|
19
|
+
this.harvestOpts.raw = true
|
|
21
20
|
|
|
22
|
-
this.harvestTimeSeconds = agentRef.init.session_trace.harvestTimeSeconds || 30
|
|
23
21
|
/** Tied to the entitlement flag response from BCS. Will short circuit operations of the agg if false */
|
|
24
22
|
this.entitled = undefined
|
|
25
23
|
/** A flag used to decide if the 30 node threshold should be ignored on the first harvest to ensure sending on the first payload */
|
|
@@ -28,7 +26,8 @@ export class Aggregate extends AggregateBase {
|
|
|
28
26
|
this.harvesting = false
|
|
29
27
|
/** TraceStorage is the mechanism that holds, normalizes and aggregates ST nodes. It will be accessed and purged when harvests occur */
|
|
30
28
|
this.events = new TraceStorage(this)
|
|
31
|
-
|
|
29
|
+
|
|
30
|
+
/* This agg needs information about sampling (sts) and entitlements (st) to make the appropriate decisions on running */
|
|
32
31
|
this.waitForFlags(['sts', 'st'])
|
|
33
32
|
.then(([stMode, stEntitled]) => this.initialize(stMode, stEntitled))
|
|
34
33
|
}
|
|
@@ -36,7 +35,8 @@ export class Aggregate extends AggregateBase {
|
|
|
36
35
|
/** Sets up event listeners, and initializes this module to run in the correct "mode". Can be triggered from a few places, but makes an effort to only set up listeners once */
|
|
37
36
|
initialize (stMode, stEntitled, ignoreSession) {
|
|
38
37
|
this.entitled ??= stEntitled
|
|
39
|
-
if (
|
|
38
|
+
if (!this.entitled) this.blocked = true
|
|
39
|
+
if (this.blocked) return this.deregisterDrain()
|
|
40
40
|
|
|
41
41
|
if (!this.initialized) {
|
|
42
42
|
this.initialized = true
|
|
@@ -55,7 +55,7 @@ export class Aggregate extends AggregateBase {
|
|
|
55
55
|
// this will only have an effect if ST is NOT already in full mode
|
|
56
56
|
if (this.mode !== MODE.FULL && (sessionState.sessionReplayMode === MODE.FULL || sessionState.sessionTraceMode === MODE.FULL)) this.switchToFull()
|
|
57
57
|
// if another page's session entity has expired, or another page has transitioned to off and this one hasn't... we can just abort straight away here
|
|
58
|
-
if (this.sessionId !== sessionState.value || (eventType === 'cross-tab' &&
|
|
58
|
+
if (this.sessionId !== sessionState.value || (eventType === 'cross-tab' && sessionState.sessionTraceMode === MODE.OFF)) this.abort(2)
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
if (typeof PerformanceNavigationTiming !== 'undefined') {
|
|
@@ -76,13 +76,6 @@ export class Aggregate extends AggregateBase {
|
|
|
76
76
|
|
|
77
77
|
this.timeKeeper ??= this.agentRef.runtime.timeKeeper
|
|
78
78
|
|
|
79
|
-
this.scheduler = new HarvestScheduler(FEATURE_TO_ENDPOINT[this.featureName], {
|
|
80
|
-
onFinished: (result) => this.postHarvestCleanup(result.sent && result.retry),
|
|
81
|
-
retryDelay: this.harvestTimeSeconds,
|
|
82
|
-
getPayload: (options) => this.makeHarvestPayload(options.retry),
|
|
83
|
-
raw: true
|
|
84
|
-
}, this)
|
|
85
|
-
|
|
86
79
|
/** The handlers set up by the Inst file */
|
|
87
80
|
registerHandler('bst', (...args) => this.events.storeEvent(...args), this.featureName, this.ee)
|
|
88
81
|
registerHandler('bstResource', (...args) => this.events.storeResources(...args), this.featureName, this.ee)
|
|
@@ -92,9 +85,7 @@ export class Aggregate extends AggregateBase {
|
|
|
92
85
|
registerHandler('trace-jserror', (...args) => this.events.storeErrorAgg(...args), this.featureName, this.ee)
|
|
93
86
|
registerHandler('pvtAdded', (...args) => this.events.processPVT(...args), this.featureName, this.ee)
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
if (this.mode === MODE.FULL) this.startHarvesting()
|
|
97
|
-
else {
|
|
88
|
+
if (this.mode !== MODE.FULL) {
|
|
98
89
|
/** A separate handler for noticing errors, and switching to "full" mode if running in "error" mode */
|
|
99
90
|
registerHandler('trace-jserror', () => {
|
|
100
91
|
if (this.mode === MODE.ERROR) this.switchToFull()
|
|
@@ -104,13 +95,6 @@ export class Aggregate extends AggregateBase {
|
|
|
104
95
|
this.drain()
|
|
105
96
|
}
|
|
106
97
|
|
|
107
|
-
/** This module does not auto harvest by default -- it needs to be kicked off. Once this method is called, it will then harvest on an interval */
|
|
108
|
-
startHarvesting () {
|
|
109
|
-
if (this.scheduler.started || this.blocked) return
|
|
110
|
-
this.scheduler.runHarvest()
|
|
111
|
-
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
98
|
preHarvestChecks () {
|
|
115
99
|
if (this.mode !== MODE.FULL) return // only allow harvest if running in full mode
|
|
116
100
|
if (!this.timeKeeper?.ready) return // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
|
|
@@ -185,8 +169,8 @@ export class Aggregate extends AggregateBase {
|
|
|
185
169
|
if (prevMode === MODE.OFF || !this.initialized) return this.initialize(this.mode, this.entitled)
|
|
186
170
|
if (this.initialized) {
|
|
187
171
|
this.events.trimSTNs(ERROR_MODE_SECONDS_WINDOW) // up until now, Trace would've been just buffering nodes up to max, which needs to be trimmed to last X seconds
|
|
172
|
+
this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
188
173
|
}
|
|
189
|
-
this.startHarvesting()
|
|
190
174
|
}
|
|
191
175
|
|
|
192
176
|
/** Stop running for the remainder of the page lifecycle */
|
|
@@ -194,7 +178,6 @@ export class Aggregate extends AggregateBase {
|
|
|
194
178
|
this.blocked = true
|
|
195
179
|
this.mode = MODE.OFF
|
|
196
180
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
197
|
-
this.scheduler?.stopTimer()
|
|
198
181
|
this.events.clear()
|
|
199
182
|
}
|
|
200
183
|
}
|
|
@@ -273,7 +273,8 @@ export class TraceStorage {
|
|
|
273
273
|
this.storeSTN(new TraceNode('Ajax', metrics.time, metrics.time + metrics.duration, `${params.status} ${params.method}: ${params.host}${params.pathname}`, 'ajax'))
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
/* Below are the interface expected & required of whatever storage is used across all features on an individual basis. This allows a common `.events` property on Trace.
|
|
276
|
+
/* Below are the interface expected & required of whatever storage is used across all features on an individual basis. This allows a common `.events` property on Trace shared with AggregateBase.
|
|
277
|
+
Note that the usage must be in sync with the EventStoreManager class such that AggregateBase.makeHarvestPayload can run the same regardless of which storage class a feature is using. */
|
|
277
278
|
isEmpty () {
|
|
278
279
|
return this.nodeCount === 0
|
|
279
280
|
}
|
|
@@ -282,7 +283,9 @@ export class TraceStorage {
|
|
|
282
283
|
this.#backupTrace = this.trace
|
|
283
284
|
}
|
|
284
285
|
|
|
285
|
-
get
|
|
286
|
+
get () {
|
|
287
|
+
return [{ targetApp: this.parent.agentRef.mainAppKey, data: this.takeSTNs() }]
|
|
288
|
+
}
|
|
286
289
|
|
|
287
290
|
clear () {
|
|
288
291
|
this.trace = {}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { handle } from '../../../common/event-emitter/handle'
|
|
2
2
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
3
|
-
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
4
3
|
import { single } from '../../../common/util/invoke'
|
|
5
4
|
import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
|
|
6
|
-
import { FEATURE_NAMES
|
|
5
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
7
6
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
|
|
8
7
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
9
8
|
import { API_TRIGGER_NAME, FEATURE_NAME, INTERACTION_STATUS, INTERACTION_TRIGGERS, IPL_TRIGGER_NAME } from '../constants'
|
|
@@ -16,7 +15,6 @@ export class Aggregate extends AggregateBase {
|
|
|
16
15
|
constructor (agentRef, { domObserver }) {
|
|
17
16
|
super(agentRef, FEATURE_NAME)
|
|
18
17
|
|
|
19
|
-
const harvestTimeSeconds = agentRef.init.soft_navigations.harvestTimeSeconds || 10
|
|
20
18
|
this.interactionsToHarvest = this.events
|
|
21
19
|
this.domObserver = domObserver
|
|
22
20
|
|
|
@@ -36,18 +34,12 @@ export class Aggregate extends AggregateBase {
|
|
|
36
34
|
this.latestRouteSetByApi = null
|
|
37
35
|
this.interactionInProgress = null // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
|
|
38
36
|
this.latestHistoryUrl = null
|
|
37
|
+
this.harvestOpts.beforeUnload = () => this.interactionInProgress?.done() // return any withheld ajax or jserr events so they can be sent with EoL harvest
|
|
39
38
|
|
|
40
|
-
this.blocked = false
|
|
41
39
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
42
40
|
if (spaOn) {
|
|
43
41
|
this.drain()
|
|
44
|
-
|
|
45
|
-
onFinished: (result) => this.postHarvestCleanup(result.sent && result.retry),
|
|
46
|
-
getPayload: (options) => this.makeHarvestPayload(options.retry),
|
|
47
|
-
retryDelay: harvestTimeSeconds,
|
|
48
|
-
onUnload: () => this.interactionInProgress?.done() // return any held ajax or jserr events so they can be sent with EoL harvest
|
|
49
|
-
}, this)
|
|
50
|
-
scheduler.startTimer(harvestTimeSeconds, 0)
|
|
42
|
+
setTimeout(() => agentRef.runtime.harvester.triggerHarvestFor(this), 0) // send the IPL ixn on next tick, giving some time for any ajax to finish; we may want to just remove this?
|
|
51
43
|
} else {
|
|
52
44
|
this.blocked = true // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
|
|
53
45
|
this.deregisterDrain()
|
|
@@ -136,7 +128,7 @@ export class Aggregate extends AggregateBase {
|
|
|
136
128
|
*/
|
|
137
129
|
if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress
|
|
138
130
|
let saveIxn
|
|
139
|
-
const interactionsBuffer = this.interactionsToHarvest.get()
|
|
131
|
+
const interactionsBuffer = this.interactionsToHarvest.get(this.agentRef.mainAppKey)[0].data
|
|
140
132
|
for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) { // reverse search for the latest completed interaction for efficiency
|
|
141
133
|
const finishedInteraction = interactionsBuffer[idx]
|
|
142
134
|
if (finishedInteraction.isActiveDuring(timestamp)) {
|
|
@@ -10,11 +10,10 @@ import { navTimingValues as navTiming } from '../../../common/timing/nav-timing'
|
|
|
10
10
|
import { generateUuid } from '../../../common/ids/unique-id'
|
|
11
11
|
import { Interaction } from './interaction'
|
|
12
12
|
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts'
|
|
13
|
-
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
14
13
|
import { Serializer } from './serializer'
|
|
15
14
|
import { ee } from '../../../common/event-emitter/contextual-ee'
|
|
16
15
|
import * as CONSTANTS from '../constants'
|
|
17
|
-
import { FEATURE_NAMES
|
|
16
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
18
17
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
19
18
|
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
|
|
20
19
|
import { firstPaint } from '../../../common/vitals/first-paint'
|
|
@@ -46,14 +45,12 @@ export class Aggregate extends AggregateBase {
|
|
|
46
45
|
pageLoaded: false,
|
|
47
46
|
childTime: 0,
|
|
48
47
|
depth: 0,
|
|
49
|
-
harvestTimeSeconds: agentRef.init.spa.harvestTimeSeconds || 10,
|
|
50
48
|
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
51
49
|
disableSpaFix: (agentRef.init.feature_flags || []).indexOf('disable-spa-fix') > -1
|
|
52
50
|
}
|
|
53
51
|
this.spaSerializerClass = new Serializer(this)
|
|
54
52
|
|
|
55
53
|
const classThis = this
|
|
56
|
-
let scheduler
|
|
57
54
|
|
|
58
55
|
const baseEE = ee.get(agentRef.agentIdentifier) // <-- parent baseEE
|
|
59
56
|
const mutationEE = baseEE.get('mutation')
|
|
@@ -98,13 +95,10 @@ export class Aggregate extends AggregateBase {
|
|
|
98
95
|
// | click ending: | 65 | 50 | | | |
|
|
99
96
|
// click fn-end | 70 | 0 | 0 | 70 | 20 |
|
|
100
97
|
|
|
98
|
+
let harvester
|
|
101
99
|
this.waitForFlags((['spa'])).then(([spaFlag]) => {
|
|
102
100
|
if (spaFlag) {
|
|
103
|
-
|
|
104
|
-
onFinished: (result) => this.postHarvestCleanup(result.sent && result.retry),
|
|
105
|
-
getPayload: (options) => this.makeHarvestPayload(options.retry),
|
|
106
|
-
retryDelay: state.harvestTimeSeconds
|
|
107
|
-
}, this)
|
|
101
|
+
harvester = agentRef.runtime.harvester // since this is after RUM call, PVE would've initialized harvester by now
|
|
108
102
|
this.drain()
|
|
109
103
|
} else {
|
|
110
104
|
this.blocked = true
|
|
@@ -725,8 +719,11 @@ export class Aggregate extends AggregateBase {
|
|
|
725
719
|
else smCategory = 'Custom'
|
|
726
720
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`Spa/Interaction/${smCategory}/Duration/Ms`, Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE)
|
|
727
721
|
|
|
728
|
-
|
|
729
|
-
|
|
722
|
+
if (!harvester) {
|
|
723
|
+
warn(19)
|
|
724
|
+
return
|
|
725
|
+
}
|
|
726
|
+
harvester.triggerHarvestFor(classThis)
|
|
730
727
|
}
|
|
731
728
|
}
|
|
732
729
|
|
|
@@ -5,19 +5,34 @@ import { gosCDN } from '../../common/window/nreum'
|
|
|
5
5
|
import { drain } from '../../common/drain/drain'
|
|
6
6
|
import { activatedFeatures } from '../../common/util/feature-flags'
|
|
7
7
|
import { Obfuscator } from '../../common/util/obfuscate'
|
|
8
|
-
import { EventBuffer } from './event-buffer'
|
|
9
8
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
9
|
+
import { EventStoreManager } from './event-store-manager'
|
|
10
|
+
import { Harvester } from '../../common/harvest/harvester'
|
|
10
11
|
|
|
11
12
|
export class AggregateBase extends FeatureBase {
|
|
12
13
|
constructor (agentRef, featureName) {
|
|
13
14
|
super(agentRef.agentIdentifier, featureName)
|
|
14
15
|
this.agentRef = agentRef
|
|
15
|
-
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
16
|
-
if ([FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics].includes(this.featureName)) this.events = agentRef.sharedAggregator
|
|
17
|
-
// PVE has no need for eventBuffer, and SessionTrace has its own storage mechanism.
|
|
18
|
-
else if (![FEATURE_NAMES.pageViewEvent, FEATURE_NAMES.sessionTrace].includes(this.featureName)) this.events = new EventBuffer()
|
|
19
16
|
this.checkConfiguration(agentRef)
|
|
20
|
-
this.
|
|
17
|
+
this.doOnceForAllAggregate(agentRef)
|
|
18
|
+
|
|
19
|
+
// This switch needs to be after doOnceForAllAggregate which may new sharedAggregator and reset mainAppKey.
|
|
20
|
+
switch (this.featureName) {
|
|
21
|
+
// PVE has no need for eventBuffer, and SessionTrace + Replay have their own storage mechanisms.
|
|
22
|
+
case FEATURE_NAMES.pageViewEvent:
|
|
23
|
+
case FEATURE_NAMES.sessionTrace:
|
|
24
|
+
case FEATURE_NAMES.sessionReplay:
|
|
25
|
+
break
|
|
26
|
+
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
27
|
+
case FEATURE_NAMES.jserrors:
|
|
28
|
+
case FEATURE_NAMES.metrics:
|
|
29
|
+
this.events = agentRef.sharedAggregator
|
|
30
|
+
break
|
|
31
|
+
default:
|
|
32
|
+
this.events = new EventStoreManager(agentRef.mainAppKey, 1)
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
/**
|
|
@@ -56,34 +71,40 @@ export class AggregateBase extends FeatureBase {
|
|
|
56
71
|
/**
|
|
57
72
|
* Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
|
|
58
73
|
* @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
|
|
59
|
-
* @
|
|
74
|
+
* @param {object|undefined} opts.target - the target app passed onto the event store manager to determine which app's data to return; if none provided, all apps data will be returned
|
|
75
|
+
* @returns {Array} Final payload tagged with their targeting browser app. The value of `payload` can be undefined if there are no pending events for an app. This should be a minimum length of 1.
|
|
60
76
|
*/
|
|
61
77
|
makeHarvestPayload (shouldRetryOnFail = false, opts = {}) {
|
|
62
|
-
if (this.events.isEmpty(opts)) return
|
|
78
|
+
if (this.events.isEmpty(this.harvestOpts, opts.target)) return
|
|
63
79
|
// Other conditions and things to do when preparing harvest that is required.
|
|
64
|
-
if (this.preHarvestChecks && !this.preHarvestChecks()) return
|
|
80
|
+
if (this.preHarvestChecks && !this.preHarvestChecks(opts)) return
|
|
65
81
|
|
|
66
|
-
if (shouldRetryOnFail) this.events.save(opts)
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.events.clear(opts)
|
|
82
|
+
if (shouldRetryOnFail) this.events.save(this.harvestOpts, opts.target)
|
|
83
|
+
const returnedDataArr = this.events.get(this.harvestOpts, opts.target)
|
|
84
|
+
if (!returnedDataArr.length) throw new Error('Unexpected problem encountered. There should be at least one app for harvest!')
|
|
85
|
+
this.events.clear(this.harvestOpts, opts.target)
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
body
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
return returnedDataArr.map(({ targetApp, data }) => {
|
|
88
|
+
// A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
|
|
89
|
+
const body = this.serializer ? this.serializer(data) : data
|
|
90
|
+
const payload = {
|
|
91
|
+
body
|
|
92
|
+
}
|
|
93
|
+
// Constructs the payload `qs` for relevant features on harvest.
|
|
94
|
+
if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data)
|
|
95
|
+
|
|
96
|
+
return { targetApp, payload }
|
|
97
|
+
})
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
/**
|
|
81
101
|
* Cleanup task after a harvest.
|
|
82
|
-
* @param {
|
|
102
|
+
* @param {object} result - the cbResult object from the harvester's send method
|
|
83
103
|
*/
|
|
84
|
-
postHarvestCleanup (
|
|
85
|
-
|
|
86
|
-
this.events.
|
|
104
|
+
postHarvestCleanup (result = {}) {
|
|
105
|
+
const harvestFailed = result.sent && result.retry
|
|
106
|
+
if (harvestFailed) this.events.reloadSave(this.harvestOpts, result.targetApp)
|
|
107
|
+
this.events.clearSave(this.harvestOpts, result.targetApp)
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
/**
|
|
@@ -112,9 +133,20 @@ export class AggregateBase extends FeatureBase {
|
|
|
112
133
|
runtime: existingAgent.runtime
|
|
113
134
|
})
|
|
114
135
|
}
|
|
136
|
+
}
|
|
115
137
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
138
|
+
/**
|
|
139
|
+
* These are actions related to shared resources that should be initialized once by whichever feature Aggregate subclass loads first.
|
|
140
|
+
* This method should run after checkConfiguration, which may reset the agent's info/runtime object that is used here.
|
|
141
|
+
*/
|
|
142
|
+
doOnceForAllAggregate (agentRef) {
|
|
143
|
+
if (!agentRef.runtime.obfuscator) agentRef.runtime.obfuscator = new Obfuscator(this.agentIdentifier)
|
|
144
|
+
this.obfuscator = agentRef.runtime.obfuscator
|
|
145
|
+
|
|
146
|
+
if (!agentRef.mainAppKey) agentRef.mainAppKey = { licenseKey: agentRef.info.licenseKey, appId: agentRef.info.applicationID }
|
|
147
|
+
// Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
|
|
148
|
+
if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2)
|
|
149
|
+
|
|
150
|
+
if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
|
|
119
151
|
}
|
|
120
152
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { EventAggregator } from '../../common/aggregate/event-aggregator'
|
|
2
|
+
import { EventBuffer } from './event-buffer'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
|
|
6
|
+
* The purpose is so the harvester can send data to different apps within the same agent. Each feature should have a manager if it needs this capability.
|
|
7
|
+
*/
|
|
8
|
+
export class EventStoreManager {
|
|
9
|
+
/**
|
|
10
|
+
* @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
|
|
11
|
+
* @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
|
|
12
|
+
*/
|
|
13
|
+
constructor (defaultTarget, storageChoice) {
|
|
14
|
+
this.mainApp = defaultTarget
|
|
15
|
+
this.StorageClass = storageChoice === 1 ? EventBuffer : EventAggregator
|
|
16
|
+
this.appStorageMap = new Map()
|
|
17
|
+
this.appStorageMap.set(defaultTarget, new this.StorageClass())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
|
|
24
|
+
* @param {object} target - specific app's storage to check; if not provided, this method takes into account all apps recorded by this manager
|
|
25
|
+
* @returns {boolean} True if the target's storage is empty, or target does not exist in map (defaults to all storages)
|
|
26
|
+
*/
|
|
27
|
+
isEmpty (optsIfPresent, target) {
|
|
28
|
+
if (target) {
|
|
29
|
+
if (!this.appStorageMap.has(target)) return true
|
|
30
|
+
else return this.appStorageMap.get(target).isEmpty(optsIfPresent)
|
|
31
|
+
}
|
|
32
|
+
for (const eventStore of this.appStorageMap.values()) {
|
|
33
|
+
if (!eventStore.isEmpty(optsIfPresent)) return false
|
|
34
|
+
}
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} event - the event element to store
|
|
40
|
+
* @param {object} target - the app to store event under; if not provided, this method adds to the main app from NREUM.info
|
|
41
|
+
* @returns {boolean} True if the event was successfully added
|
|
42
|
+
*/
|
|
43
|
+
add (event, target) {
|
|
44
|
+
if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass())
|
|
45
|
+
return this.appStorageMap.get(target || this.mainApp).add(event)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** This is only used by the Metrics feature which has no need to add metric under a different app atm. */
|
|
49
|
+
addMetric (type, name, params, value) {
|
|
50
|
+
return this.appStorageMap.get(this.mainApp).addMetric(type, name, params, value)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
|
|
55
|
+
* @param {object} target - specific app to fetch; if not provided, this method fetches from all apps
|
|
56
|
+
* @returns {Array} Objects of `data` labeled with their respective `target` app to be sent to
|
|
57
|
+
*/
|
|
58
|
+
get (optsIfPresent, target) {
|
|
59
|
+
if (target) return [{ targetApp: target, data: this.appStorageMap.get(target)?.get(optsIfPresent) }]
|
|
60
|
+
const allPayloads = []
|
|
61
|
+
this.appStorageMap.forEach((eventStore, recordedTarget) => {
|
|
62
|
+
allPayloads.push({ targetApp: recordedTarget, data: eventStore.get(optsIfPresent) })
|
|
63
|
+
})
|
|
64
|
+
return allPayloads
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
byteSize (target) {
|
|
68
|
+
return this.appStorageMap.get(target || this.mainApp).byteSize()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
wouldExceedMaxSize (incomingSize, target) {
|
|
72
|
+
return this.appStorageMap.get(target || this.mainApp).wouldExceedMaxSize(incomingSize)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
save (optsIfPresent, target) {
|
|
76
|
+
if (target) return this.appStorageMap.get(target)?.save(optsIfPresent)
|
|
77
|
+
this.appStorageMap.forEach((eventStore) => eventStore.save(optsIfPresent))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
clear (optsIfPresent, target) {
|
|
81
|
+
if (target) return this.appStorageMap.get(target)?.clear(optsIfPresent)
|
|
82
|
+
this.appStorageMap.forEach((eventStore) => eventStore.clear(optsIfPresent))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Unlike the methods above, the following will have a target as they are called by AggregateBase.postHarvestCleanup callback on harvest finish after getting & sending the data.
|
|
86
|
+
reloadSave (optsIfPresent, target) {
|
|
87
|
+
if (!target) { // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
|
|
88
|
+
this.appStorageMap.forEach((eventStore) => eventStore.reloadSave(optsIfPresent))
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
return this.appStorageMap.get(target)?.reloadSave(optsIfPresent)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clearSave (optsIfPresent, target) {
|
|
95
|
+
if (!target) { // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
|
|
96
|
+
this.appStorageMap.forEach((eventStore) => eventStore.clearSave(optsIfPresent))
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
return this.appStorageMap.get(target)?.clearSave(optsIfPresent)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -43,7 +43,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
43
43
|
/**
|
|
44
44
|
* @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
|
|
45
45
|
* one another if there are inter-features dependencies.
|
|
46
|
-
* TODO: This is only used for the SPA feature component tests and should be refactored out.
|
|
47
46
|
*/
|
|
48
47
|
this.onAggregateImported = undefined
|
|
49
48
|
|
|
@@ -95,13 +94,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
95
94
|
* it's only responsible for aborting its one specific feature, rather than all.
|
|
96
95
|
*/
|
|
97
96
|
try {
|
|
98
|
-
// Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
|
|
99
|
-
if (!agentRef.sharedAggregator) {
|
|
100
|
-
agentRef.sharedAggregator = import(/* webpackChunkName: "shared-aggregator" */ '../../common/aggregate/event-aggregator')
|
|
101
|
-
const { EventAggregator } = await agentRef.sharedAggregator
|
|
102
|
-
agentRef.sharedAggregator = new EventAggregator()
|
|
103
|
-
} else await agentRef.sharedAggregator // if another feature is already importing the aggregator, wait for it to finish
|
|
104
|
-
|
|
105
97
|
if (!this.#shouldImportAgg(this.featureName, session)) {
|
|
106
98
|
drain(this.agentIdentifier, this.featureName)
|
|
107
99
|
loadedSuccessfully(false) // aggregate module isn't loaded at all
|
|
@@ -110,6 +102,8 @@ export class InstrumentBase extends FeatureBase {
|
|
|
110
102
|
const { lazyFeatureLoader } = await import(/* webpackChunkName: "lazy-feature-loader" */ './lazy-feature-loader')
|
|
111
103
|
const { Aggregate } = await lazyFeatureLoader(this.featureName, 'aggregate')
|
|
112
104
|
this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument)
|
|
105
|
+
|
|
106
|
+
agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate) // "subscribe" the feature to future harvest intervals (PVE will start the timer)
|
|
113
107
|
loadedSuccessfully(true)
|
|
114
108
|
} catch (e) {
|
|
115
109
|
warn(34, e)
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// To reduce build size a bit:
|
|
2
|
+
export const EVENTS = 'events'
|
|
3
|
+
export const JSERRORS = 'jserrors'
|
|
4
|
+
const BLOBS = 'browser/blobs'
|
|
5
|
+
export const RUM = 'rum'
|
|
6
|
+
|
|
1
7
|
export const FEATURE_NAMES = {
|
|
2
8
|
ajax: 'ajax',
|
|
3
9
|
genericEvents: 'generic_events',
|
|
4
|
-
jserrors:
|
|
10
|
+
jserrors: JSERRORS,
|
|
5
11
|
logging: 'logging',
|
|
6
12
|
metrics: 'metrics',
|
|
7
13
|
/**
|
|
@@ -35,14 +41,15 @@ export const featurePriority = {
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export const FEATURE_TO_ENDPOINT = {
|
|
38
|
-
[FEATURE_NAMES.
|
|
39
|
-
[FEATURE_NAMES.
|
|
40
|
-
[FEATURE_NAMES.
|
|
41
|
-
[FEATURE_NAMES.
|
|
42
|
-
[FEATURE_NAMES.
|
|
43
|
-
[FEATURE_NAMES.
|
|
44
|
-
[FEATURE_NAMES.
|
|
45
|
-
[FEATURE_NAMES.
|
|
44
|
+
[FEATURE_NAMES.pageViewEvent]: RUM,
|
|
45
|
+
[FEATURE_NAMES.pageViewTiming]: EVENTS,
|
|
46
|
+
[FEATURE_NAMES.ajax]: EVENTS,
|
|
47
|
+
[FEATURE_NAMES.spa]: EVENTS,
|
|
48
|
+
[FEATURE_NAMES.softNav]: EVENTS,
|
|
49
|
+
[FEATURE_NAMES.metrics]: JSERRORS,
|
|
50
|
+
[FEATURE_NAMES.jserrors]: JSERRORS,
|
|
51
|
+
[FEATURE_NAMES.sessionTrace]: BLOBS,
|
|
52
|
+
[FEATURE_NAMES.sessionReplay]: BLOBS,
|
|
46
53
|
[FEATURE_NAMES.logging]: 'browser/logs',
|
|
47
54
|
[FEATURE_NAMES.genericEvents]: 'ins'
|
|
48
55
|
}
|
|
@@ -64,6 +64,7 @@ export class MicroAgent extends MicroAgentBase {
|
|
|
64
64
|
return lazyFeatureLoader(f, 'aggregate')
|
|
65
65
|
}).then(({ Aggregate }) => {
|
|
66
66
|
this.features[f] = new Aggregate(this)
|
|
67
|
+
this.runtime.harvester.initializedAggregates.push(this.features[f]) // so that harvester will poll this feature agg on interval
|
|
67
68
|
}).catch(err => warn(25, err))
|
|
68
69
|
}
|
|
69
70
|
})
|