@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
|
@@ -6,128 +6,77 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.EventBuffer = void 0;
|
|
7
7
|
var _stringify = require("../../common/util/stringify");
|
|
8
8
|
var _agentConstants = require("../../common/constants/agent-constants");
|
|
9
|
-
/**
|
|
10
|
-
* A container that keeps an event buffer and size with helper methods
|
|
11
|
-
* @typedef {Object} EventBuffer
|
|
12
|
-
* @property {number} size
|
|
13
|
-
* @property {*[]} buffer
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A container that holds, evaluates, and merges event objects for harvesting
|
|
18
|
-
*/
|
|
19
9
|
class EventBuffer {
|
|
20
|
-
/** @type {Object[]} */
|
|
21
10
|
#buffer = [];
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
#held;
|
|
11
|
+
#rawBytes = 0;
|
|
12
|
+
#bufferBackup;
|
|
13
|
+
#rawBytesBackup;
|
|
26
14
|
|
|
27
15
|
/**
|
|
28
|
-
*
|
|
29
|
-
* @param {number=} maxPayloadSize
|
|
16
|
+
* @param {number} maxPayloadSize
|
|
30
17
|
*/
|
|
31
18
|
constructor(maxPayloadSize = _agentConstants.MAX_PAYLOAD_SIZE) {
|
|
32
19
|
this.maxPayloadSize = maxPayloadSize;
|
|
33
20
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
get buffer() {
|
|
21
|
+
isEmpty() {
|
|
22
|
+
return this.#buffer.length === 0;
|
|
23
|
+
}
|
|
24
|
+
get() {
|
|
39
25
|
return this.#buffer;
|
|
40
26
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* bytes is read only, use the helper methods to add or clear buffer data
|
|
44
|
-
*/
|
|
45
|
-
get bytes() {
|
|
46
|
-
return this.#bytes;
|
|
27
|
+
byteSize() {
|
|
28
|
+
return this.#rawBytes;
|
|
47
29
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* held is another event buffer
|
|
51
|
-
*/
|
|
52
|
-
get held() {
|
|
53
|
-
if (!this.#held) this.#held = new EventBuffer(this.maxPayloadSize);
|
|
54
|
-
return this.#held;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns a boolean indicating whether the current size and buffer contain valid data
|
|
59
|
-
* @returns {boolean}
|
|
60
|
-
*/
|
|
61
|
-
get hasData() {
|
|
62
|
-
return this.buffer.length > 0 && this.bytes > 0;
|
|
30
|
+
wouldExceedMaxSize(incomingSize) {
|
|
31
|
+
return this.#rawBytes + incomingSize > this.maxPayloadSize;
|
|
63
32
|
}
|
|
64
33
|
|
|
65
34
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @
|
|
69
|
-
* @returns {EventBuffer} returns the event buffer for chaining
|
|
35
|
+
* Add feature-processed event to our buffer. If this event would cause our total raw size to exceed the set max payload size, it is dropped.
|
|
36
|
+
* @param {any} event - any primitive type or object
|
|
37
|
+
* @returns {Boolean} true if successfully added; false otherwise
|
|
70
38
|
*/
|
|
71
39
|
add(event) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
40
|
+
const addSize = (0, _stringify.stringify)(event)?.length || 0; // (estimate) # of bytes a directly stringified event it would take to send
|
|
41
|
+
if (this.#rawBytes + addSize > this.maxPayloadSize) return false;
|
|
74
42
|
this.#buffer.push(event);
|
|
75
|
-
this.#
|
|
76
|
-
return
|
|
43
|
+
this.#rawBytes += addSize;
|
|
44
|
+
return true;
|
|
77
45
|
}
|
|
78
46
|
|
|
79
47
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @returns {EventBuffer}
|
|
48
|
+
* Wipes the main buffer
|
|
82
49
|
*/
|
|
83
50
|
clear() {
|
|
84
|
-
this.#bytes = 0;
|
|
85
51
|
this.#buffer = [];
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Hold the buffer data in a new (child) EventBuffer (.held) to unblock the main buffer.
|
|
91
|
-
* This action clears the main buffer
|
|
92
|
-
* @returns {EventBuffer}
|
|
93
|
-
*/
|
|
94
|
-
hold() {
|
|
95
|
-
this.held.merge(this);
|
|
96
|
-
this.clear();
|
|
97
|
-
return this;
|
|
52
|
+
this.#rawBytes = 0;
|
|
98
53
|
}
|
|
99
54
|
|
|
100
55
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @returns {EventBuffer}
|
|
56
|
+
* Backup the buffered data and clear the main buffer
|
|
57
|
+
* @returns {Array} the events being backed up
|
|
104
58
|
*/
|
|
105
|
-
|
|
106
|
-
this
|
|
107
|
-
this
|
|
108
|
-
return this;
|
|
59
|
+
save() {
|
|
60
|
+
this.#bufferBackup = this.#buffer;
|
|
61
|
+
this.#rawBytesBackup = this.#rawBytes;
|
|
109
62
|
}
|
|
110
63
|
|
|
111
64
|
/**
|
|
112
|
-
*
|
|
113
|
-
* @param {EventBuffer} events an EventBuffer intended to merge with this EventBuffer
|
|
114
|
-
* @param {boolean} prepend if true, the supplied events will be prepended before the events of this class
|
|
115
|
-
* @returns {EventBuffer} returns the event buffer for chaining
|
|
65
|
+
* Wipes the backup buffer
|
|
116
66
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.#
|
|
120
|
-
this.#bytes += eventBuffer.#bytes;
|
|
121
|
-
return this;
|
|
67
|
+
clearSave() {
|
|
68
|
+
this.#bufferBackup = undefined;
|
|
69
|
+
this.#rawBytesBackup = undefined;
|
|
122
70
|
}
|
|
123
71
|
|
|
124
72
|
/**
|
|
125
|
-
*
|
|
126
|
-
* @param {number} size
|
|
127
|
-
* @returns {boolean}
|
|
73
|
+
* Prepend the backup buffer back into the main buffer
|
|
128
74
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
75
|
+
reloadSave() {
|
|
76
|
+
if (!this.#bufferBackup) return;
|
|
77
|
+
if (this.#rawBytesBackup + this.#rawBytes > this.maxPayloadSize) return;
|
|
78
|
+
this.#buffer = [...this.#bufferBackup, ...this.#buffer];
|
|
79
|
+
this.#rawBytes = this.#rawBytesBackup + this.#rawBytes;
|
|
131
80
|
}
|
|
132
81
|
}
|
|
133
82
|
exports.EventBuffer = EventBuffer;
|
|
@@ -102,11 +102,11 @@ class InstrumentBase extends _featureBase.FeatureBase {
|
|
|
102
102
|
try {
|
|
103
103
|
// Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
|
|
104
104
|
if (!agentRef.sharedAggregator) {
|
|
105
|
-
agentRef.sharedAggregator = Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "shared-aggregator" */'../../common/aggregate/aggregator')));
|
|
105
|
+
agentRef.sharedAggregator = Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "shared-aggregator" */'../../common/aggregate/event-aggregator')));
|
|
106
106
|
const {
|
|
107
|
-
|
|
107
|
+
EventAggregator
|
|
108
108
|
} = await agentRef.sharedAggregator;
|
|
109
|
-
agentRef.sharedAggregator = new
|
|
109
|
+
agentRef.sharedAggregator = new EventAggregator();
|
|
110
110
|
} else await agentRef.sharedAggregator; // if another feature is already importing the aggregator, wait for it to finish
|
|
111
111
|
|
|
112
112
|
if (!this.#shouldImportAgg(this.featureName, session)) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.featurePriority = exports.FEATURE_NAMES = void 0;
|
|
6
|
+
exports.featurePriority = exports.FEATURE_TO_ENDPOINT = exports.FEATURE_NAMES = void 0;
|
|
7
7
|
const FEATURE_NAMES = exports.FEATURE_NAMES = {
|
|
8
8
|
ajax: 'ajax',
|
|
9
9
|
genericEvents: 'generic_events',
|
|
@@ -38,4 +38,16 @@ const featurePriority = exports.featurePriority = {
|
|
|
38
38
|
[FEATURE_NAMES.sessionReplay]: 9,
|
|
39
39
|
[FEATURE_NAMES.logging]: 10,
|
|
40
40
|
[FEATURE_NAMES.genericEvents]: 11
|
|
41
|
+
};
|
|
42
|
+
const FEATURE_TO_ENDPOINT = exports.FEATURE_TO_ENDPOINT = {
|
|
43
|
+
[FEATURE_NAMES.pageViewTiming]: 'events',
|
|
44
|
+
[FEATURE_NAMES.ajax]: 'events',
|
|
45
|
+
[FEATURE_NAMES.spa]: 'events',
|
|
46
|
+
[FEATURE_NAMES.softNav]: 'events',
|
|
47
|
+
[FEATURE_NAMES.metrics]: 'jserrors',
|
|
48
|
+
[FEATURE_NAMES.jserrors]: 'jserrors',
|
|
49
|
+
[FEATURE_NAMES.sessionTrace]: 'browser/blobs',
|
|
50
|
+
[FEATURE_NAMES.sessionReplay]: 'browser/blobs',
|
|
51
|
+
[FEATURE_NAMES.logging]: 'browser/logs',
|
|
52
|
+
[FEATURE_NAMES.genericEvents]: 'ins'
|
|
41
53
|
};
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export class Aggregator extends SharedContext {
|
|
8
|
-
constructor(parent) {
|
|
9
|
-
super(parent);
|
|
5
|
+
export class Aggregator {
|
|
6
|
+
constructor() {
|
|
10
7
|
this.aggregatedData = {};
|
|
11
8
|
}
|
|
12
9
|
|
|
@@ -15,12 +12,14 @@ export class Aggregator extends SharedContext {
|
|
|
15
12
|
// metrics are the numeric values to be aggregated
|
|
16
13
|
|
|
17
14
|
store(type, name, params, newMetrics, customParams) {
|
|
18
|
-
var bucket = this
|
|
15
|
+
var bucket = this.#getBucket(type, name, params, customParams);
|
|
19
16
|
bucket.metrics = aggregateMetrics(newMetrics, bucket.metrics);
|
|
20
17
|
return bucket;
|
|
21
18
|
}
|
|
22
|
-
merge(type, name, metrics, params, customParams) {
|
|
23
|
-
var bucket = this
|
|
19
|
+
merge(type, name, metrics, params, customParams, overwriteParams = false) {
|
|
20
|
+
var bucket = this.#getBucket(type, name, params, customParams);
|
|
21
|
+
if (overwriteParams) bucket.params = params; // replace current params with incoming params obj
|
|
22
|
+
|
|
24
23
|
if (!bucket.metrics) {
|
|
25
24
|
bucket.metrics = metrics;
|
|
26
25
|
return;
|
|
@@ -45,11 +44,25 @@ export class Aggregator extends SharedContext {
|
|
|
45
44
|
});
|
|
46
45
|
}
|
|
47
46
|
storeMetric(type, name, params, value) {
|
|
48
|
-
var bucket = this
|
|
47
|
+
var bucket = this.#getBucket(type, name, params);
|
|
49
48
|
bucket.stats = updateMetric(value, bucket.stats);
|
|
50
49
|
return bucket;
|
|
51
50
|
}
|
|
52
|
-
|
|
51
|
+
|
|
52
|
+
// Get all listed types buckets and it deletes the retrieved content from the aggregatedData
|
|
53
|
+
take(types, deleteWhenRetrieved = true) {
|
|
54
|
+
var results = {};
|
|
55
|
+
var type = '';
|
|
56
|
+
var hasData = false;
|
|
57
|
+
for (var i = 0; i < types.length; i++) {
|
|
58
|
+
type = types[i];
|
|
59
|
+
results[type] = Object.values(this.aggregatedData[type] || {});
|
|
60
|
+
if (results[type].length) hasData = true;
|
|
61
|
+
if (deleteWhenRetrieved) delete this.aggregatedData[type];
|
|
62
|
+
}
|
|
63
|
+
return hasData ? results : null;
|
|
64
|
+
}
|
|
65
|
+
#getBucket(type, name, params, customParams) {
|
|
53
66
|
if (!this.aggregatedData[type]) this.aggregatedData[type] = {};
|
|
54
67
|
var bucket = this.aggregatedData[type][name];
|
|
55
68
|
if (!bucket) {
|
|
@@ -62,26 +75,6 @@ export class Aggregator extends SharedContext {
|
|
|
62
75
|
}
|
|
63
76
|
return bucket;
|
|
64
77
|
}
|
|
65
|
-
get(type, name) {
|
|
66
|
-
// if name is passed, get a single bucket
|
|
67
|
-
if (name) return this.aggregatedData[type] && this.aggregatedData[type][name];
|
|
68
|
-
// else, get all buckets of that type
|
|
69
|
-
return this.aggregatedData[type];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Like get, but for many types and it deletes the retrieved content from the aggregatedData
|
|
73
|
-
take(types) {
|
|
74
|
-
var results = {};
|
|
75
|
-
var type = '';
|
|
76
|
-
var hasData = false;
|
|
77
|
-
for (var i = 0; i < types.length; i++) {
|
|
78
|
-
type = types[i];
|
|
79
|
-
results[type] = Object.values(this.aggregatedData[type] || {});
|
|
80
|
-
if (results[type].length) hasData = true;
|
|
81
|
-
delete this.aggregatedData[type];
|
|
82
|
-
}
|
|
83
|
-
return hasData ? results : null;
|
|
84
|
-
}
|
|
85
78
|
}
|
|
86
79
|
function aggregateMetrics(newMetrics, oldMetrics) {
|
|
87
80
|
if (!oldMetrics) oldMetrics = {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Aggregator } from './aggregator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An extension of the Aggregator class that provides an interface similar to that of EventBuffer class.
|
|
5
|
+
* This typecasting allow features that uses Aggregator as their event handler to share the same AggregateBase.events utilization by those features.
|
|
6
|
+
*/
|
|
7
|
+
export class EventAggregator {
|
|
8
|
+
#aggregator = new Aggregator();
|
|
9
|
+
#savedNamesToBuckets = {};
|
|
10
|
+
isEmpty({
|
|
11
|
+
aggregatorTypes
|
|
12
|
+
}) {
|
|
13
|
+
if (!aggregatorTypes) return Object.keys(this.#aggregator.aggregatedData).length === 0;
|
|
14
|
+
return aggregatorTypes.every(type => !this.#aggregator.aggregatedData[type]); // no bucket exist for any of the types we're looking for
|
|
15
|
+
}
|
|
16
|
+
add(type, name, params, newMetrics, customParams) {
|
|
17
|
+
// Do we need to track byte size here like EventBuffer?
|
|
18
|
+
this.#aggregator.store(type, name, params, newMetrics, customParams);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
addMetric(type, name, params, value) {
|
|
22
|
+
this.#aggregator.storeMetric(type, name, params, value);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
save({
|
|
26
|
+
aggregatorTypes
|
|
27
|
+
}) {
|
|
28
|
+
const key = aggregatorTypes.toString(); // the stringified types serve as the key to each save call, e.g. ['err', 'ierr', 'xhr'] => 'err,ierr,xhr'
|
|
29
|
+
const backupAggregatedDataSubset = {};
|
|
30
|
+
aggregatorTypes.forEach(type => backupAggregatedDataSubset[type] = this.#aggregator.aggregatedData[type]); // make a subset of the aggregatedData for each of the types we want to save
|
|
31
|
+
this.#savedNamesToBuckets[key] = backupAggregatedDataSubset;
|
|
32
|
+
/*
|
|
33
|
+
{ 'err,ierr,xhr': {
|
|
34
|
+
'err': {
|
|
35
|
+
<aggregateHash>: { metrics: { count: 1, time, ... }, params: {}, custom: {} },
|
|
36
|
+
<otherHashName>: { metrics: { count: 1, ... }, ... }
|
|
37
|
+
},
|
|
38
|
+
'ierr': { ... },
|
|
39
|
+
'xhr': { ... }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
*/
|
|
43
|
+
}
|
|
44
|
+
get(opts) {
|
|
45
|
+
const aggregatorTypes = Array.isArray(opts) ? opts : opts.aggregatorTypes;
|
|
46
|
+
return this.#aggregator.take(aggregatorTypes, false);
|
|
47
|
+
}
|
|
48
|
+
clear({
|
|
49
|
+
aggregatorTypes
|
|
50
|
+
} = {}) {
|
|
51
|
+
if (!aggregatorTypes) {
|
|
52
|
+
this.#aggregator.aggregatedData = {};
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
aggregatorTypes.forEach(type => delete this.#aggregator.aggregatedData[type]);
|
|
56
|
+
}
|
|
57
|
+
reloadSave({
|
|
58
|
+
aggregatorTypes
|
|
59
|
+
}) {
|
|
60
|
+
const key = aggregatorTypes.toString();
|
|
61
|
+
const backupAggregatedDataSubset = this.#savedNamesToBuckets[key];
|
|
62
|
+
// Grabs the previously stored subset and merge it back into aggregatedData.
|
|
63
|
+
aggregatorTypes.forEach(type => {
|
|
64
|
+
Object.keys(backupAggregatedDataSubset[type] || {}).forEach(name => {
|
|
65
|
+
const bucket = backupAggregatedDataSubset[type][name];
|
|
66
|
+
// The older aka saved params take effect over the newer one. This is especially important when merging back for a failed harvest retry if, for example,
|
|
67
|
+
// the first-ever occurrence of an error is in the retry: it contains the params.stack_trace whereas the newer or current bucket.params would not.
|
|
68
|
+
this.#aggregator.merge(type, name, bucket.metrics, bucket.params, bucket.custom, true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
clearSave({
|
|
73
|
+
aggregatorTypes
|
|
74
|
+
}) {
|
|
75
|
+
const key = aggregatorTypes.toString();
|
|
76
|
+
delete this.#savedNamesToBuckets[key];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -72,9 +72,6 @@ const model = () => {
|
|
|
72
72
|
page_action: {
|
|
73
73
|
enabled: true
|
|
74
74
|
},
|
|
75
|
-
user_actions: {
|
|
76
|
-
enabled: true
|
|
77
|
-
},
|
|
78
75
|
page_view_event: {
|
|
79
76
|
enabled: true,
|
|
80
77
|
autoStart: true
|
|
@@ -84,6 +81,10 @@ const model = () => {
|
|
|
84
81
|
harvestTimeSeconds: 30,
|
|
85
82
|
autoStart: true
|
|
86
83
|
},
|
|
84
|
+
performance: {
|
|
85
|
+
capture_marks: false,
|
|
86
|
+
capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
|
|
87
|
+
},
|
|
87
88
|
privacy: {
|
|
88
89
|
cookies_enabled: true
|
|
89
90
|
},
|
|
@@ -167,7 +168,10 @@ const model = () => {
|
|
|
167
168
|
harvestTimeSeconds: 10,
|
|
168
169
|
autoStart: true
|
|
169
170
|
},
|
|
170
|
-
ssl: undefined
|
|
171
|
+
ssl: undefined,
|
|
172
|
+
user_actions: {
|
|
173
|
+
enabled: true
|
|
174
|
+
}
|
|
171
175
|
};
|
|
172
176
|
};
|
|
173
177
|
const _cache = {};
|
|
@@ -95,7 +95,7 @@ export class HarvestScheduler extends SharedContext {
|
|
|
95
95
|
let submitMethod;
|
|
96
96
|
let payload;
|
|
97
97
|
if (this.opts.getPayload) {
|
|
98
|
-
// Ajax
|
|
98
|
+
// Ajax, PVT, Softnav, Logging, SR & ST features provide a single callback function to get data for harvesting
|
|
99
99
|
submitMethod = submitData.getSubmitMethod({
|
|
100
100
|
isFinalHarvest: opts?.unload
|
|
101
101
|
});
|
|
@@ -120,11 +120,7 @@ export class Harvest extends SharedContext {
|
|
|
120
120
|
const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
|
|
121
121
|
const gzip = !!qs?.attributes?.includes('gzip');
|
|
122
122
|
if (!gzip) {
|
|
123
|
-
if (endpoint
|
|
124
|
-
body = body.e;
|
|
125
|
-
} else {
|
|
126
|
-
body = stringify(body);
|
|
127
|
-
}
|
|
123
|
+
if (endpoint !== 'events') body = stringify(body); // all features going to /events/ endpoint should already be serialized & stringified
|
|
128
124
|
/** Warn --once per endpoint-- if the agent tries to send large payloads */
|
|
129
125
|
if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(28, endpoint);
|
|
130
126
|
}
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* @typedef {object} HarvestPayload
|
|
13
13
|
* @property {object} qs Map of values that should be sent as part of the request query string.
|
|
14
14
|
* @property {object} body Map of values that should be sent as the body of the request.
|
|
15
|
-
* @property {string} body.e Special case of body used for browser interactions.
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
/**
|
|
@@ -8,44 +8,41 @@ import { handle } from '../../../common/event-emitter/handle';
|
|
|
8
8
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
9
9
|
import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list';
|
|
10
10
|
import { FEATURE_NAME } from '../constants';
|
|
11
|
-
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
11
|
+
import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
|
|
12
12
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
13
13
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
14
14
|
import { parseGQL } from './gql';
|
|
15
|
-
import {
|
|
16
|
-
import Chunk from './chunk';
|
|
17
|
-
import { EventBuffer } from '../../utils/event-buffer';
|
|
15
|
+
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer';
|
|
18
16
|
export class Aggregate extends AggregateBase {
|
|
19
17
|
static featureName = FEATURE_NAME;
|
|
20
18
|
constructor(agentRef) {
|
|
21
19
|
super(agentRef, FEATURE_NAME);
|
|
22
20
|
const harvestTimeSeconds = agentRef.init.ajax.harvestTimeSeconds || 10;
|
|
23
21
|
setDenyList(agentRef.runtime.denyList);
|
|
24
|
-
this.
|
|
25
|
-
this.spaAjaxEvents = {};
|
|
22
|
+
this.underSpaEvents = {};
|
|
26
23
|
const classThis = this;
|
|
27
24
|
|
|
28
25
|
// --- v Used by old spa feature
|
|
29
26
|
this.ee.on('interactionDone', (interaction, wasSaved) => {
|
|
30
|
-
if (!this.
|
|
27
|
+
if (!this.underSpaEvents[interaction.id]) return;
|
|
31
28
|
if (!wasSaved) {
|
|
32
29
|
// if the ixn was saved, then its ajax reqs are part of the payload whereas if it was discarded, it should still be harvested in the ajax feature itself
|
|
33
|
-
this.
|
|
30
|
+
this.underSpaEvents[interaction.id].forEach(item => this.events.add(item));
|
|
34
31
|
}
|
|
35
|
-
delete this.
|
|
32
|
+
delete this.underSpaEvents[interaction.id];
|
|
36
33
|
});
|
|
37
34
|
// --- ^
|
|
38
35
|
// --- v Used by new soft nav
|
|
39
|
-
registerHandler('returnAjax', event => this.
|
|
36
|
+
registerHandler('returnAjax', event => this.events.add(event), this.featureName, this.ee);
|
|
40
37
|
// --- ^
|
|
41
38
|
registerHandler('xhr', function () {
|
|
42
39
|
// the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
|
|
43
40
|
classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
|
|
44
41
|
}, this.featureName, this.ee);
|
|
45
42
|
this.waitForFlags([]).then(() => {
|
|
46
|
-
const scheduler = new HarvestScheduler(
|
|
47
|
-
onFinished: this.
|
|
48
|
-
getPayload: this.
|
|
43
|
+
const scheduler = new HarvestScheduler(FEATURE_TO_ENDPOINT[this.featureName], {
|
|
44
|
+
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
45
|
+
getPayload: options => this.makeHarvestPayload(options.retry)
|
|
49
46
|
}, this);
|
|
50
47
|
scheduler.startTimer(harvestTimeSeconds);
|
|
51
48
|
this.drain();
|
|
@@ -63,10 +60,11 @@ export class Aggregate extends AggregateBase {
|
|
|
63
60
|
}
|
|
64
61
|
const shouldCollect = shouldCollectEvent(params);
|
|
65
62
|
const shouldOmitAjaxMetrics = this.agentRef.init.feature_flags?.includes('ajax_metrics_deny_list');
|
|
63
|
+
const jserrorsInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.jserrors]);
|
|
66
64
|
|
|
67
|
-
//
|
|
68
|
-
if (shouldCollect || !shouldOmitAjaxMetrics) {
|
|
69
|
-
this.agentRef.sharedAggregator.
|
|
65
|
+
// Report ajax timeslice metric (to be harvested by jserrors feature, but only if it's running).
|
|
66
|
+
if (jserrorsInUse && (shouldCollect || !shouldOmitAjaxMetrics)) {
|
|
67
|
+
this.agentRef.sharedAggregator.add('xhr', hash, params, metrics);
|
|
70
68
|
}
|
|
71
69
|
if (!shouldCollect) {
|
|
72
70
|
if (params.hostname === this.agentRef.info.errorBeacon || this.agentRef.init.proxy?.beacon && params.hostname === this.agentRef.init.proxy.beacon) {
|
|
@@ -105,62 +103,55 @@ export class Aggregate extends AggregateBase {
|
|
|
105
103
|
query: ctx.parsedOrigin?.search
|
|
106
104
|
});
|
|
107
105
|
if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
108
|
-
const softNavInUse = Boolean(
|
|
106
|
+
const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav]);
|
|
109
107
|
if (softNavInUse) {
|
|
110
108
|
// For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
|
|
111
109
|
handle('ajax', [event], undefined, FEATURE_NAMES.softNav, this.ee);
|
|
112
110
|
} else if (ctx.spaNode) {
|
|
113
111
|
// For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
114
112
|
const interactionId = ctx.spaNode.interaction.id;
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
113
|
+
this.underSpaEvents[interactionId] ??= [];
|
|
114
|
+
this.underSpaEvents[interactionId].push(event);
|
|
117
115
|
} else {
|
|
118
|
-
this.
|
|
116
|
+
this.events.add(event);
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// Check if the current payload string is too big, if so then run getPayload again with more buckets.
|
|
156
|
-
return tooBig ? this.#getPayload(events, ++numberOfChunks) : payload;
|
|
157
|
-
function splitChunks(arr, chunkSize) {
|
|
158
|
-
chunkSize = chunkSize || arr.length;
|
|
159
|
-
const chunks = [];
|
|
160
|
-
for (let i = 0, len = arr.length; i < len; i += chunkSize) {
|
|
161
|
-
chunks.push(new Chunk(arr.slice(i, i + chunkSize), this));
|
|
119
|
+
serializer(eventBuffer) {
|
|
120
|
+
const addString = getAddStringContext(this.agentIdentifier);
|
|
121
|
+
let payload = 'bel.7;';
|
|
122
|
+
for (let i = 0; i < eventBuffer.length; i++) {
|
|
123
|
+
const event = eventBuffer[i];
|
|
124
|
+
const fields = [numeric(event.startTime), numeric(event.endTime - event.startTime), numeric(0),
|
|
125
|
+
// callbackEnd
|
|
126
|
+
numeric(0),
|
|
127
|
+
// no callbackDuration for non-SPA events
|
|
128
|
+
addString(event.method), numeric(event.status), addString(event.domain), addString(event.path), numeric(event.requestSize), numeric(event.responseSize), event.type === 'fetch' ? 1 : '', addString(0),
|
|
129
|
+
// nodeId
|
|
130
|
+
nullable(event.spanId, addString, true) +
|
|
131
|
+
// guid
|
|
132
|
+
nullable(event.traceId, addString, true) +
|
|
133
|
+
// traceId
|
|
134
|
+
nullable(event.spanTimestamp, numeric, false) // timestamp
|
|
135
|
+
];
|
|
136
|
+
let insert = '2,';
|
|
137
|
+
|
|
138
|
+
// Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
|
|
139
|
+
const jsAttributes = this.agentRef.info.jsAttributes;
|
|
140
|
+
|
|
141
|
+
// add custom attributes
|
|
142
|
+
// gql decorators are added as custom attributes to alleviate need for new BEL schema
|
|
143
|
+
const attrParts = addCustomAttributes({
|
|
144
|
+
...(jsAttributes || {}),
|
|
145
|
+
...(event.gql || {})
|
|
146
|
+
}, addString);
|
|
147
|
+
fields.unshift(numeric(attrParts.length));
|
|
148
|
+
insert += fields.join(',');
|
|
149
|
+
if (attrParts && attrParts.length > 0) {
|
|
150
|
+
insert += ';' + attrParts.join(';');
|
|
162
151
|
}
|
|
163
|
-
|
|
152
|
+
if (i + 1 < eventBuffer.length) insert += ';';
|
|
153
|
+
payload += insert;
|
|
164
154
|
}
|
|
155
|
+
return payload;
|
|
165
156
|
}
|
|
166
157
|
}
|