@newrelic/browser-agent 1.271.0 → 1.273.0
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 +15 -0
- package/dist/cjs/common/aggregate/aggregator.js +23 -30
- package/dist/cjs/common/aggregate/event-aggregator.js +84 -0
- package/dist/cjs/common/config/init.js +8 -4
- 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/harvest-scheduler.js +1 -1
- package/dist/cjs/common/harvest/harvest.js +1 -5
- package/dist/cjs/common/harvest/types.js +0 -1
- package/dist/cjs/features/ajax/aggregate/index.js +52 -62
- package/dist/cjs/features/generic_events/aggregate/index.js +57 -36
- package/dist/cjs/features/generic_events/instrument/index.js +1 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +23 -69
- package/dist/cjs/features/logging/aggregate/index.js +52 -59
- package/dist/cjs/features/metrics/aggregate/index.js +8 -5
- package/dist/cjs/features/page_view_timing/aggregate/index.js +8 -25
- package/dist/cjs/features/session_replay/aggregate/index.js +11 -10
- package/dist/cjs/features/session_replay/shared/recorder-events.js +2 -2
- package/dist/cjs/features/session_trace/aggregate/index.js +77 -88
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +22 -13
- package/dist/cjs/features/soft_navigations/aggregate/index.js +10 -20
- package/dist/cjs/features/soft_navigations/instrument/index.js +5 -9
- package/dist/cjs/features/spa/aggregate/index.js +10 -26
- package/dist/cjs/features/utils/aggregate-base.js +37 -0
- package/dist/cjs/features/utils/event-buffer.js +36 -87
- package/dist/cjs/features/utils/instrument-base.js +3 -3
- package/dist/cjs/loaders/features/features.js +13 -1
- package/dist/esm/common/aggregate/aggregator.js +23 -30
- package/dist/esm/common/aggregate/event-aggregator.js +78 -0
- package/dist/esm/common/config/init.js +8 -4
- 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/harvest-scheduler.js +1 -1
- package/dist/esm/common/harvest/harvest.js +1 -5
- package/dist/esm/common/harvest/types.js +0 -1
- package/dist/esm/features/ajax/aggregate/index.js +53 -62
- package/dist/esm/features/generic_events/aggregate/index.js +57 -36
- package/dist/esm/features/generic_events/instrument/index.js +1 -1
- package/dist/esm/features/jserrors/aggregate/index.js +24 -70
- package/dist/esm/features/logging/aggregate/index.js +52 -59
- package/dist/esm/features/metrics/aggregate/index.js +8 -5
- package/dist/esm/features/page_view_timing/aggregate/index.js +9 -26
- package/dist/esm/features/session_replay/aggregate/index.js +12 -11
- package/dist/esm/features/session_replay/shared/recorder-events.js +2 -2
- package/dist/esm/features/session_trace/aggregate/index.js +77 -88
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +22 -13
- package/dist/esm/features/soft_navigations/aggregate/index.js +11 -21
- package/dist/esm/features/soft_navigations/instrument/index.js +5 -9
- package/dist/esm/features/spa/aggregate/index.js +11 -27
- package/dist/esm/features/utils/aggregate-base.js +37 -0
- package/dist/esm/features/utils/event-buffer.js +36 -88
- package/dist/esm/features/utils/instrument-base.js +3 -3
- package/dist/esm/loaders/features/features.js +12 -0
- package/dist/types/common/aggregate/aggregator.d.ts +4 -6
- package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
- package/dist/types/common/aggregate/event-aggregator.d.ts +26 -0
- package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -0
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/harvest/types.d.ts +1 -4
- package/dist/types/common/harvest/types.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts +2 -10
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +5 -11
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +4 -7
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +10 -28
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -9
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +3 -4
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -1
- package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +17 -19
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +10 -6
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +3 -9
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +2 -3
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +14 -0
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/event-buffer.d.ts +19 -56
- package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +3 -0
- package/dist/types/loaders/features/features.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/common/aggregate/aggregator.js +22 -32
- package/src/common/aggregate/event-aggregator.js +76 -0
- package/src/common/config/init.js +6 -2
- package/src/common/harvest/harvest-scheduler.js +1 -1
- package/src/common/harvest/harvest.js +1 -5
- package/src/common/harvest/types.js +0 -1
- package/src/features/ajax/aggregate/index.js +60 -67
- package/src/features/generic_events/aggregate/index.js +48 -38
- package/src/features/generic_events/instrument/index.js +2 -0
- package/src/features/jserrors/aggregate/index.js +21 -77
- package/src/features/logging/aggregate/index.js +46 -60
- package/src/features/metrics/aggregate/index.js +6 -4
- package/src/features/page_view_timing/aggregate/index.js +9 -30
- package/src/features/session_replay/aggregate/index.js +10 -14
- package/src/features/session_replay/shared/recorder-events.js +2 -2
- package/src/features/session_trace/aggregate/index.js +64 -73
- package/src/features/session_trace/aggregate/trace/storage.js +25 -14
- package/src/features/soft_navigations/aggregate/index.js +11 -22
- package/src/features/soft_navigations/instrument/index.js +6 -9
- package/src/features/spa/aggregate/index.js +12 -27
- package/src/features/utils/aggregate-base.js +39 -0
- package/src/features/utils/event-buffer.js +36 -83
- package/src/features/utils/instrument-base.js +3 -3
- package/src/loaders/features/features.js +13 -0
- package/dist/cjs/features/ajax/aggregate/chunk.js +0 -51
- package/dist/esm/features/ajax/aggregate/chunk.js +0 -44
- package/dist/types/features/ajax/aggregate/chunk.d.ts +0 -8
- package/dist/types/features/ajax/aggregate/chunk.d.ts.map +0 -1
- package/src/features/ajax/aggregate/chunk.js +0 -52
|
@@ -13,6 +13,7 @@ var _encode = require("../../../common/url/encode");
|
|
|
13
13
|
var _runtime = require("../../../common/constants/runtime");
|
|
14
14
|
var _constants2 = require("../../../common/session/constants");
|
|
15
15
|
var _traverse = require("../../../common/util/traverse");
|
|
16
|
+
var _features = require("../../../loaders/features/features");
|
|
16
17
|
var _cleanUrl = require("../../../common/url/clean-url");
|
|
17
18
|
const ERROR_MODE_SECONDS_WINDOW = 30 * 1000; // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
|
|
18
19
|
/** Reserved room for query param attrs */
|
|
@@ -21,9 +22,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
21
22
|
static featureName = _constants.FEATURE_NAME;
|
|
22
23
|
constructor(agentRef) {
|
|
23
24
|
super(agentRef, _constants.FEATURE_NAME);
|
|
24
|
-
|
|
25
|
-
/** A buffer to hold on to harvested traces in the case that a retry must be made later */
|
|
26
|
-
this.sentTrace = null;
|
|
27
25
|
this.harvestTimeSeconds = agentRef.init.session_trace.harvestTimeSeconds || 30;
|
|
28
26
|
/** Tied to the entitlement flag response from BCS. Will short circuit operations of the agg if false */
|
|
29
27
|
this.entitled = undefined;
|
|
@@ -32,7 +30,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
32
30
|
/** If the harvest module is harvesting */
|
|
33
31
|
this.harvesting = false;
|
|
34
32
|
/** TraceStorage is the mechanism that holds, normalizes and aggregates ST nodes. It will be accessed and purged when harvests occur */
|
|
35
|
-
this.
|
|
33
|
+
this.events = new _storage.TraceStorage(this);
|
|
36
34
|
/** This agg needs information about sampling (sts) and entitlements (st) to make the appropriate decisions on running */
|
|
37
35
|
this.waitForFlags(['sts', 'st']).then(([stMode, stEntitled]) => this.initialize(stMode, stEntitled));
|
|
38
36
|
}
|
|
@@ -61,9 +59,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
61
59
|
if (this.sessionId !== sessionState.value || eventType === 'cross-tab' && this.scheduler?.started && sessionState.sessionTraceMode === _constants2.MODE.OFF) this.abort(2);
|
|
62
60
|
});
|
|
63
61
|
if (typeof PerformanceNavigationTiming !== 'undefined') {
|
|
64
|
-
this.
|
|
62
|
+
this.events.storeTiming(_runtime.globalScope.performance?.getEntriesByType?.('navigation')[0]);
|
|
65
63
|
} else {
|
|
66
|
-
this.
|
|
64
|
+
this.events.storeTiming(_runtime.globalScope.performance?.timing, true);
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
67
|
|
|
@@ -75,21 +73,21 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
75
73
|
* If it drains later (due to a mode change), data and handlers will instantly drain instead of waiting for the registry. */
|
|
76
74
|
if (this.mode === _constants2.MODE.OFF) return this.deregisterDrain();
|
|
77
75
|
this.timeKeeper ??= this.agentRef.runtime.timeKeeper;
|
|
78
|
-
this.scheduler = new _harvestScheduler.HarvestScheduler(
|
|
79
|
-
onFinished: this.
|
|
76
|
+
this.scheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
|
|
77
|
+
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
80
78
|
retryDelay: this.harvestTimeSeconds,
|
|
81
|
-
getPayload: this.
|
|
79
|
+
getPayload: options => this.makeHarvestPayload(options.retry),
|
|
82
80
|
raw: true
|
|
83
81
|
}, this);
|
|
84
82
|
|
|
85
83
|
/** The handlers set up by the Inst file */
|
|
86
|
-
(0, _registerHandler.registerHandler)('bst', (...args) => this.
|
|
87
|
-
(0, _registerHandler.registerHandler)('bstResource', (...args) => this.
|
|
88
|
-
(0, _registerHandler.registerHandler)('bstHist', (...args) => this.
|
|
89
|
-
(0, _registerHandler.registerHandler)('bstXhrAgg', (...args) => this.
|
|
90
|
-
(0, _registerHandler.registerHandler)('bstApi', (...args) => this.
|
|
91
|
-
(0, _registerHandler.registerHandler)('trace-jserror', (...args) => this.
|
|
92
|
-
(0, _registerHandler.registerHandler)('pvtAdded', (...args) => this.
|
|
84
|
+
(0, _registerHandler.registerHandler)('bst', (...args) => this.events.storeEvent(...args), this.featureName, this.ee);
|
|
85
|
+
(0, _registerHandler.registerHandler)('bstResource', (...args) => this.events.storeResources(...args), this.featureName, this.ee);
|
|
86
|
+
(0, _registerHandler.registerHandler)('bstHist', (...args) => this.events.storeHist(...args), this.featureName, this.ee);
|
|
87
|
+
(0, _registerHandler.registerHandler)('bstXhrAgg', (...args) => this.events.storeXhrAgg(...args), this.featureName, this.ee);
|
|
88
|
+
(0, _registerHandler.registerHandler)('bstApi', (...args) => this.events.storeSTN(...args), this.featureName, this.ee);
|
|
89
|
+
(0, _registerHandler.registerHandler)('trace-jserror', (...args) => this.events.storeErrorAgg(...args), this.featureName, this.ee);
|
|
90
|
+
(0, _registerHandler.registerHandler)('pvtAdded', (...args) => this.events.processPVT(...args), this.featureName, this.ee);
|
|
93
91
|
|
|
94
92
|
/** Only start actually harvesting if running in full mode at init time */
|
|
95
93
|
if (this.mode === _constants2.MODE.FULL) this.startHarvesting();else {
|
|
@@ -110,32 +108,38 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
110
108
|
this.scheduler.runHarvest();
|
|
111
109
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
prepareHarvest(options = {}) {
|
|
116
|
-
this.traceStorage.prevStoredEvents.clear(); // release references to past events for GC
|
|
111
|
+
preHarvestChecks() {
|
|
112
|
+
if (this.mode !== _constants2.MODE.FULL) return; // only allow harvest if running in full mode
|
|
117
113
|
if (!this.timeKeeper?.ready) return; // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
|
|
118
|
-
if (this.
|
|
119
|
-
if (this.sessionId !== this.agentRef.runtime.session
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
earliestTimeStamp,
|
|
124
|
-
latestTimeStamp
|
|
125
|
-
} = this.traceStorage.takeSTNs();
|
|
126
|
-
if (!stns) return; // there are no trace nodes
|
|
127
|
-
if (options.retry) {
|
|
128
|
-
this.sentTrace = stns;
|
|
114
|
+
if (!this.agentRef.runtime.session) return; // session entity is required for trace to run and continue running
|
|
115
|
+
if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
|
|
116
|
+
// If something unexpected happened and we somehow still got to harvesting after a session identifier changed, we should force-exit instead of harvesting:
|
|
117
|
+
this.abort(3);
|
|
118
|
+
return;
|
|
129
119
|
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
serializer({
|
|
123
|
+
stns
|
|
124
|
+
}) {
|
|
125
|
+
if (!stns.length) return; // there are no processed nodes
|
|
126
|
+
this.everHarvested = true;
|
|
127
|
+
return (0, _traverse.applyFnToProps)(stns, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
|
|
128
|
+
}
|
|
129
|
+
queryStringsBuilder({
|
|
130
|
+
stns,
|
|
131
|
+
earliestTimeStamp,
|
|
132
|
+
latestTimeStamp
|
|
133
|
+
}) {
|
|
130
134
|
const firstSessionHarvest = !this.agentRef.runtime.session.state.traceHarvestStarted;
|
|
131
135
|
if (firstSessionHarvest) this.agentRef.runtime.session.write({
|
|
132
136
|
traceHarvestStarted: true
|
|
133
137
|
});
|
|
134
|
-
const hasReplay = this.agentRef.runtime.session
|
|
135
|
-
const endUserId = this.agentRef.info
|
|
136
|
-
|
|
138
|
+
const hasReplay = this.agentRef.runtime.session.state.sessionReplayMode === 1;
|
|
139
|
+
const endUserId = this.agentRef.info.jsAttributes['enduser.id'];
|
|
140
|
+
const entityGuid = this.agentRef.runtime.appMetadata.agents?.[0]?.entityGuid;
|
|
137
141
|
|
|
138
|
-
|
|
142
|
+
/* The blob consumer expects the following and will reject if not supplied:
|
|
139
143
|
* browser_monitoring_key
|
|
140
144
|
* type
|
|
141
145
|
* app_id
|
|
@@ -144,61 +148,45 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
144
148
|
*
|
|
145
149
|
* For data that does not fit the schema of the above, it should be url-encoded and placed into `attributes`
|
|
146
150
|
*/
|
|
147
|
-
|
|
151
|
+
|
|
148
152
|
return {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}, QUERY_PARAM_PADDING).substring(1) // remove the leading '&'
|
|
184
|
-
},
|
|
185
|
-
body: (0, _traverse.applyFnToProps)(stns, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
|
|
153
|
+
browser_monitoring_key: this.agentRef.info.licenseKey,
|
|
154
|
+
type: 'BrowserSessionChunk',
|
|
155
|
+
app_id: this.agentRef.info.applicationID,
|
|
156
|
+
protocol_version: '0',
|
|
157
|
+
timestamp: Math.floor(this.timeKeeper.correctRelativeTimestamp(earliestTimeStamp)),
|
|
158
|
+
attributes: (0, _encode.obj)({
|
|
159
|
+
...(entityGuid && {
|
|
160
|
+
entityGuid
|
|
161
|
+
}),
|
|
162
|
+
harvestId: "".concat(this.agentRef.runtime.session.state.value, "_").concat(this.agentRef.runtime.ptid, "_").concat(this.agentRef.runtime.harvestCount),
|
|
163
|
+
// this section of attributes must be controllable and stay below the query param padding limit -- see QUERY_PARAM_PADDING
|
|
164
|
+
// if not, data could be lost to truncation at time of sending, potentially breaking parsing / API behavior in NR1
|
|
165
|
+
// trace payload metadata
|
|
166
|
+
'trace.firstTimestamp': Math.floor(this.timeKeeper.correctRelativeTimestamp(earliestTimeStamp)),
|
|
167
|
+
'trace.lastTimestamp': Math.floor(this.timeKeeper.correctRelativeTimestamp(latestTimeStamp)),
|
|
168
|
+
'trace.nodes': stns.length,
|
|
169
|
+
'trace.originTimestamp': this.timeKeeper.correctedOriginTime,
|
|
170
|
+
// other payload metadata
|
|
171
|
+
agentVersion: this.agentRef.runtime.version,
|
|
172
|
+
...(firstSessionHarvest && {
|
|
173
|
+
firstSessionHarvest
|
|
174
|
+
}),
|
|
175
|
+
...(hasReplay && {
|
|
176
|
+
hasReplay
|
|
177
|
+
}),
|
|
178
|
+
ptid: "".concat(this.ptid),
|
|
179
|
+
session: "".concat(this.sessionId),
|
|
180
|
+
// customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
|
|
181
|
+
...(endUserId && {
|
|
182
|
+
'enduser.id': this.obfuscator.obfuscateString(endUserId)
|
|
183
|
+
}),
|
|
184
|
+
currentUrl: this.obfuscator.obfuscateString((0, _cleanUrl.cleanURL)('' + location))
|
|
185
|
+
// The Query Param is being arbitrarily limited in length here. It is also applied when estimating the size of the payload in getPayloadSize()
|
|
186
|
+
}, QUERY_PARAM_PADDING).substring(1) // remove the leading '&'
|
|
186
187
|
};
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
/** When the harvest scheduler finishes, this callback is executed. It's main purpose is to determine if the payload needs to be retried
|
|
190
|
-
* and if so, it will take all data from the temporary buffer and place it back into the traceStorage module
|
|
191
|
-
*/
|
|
192
|
-
onHarvestFinished(result) {
|
|
193
|
-
if (result.sent && result.retry && this.sentTrace) {
|
|
194
|
-
// merge previous trace back into buffer to retry for next harvest
|
|
195
|
-
Object.entries(this.sentTrace).forEach(([name, listOfSTNodes]) => {
|
|
196
|
-
this.traceStorage.restoreNode(name, listOfSTNodes);
|
|
197
|
-
});
|
|
198
|
-
this.sentTrace = null;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
190
|
/** Switch from "off" or "error" to full mode (if entitled) */
|
|
203
191
|
switchToFull() {
|
|
204
192
|
if (this.mode === _constants2.MODE.FULL || !this.entitled || this.blocked) return;
|
|
@@ -209,19 +197,20 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
209
197
|
});
|
|
210
198
|
if (prevMode === _constants2.MODE.OFF || !this.initialized) return this.initialize(this.mode, this.entitled);
|
|
211
199
|
if (this.initialized) {
|
|
212
|
-
this.
|
|
200
|
+
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
|
|
213
201
|
}
|
|
214
202
|
this.startHarvesting();
|
|
215
203
|
}
|
|
216
204
|
|
|
217
205
|
/** Stop running for the remainder of the page lifecycle */
|
|
218
|
-
abort(
|
|
206
|
+
abort() {
|
|
219
207
|
this.blocked = true;
|
|
220
208
|
this.mode = _constants2.MODE.OFF;
|
|
221
209
|
this.agentRef.runtime.session.write({
|
|
222
210
|
sessionTraceMode: this.mode
|
|
223
211
|
});
|
|
224
212
|
this.scheduler?.stopTimer();
|
|
213
|
+
this.events.clear();
|
|
225
214
|
}
|
|
226
215
|
}
|
|
227
216
|
exports.Aggregate = Aggregate;
|
|
@@ -41,8 +41,8 @@ class TraceStorage {
|
|
|
41
41
|
trace = {};
|
|
42
42
|
earliestTimeStamp = Infinity;
|
|
43
43
|
latestTimeStamp = 0;
|
|
44
|
-
tempStorage = [];
|
|
45
44
|
prevStoredEvents = new Set();
|
|
45
|
+
#backupTrace;
|
|
46
46
|
constructor(parent) {
|
|
47
47
|
this.parent = parent;
|
|
48
48
|
}
|
|
@@ -56,9 +56,6 @@ class TraceStorage {
|
|
|
56
56
|
const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
|
|
57
57
|
if (openedSpace === 0) return;
|
|
58
58
|
}
|
|
59
|
-
while (this.tempStorage.length) {
|
|
60
|
-
this.storeSTN(this.tempStorage.shift());
|
|
61
|
-
}
|
|
62
59
|
if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
|
|
63
60
|
if (stn.s < this.earliestTimeStamp) this.earliestTimeStamp = stn.s;
|
|
64
61
|
if (stn.s > this.latestTimeStamp) this.latestTimeStamp = stn.s;
|
|
@@ -105,13 +102,8 @@ class TraceStorage {
|
|
|
105
102
|
const partitionListByOriginMap = listOfSTNodes.sort((a, b) => a.s - b.s).reduce(reindexByOriginFn, {});
|
|
106
103
|
return Object.values(partitionListByOriginMap).flat(); // join the partitions back into 1-D, now ordered by origin then start time
|
|
107
104
|
}, this);
|
|
108
|
-
if (stns.length === 0) return {};
|
|
109
|
-
this.trace = {};
|
|
110
|
-
this.nodeCount = 0;
|
|
111
105
|
const earliestTimeStamp = this.earliestTimeStamp;
|
|
112
|
-
this.earliestTimeStamp = Infinity;
|
|
113
106
|
const latestTimeStamp = this.latestTimeStamp;
|
|
114
|
-
this.latestTimeStamp = 0;
|
|
115
107
|
return {
|
|
116
108
|
stns,
|
|
117
109
|
earliestTimeStamp,
|
|
@@ -282,10 +274,27 @@ class TraceStorage {
|
|
|
282
274
|
if (type !== 'xhr') return;
|
|
283
275
|
this.storeSTN(new _node.TraceNode('Ajax', metrics.time, metrics.time + metrics.duration, "".concat(params.status, " ").concat(params.method, ": ").concat(params.host).concat(params.pathname), 'ajax'));
|
|
284
276
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
277
|
+
|
|
278
|
+
/* 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. */
|
|
279
|
+
isEmpty() {
|
|
280
|
+
return this.nodeCount === 0;
|
|
281
|
+
}
|
|
282
|
+
save() {
|
|
283
|
+
this.#backupTrace = this.trace;
|
|
284
|
+
}
|
|
285
|
+
get = this.takeSTNs;
|
|
286
|
+
clear() {
|
|
287
|
+
this.trace = {};
|
|
288
|
+
this.nodeCount = 0;
|
|
289
|
+
this.prevStoredEvents.clear(); // release references to past events for GC
|
|
290
|
+
this.earliestTimeStamp = Infinity;
|
|
291
|
+
this.latestTimeStamp = 0;
|
|
292
|
+
}
|
|
293
|
+
reloadSave() {
|
|
294
|
+
Object.values(this.#backupTrace).forEach(stnsArray => stnsArray.forEach(stn => this.storeSTN(stn)));
|
|
295
|
+
}
|
|
296
|
+
clearSave() {
|
|
297
|
+
this.#backupTrace = undefined;
|
|
289
298
|
}
|
|
290
299
|
}
|
|
291
300
|
exports.TraceStorage = TraceStorage;
|
|
@@ -12,7 +12,6 @@ var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
|
|
|
12
12
|
var _features = require("../../../loaders/features/features");
|
|
13
13
|
var _constants = require("../../metrics/constants");
|
|
14
14
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
15
|
-
var _eventBuffer = require("../../utils/event-buffer");
|
|
16
15
|
var _constants2 = require("../constants");
|
|
17
16
|
var _ajaxNode = require("./ajax-node");
|
|
18
17
|
var _initialPageLoadInteraction = require("./initial-page-load-interaction");
|
|
@@ -24,7 +23,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
24
23
|
}) {
|
|
25
24
|
super(agentRef, _constants2.FEATURE_NAME);
|
|
26
25
|
const harvestTimeSeconds = agentRef.init.soft_navigations.harvestTimeSeconds || 10;
|
|
27
|
-
this.interactionsToHarvest =
|
|
26
|
+
this.interactionsToHarvest = this.events;
|
|
28
27
|
this.domObserver = domObserver;
|
|
29
28
|
this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentRef.agentIdentifier);
|
|
30
29
|
_timeToFirstByte.timeToFirstByte.subscribe(({
|
|
@@ -45,12 +44,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
45
44
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
46
45
|
if (spaOn) {
|
|
47
46
|
this.drain();
|
|
48
|
-
const scheduler = new _harvestScheduler.HarvestScheduler(
|
|
49
|
-
onFinished: this.
|
|
47
|
+
const scheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
|
|
48
|
+
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
49
|
+
getPayload: options => this.makeHarvestPayload(options.retry),
|
|
50
50
|
retryDelay: harvestTimeSeconds,
|
|
51
51
|
onUnload: () => this.interactionInProgress?.done() // return any held ajax or jserr events so they can be sent with EoL harvest
|
|
52
52
|
}, this);
|
|
53
|
-
scheduler.harvest.on('events', this.onHarvestStarted.bind(this));
|
|
54
53
|
scheduler.startTimer(harvestTimeSeconds, 0);
|
|
55
54
|
} else {
|
|
56
55
|
this.blocked = true; // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
|
|
@@ -69,26 +68,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
69
68
|
(0, _registerHandler.registerHandler)('ajax', this.#handleAjaxEvent.bind(this), this.featureName, this.ee);
|
|
70
69
|
(0, _registerHandler.registerHandler)('jserror', this.#handleJserror.bind(this), this.featureName, this.ee);
|
|
71
70
|
}
|
|
72
|
-
|
|
73
|
-
if (!this.interactionsToHarvest.hasData || this.blocked) return;
|
|
71
|
+
serializer(eventBuffer) {
|
|
74
72
|
// The payload depacker takes the first ixn of a payload (if there are multiple ixns) and positively offset the subsequent ixns timestamps by that amount.
|
|
75
73
|
// In order to accurately portray the real start & end times of the 2nd & onward ixns, we hence need to negatively offset their start timestamps with that of the 1st ixn.
|
|
76
74
|
let firstIxnStartTime = 0; // the very 1st ixn does not require any offsetting
|
|
77
75
|
const serializedIxnList = [];
|
|
78
|
-
for (const interaction of
|
|
76
|
+
for (const interaction of eventBuffer) {
|
|
79
77
|
serializedIxnList.push(interaction.serialize(firstIxnStartTime));
|
|
80
78
|
if (!firstIxnStartTime) firstIxnStartTime = Math.floor(interaction.start);
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
if (options.retry) this.interactionsToHarvest.hold();else this.interactionsToHarvest.clear();
|
|
84
|
-
return {
|
|
85
|
-
body: {
|
|
86
|
-
e: payload
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
onHarvestFinished(result) {
|
|
91
|
-
if (result.sent && result.retry && this.interactionsToHarvest.held.hasData) this.interactionsToHarvest.unhold();else this.interactionsToHarvest.held.clear();
|
|
80
|
+
return "bel.7;".concat(serializedIxnList.join(';'));
|
|
92
81
|
}
|
|
93
82
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
94
83
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
@@ -136,9 +125,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
136
125
|
*/
|
|
137
126
|
if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress;
|
|
138
127
|
let saveIxn;
|
|
139
|
-
|
|
128
|
+
const interactionsBuffer = this.interactionsToHarvest.get();
|
|
129
|
+
for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) {
|
|
140
130
|
// reverse search for the latest completed interaction for efficiency
|
|
141
|
-
const finishedInteraction =
|
|
131
|
+
const finishedInteraction = interactionsBuffer[idx];
|
|
142
132
|
if (finishedInteraction.isActiveDuring(timestamp)) {
|
|
143
133
|
if (finishedInteraction.trigger !== 'initialPageLoad') return finishedInteraction;
|
|
144
134
|
// It's possible that a complete interaction occurs before page is fully loaded, so we need to consider if a route-change ixn may have overlapped this iPL
|
|
@@ -9,7 +9,6 @@ var _runtime = require("../../../common/constants/runtime");
|
|
|
9
9
|
var _handle = require("../../../common/event-emitter/handle");
|
|
10
10
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
11
11
|
var _invoke = require("../../../common/util/invoke");
|
|
12
|
-
var _wrapEvents = require("../../../common/wrap/wrap-events");
|
|
13
12
|
var _wrapHistory = require("../../../common/wrap/wrap-history");
|
|
14
13
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
15
14
|
var _constants = require("../constants");
|
|
@@ -28,7 +27,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
28
27
|
if (!_runtime.isBrowserScope || !(0, _nreum.gosNREUMOriginals)().o.MO) return; // soft navigations is not supported outside web env or browsers without the mutation observer API
|
|
29
28
|
|
|
30
29
|
const historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
|
|
31
|
-
|
|
30
|
+
_constants.INTERACTION_TRIGGERS.forEach(trigger => {
|
|
31
|
+
(0, _eventListenerOpts.windowAddEventListener)(trigger, evt => {
|
|
32
|
+
processUserInteraction(evt);
|
|
33
|
+
}, true);
|
|
34
|
+
});
|
|
32
35
|
const trackURLChange = () => (0, _handle.handle)('newURL', [(0, _now.now)(), '' + window.location], undefined, this.featureName, this.ee);
|
|
33
36
|
historyEE.on('pushState-end', trackURLChange);
|
|
34
37
|
historyEE.on('replaceState-end', trackURLChange);
|
|
@@ -58,13 +61,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
58
61
|
}, UI_WAIT_INTERVAL, {
|
|
59
62
|
leading: true
|
|
60
63
|
});
|
|
61
|
-
eventsEE.on('fn-start', ([evt]) => {
|
|
62
|
-
// set up a new user ixn before the callback for the triggering event executes
|
|
63
|
-
if (_constants.INTERACTION_TRIGGERS.includes(evt?.type)) {
|
|
64
|
-
processUserInteraction(evt);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
for (let eventType of _constants.INTERACTION_TRIGGERS) document.addEventListener(eventType, () => {/* no-op, this ensures the UI events are monitored by our callback above */});
|
|
68
64
|
this.abortHandler = abort;
|
|
69
65
|
this.importAggregator(agentRef, {
|
|
70
66
|
domObserver
|
|
@@ -24,7 +24,6 @@ var _runtime = require("../../../common/constants/runtime");
|
|
|
24
24
|
var _handle = require("../../../common/event-emitter/handle");
|
|
25
25
|
var _constants2 = require("../../metrics/constants");
|
|
26
26
|
var _console = require("../../../common/util/console");
|
|
27
|
-
var _eventBuffer = require("../../utils/event-buffer");
|
|
28
27
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
29
28
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
30
29
|
/*
|
|
@@ -55,7 +54,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
55
54
|
static featureName = FEATURE_NAME;
|
|
56
55
|
constructor(agentRef) {
|
|
57
56
|
super(agentRef, FEATURE_NAME);
|
|
58
|
-
this.state = {
|
|
57
|
+
const state = this.state = {
|
|
59
58
|
initialPageURL: _runtime.initialLocation,
|
|
60
59
|
lastSeenUrl: _runtime.initialLocation,
|
|
61
60
|
lastSeenRouteName: null,
|
|
@@ -69,16 +68,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
69
68
|
childTime: 0,
|
|
70
69
|
depth: 0,
|
|
71
70
|
harvestTimeSeconds: agentRef.init.spa.harvestTimeSeconds || 10,
|
|
72
|
-
interactionsToHarvest: new _eventBuffer.EventBuffer(),
|
|
73
71
|
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
74
72
|
disableSpaFix: (agentRef.init.feature_flags || []).indexOf('disable-spa-fix') > -1
|
|
75
73
|
};
|
|
74
|
+
this.spaSerializerClass = new _serializer.Serializer(this);
|
|
75
|
+
const classThis = this;
|
|
76
76
|
let scheduler;
|
|
77
|
-
this.serializer = new _serializer.Serializer(this);
|
|
78
|
-
const {
|
|
79
|
-
state,
|
|
80
|
-
serializer
|
|
81
|
-
} = this;
|
|
82
77
|
const baseEE = _contextualEe.ee.get(agentRef.agentIdentifier); // <-- parent baseEE
|
|
83
78
|
const mutationEE = baseEE.get('mutation');
|
|
84
79
|
const promiseEE = baseEE.get('promise');
|
|
@@ -124,11 +119,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
124
119
|
|
|
125
120
|
this.waitForFlags(['spa']).then(([spaFlag]) => {
|
|
126
121
|
if (spaFlag) {
|
|
127
|
-
scheduler = new _harvestScheduler.HarvestScheduler(
|
|
128
|
-
onFinished:
|
|
122
|
+
scheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
|
|
123
|
+
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
124
|
+
getPayload: options => this.makeHarvestPayload(options.retry),
|
|
129
125
|
retryDelay: state.harvestTimeSeconds
|
|
130
126
|
}, this);
|
|
131
|
-
scheduler.harvest.on('events', onHarvestStarted);
|
|
132
127
|
this.drain();
|
|
133
128
|
} else {
|
|
134
129
|
this.blocked = true;
|
|
@@ -619,20 +614,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
619
614
|
});
|
|
620
615
|
setCurrentNode(null);
|
|
621
616
|
}
|
|
622
|
-
const classThis = this;
|
|
623
|
-
function onHarvestStarted(options) {
|
|
624
|
-
if (!state.interactionsToHarvest.hasData || classThis.blocked) return {};
|
|
625
|
-
var payload = serializer.serializeMultiple(state.interactionsToHarvest.buffer, 0, _navTiming.navTimingValues);
|
|
626
|
-
if (options.retry) state.interactionsToHarvest.hold();else state.interactionsToHarvest.clear();
|
|
627
|
-
return {
|
|
628
|
-
body: {
|
|
629
|
-
e: payload
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
function onHarvestFinished(result) {
|
|
634
|
-
if (result.sent && result.retry && state.interactionsToHarvest.held.hasData) state.interactionsToHarvest.unhold();else state.interactionsToHarvest.held.clear();
|
|
635
|
-
}
|
|
636
617
|
baseEE.on('spa-jserror', function (type, name, params, metrics) {
|
|
637
618
|
if (!state.currentNode) return;
|
|
638
619
|
params._interactionId = state.currentNode.interaction.id;
|
|
@@ -679,7 +660,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
679
660
|
interaction.root.attrs.firstContentfulPaint = _firstContentfulPaint.firstContentfulPaint.current.value;
|
|
680
661
|
}
|
|
681
662
|
baseEE.emit('interactionDone', [interaction, true]);
|
|
682
|
-
|
|
663
|
+
classThis.events.add(interaction);
|
|
683
664
|
let smCategory;
|
|
684
665
|
if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.routeChange) smCategory = 'RouteChange';else smCategory = 'Custom';
|
|
685
666
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ["Spa/Interaction/".concat(smCategory, "/Duration/Ms"), Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, _features.FEATURE_NAMES.metrics, baseEE);
|
|
@@ -687,5 +668,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
687
668
|
if (!scheduler) (0, _console.warn)(19);
|
|
688
669
|
}
|
|
689
670
|
}
|
|
671
|
+
serializer(eventBuffer) {
|
|
672
|
+
return this.spaSerializerClass.serializeMultiple(eventBuffer, 0, _navTiming.navTimingValues);
|
|
673
|
+
}
|
|
690
674
|
}
|
|
691
675
|
exports.Aggregate = Aggregate;
|
|
@@ -11,10 +11,16 @@ var _nreum = require("../../common/window/nreum");
|
|
|
11
11
|
var _drain = require("../../common/drain/drain");
|
|
12
12
|
var _featureFlags = require("../../common/util/feature-flags");
|
|
13
13
|
var _obfuscate = require("../../common/util/obfuscate");
|
|
14
|
+
var _eventBuffer = require("./event-buffer");
|
|
15
|
+
var _features = require("../../loaders/features/features");
|
|
14
16
|
class AggregateBase extends _featureBase.FeatureBase {
|
|
15
17
|
constructor(agentRef, featureName) {
|
|
16
18
|
super(agentRef.agentIdentifier, featureName);
|
|
17
19
|
this.agentRef = agentRef;
|
|
20
|
+
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
21
|
+
if ([_features.FEATURE_NAMES.jserrors, _features.FEATURE_NAMES.metrics].includes(this.featureName)) this.events = agentRef.sharedAggregator;
|
|
22
|
+
// PVE has no need for eventBuffer, and SessionTrace has its own storage mechanism.
|
|
23
|
+
else if (![_features.FEATURE_NAMES.pageViewEvent, _features.FEATURE_NAMES.sessionTrace].includes(this.featureName)) this.events = new _eventBuffer.EventBuffer();
|
|
18
24
|
this.checkConfiguration(agentRef);
|
|
19
25
|
this.obfuscator = agentRef.runtime.obfuscator;
|
|
20
26
|
}
|
|
@@ -51,6 +57,37 @@ class AggregateBase extends _featureBase.FeatureBase {
|
|
|
51
57
|
this.drained = true;
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
|
|
62
|
+
* @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
|
|
63
|
+
* @returns final payload, or undefined if there are no pending events
|
|
64
|
+
*/
|
|
65
|
+
makeHarvestPayload(shouldRetryOnFail = false, opts = {}) {
|
|
66
|
+
if (this.events.isEmpty(opts)) return;
|
|
67
|
+
// Other conditions and things to do when preparing harvest that is required.
|
|
68
|
+
if (this.preHarvestChecks && !this.preHarvestChecks()) return;
|
|
69
|
+
if (shouldRetryOnFail) this.events.save(opts);
|
|
70
|
+
const returnedData = this.events.get(opts);
|
|
71
|
+
// A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
|
|
72
|
+
const body = this.serializer ? this.serializer(returnedData) : returnedData;
|
|
73
|
+
this.events.clear(opts);
|
|
74
|
+
const payload = {
|
|
75
|
+
body
|
|
76
|
+
};
|
|
77
|
+
// Constructs the payload `qs` for relevant features on harvest.
|
|
78
|
+
if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(returnedData);
|
|
79
|
+
return payload;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Cleanup task after a harvest.
|
|
84
|
+
* @param {Boolean} harvestFailed - harvester flag to restore events in main buffer for retry later if request failed
|
|
85
|
+
*/
|
|
86
|
+
postHarvestCleanup(harvestFailed = false, opts = {}) {
|
|
87
|
+
if (harvestFailed) this.events.reloadSave(opts);
|
|
88
|
+
this.events.clearSave(opts);
|
|
89
|
+
}
|
|
90
|
+
|
|
54
91
|
/**
|
|
55
92
|
* Checks for additional `jsAttributes` items to support backward compatibility with implementations of the agent where
|
|
56
93
|
* loader configurations may appear after the loader code is executed.
|