@newrelic/browser-agent 1.293.0 → 1.294.0-rc.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 +12 -0
- package/dist/cjs/common/aggregate/event-aggregator.js +3 -0
- package/dist/cjs/common/config/runtime.js +4 -0
- package/dist/cjs/common/constants/agent-constants.js +1 -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/event-emitter/contextual-ee.js +3 -2
- package/dist/cjs/common/url/clean-url.js +2 -1
- package/dist/cjs/common/util/attribute-size.js +31 -0
- package/dist/cjs/features/ajax/aggregate/index.js +6 -0
- package/dist/cjs/features/generic_events/aggregate/index.js +1 -14
- package/dist/cjs/features/generic_events/constants.js +1 -3
- package/dist/cjs/features/logging/aggregate/index.js +2 -23
- package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -0
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +4 -4
- package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +0 -4
- package/dist/cjs/features/soft_navigations/aggregate/index.js +12 -12
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +3 -2
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +18 -11
- package/dist/cjs/features/spa/aggregate/interaction.js +1 -1
- package/dist/cjs/features/utils/agent-session.js +13 -5
- package/dist/cjs/features/utils/aggregate-base.js +25 -2
- package/dist/cjs/features/utils/event-buffer.js +12 -3
- package/dist/cjs/features/utils/event-store-manager.js +6 -6
- package/dist/esm/common/aggregate/event-aggregator.js +3 -0
- package/dist/esm/common/config/runtime.js +4 -0
- package/dist/esm/common/constants/agent-constants.js +1 -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/event-emitter/contextual-ee.js +3 -2
- package/dist/esm/common/url/clean-url.js +2 -1
- package/dist/esm/common/util/attribute-size.js +24 -0
- package/dist/esm/features/ajax/aggregate/index.js +6 -0
- package/dist/esm/features/generic_events/aggregate/index.js +1 -14
- package/dist/esm/features/generic_events/constants.js +0 -2
- package/dist/esm/features/logging/aggregate/index.js +2 -23
- package/dist/esm/features/page_view_timing/aggregate/index.js +2 -0
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +4 -4
- package/dist/esm/features/soft_navigations/aggregate/bel-node.js +0 -4
- package/dist/esm/features/soft_navigations/aggregate/index.js +12 -12
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +3 -2
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +18 -11
- package/dist/esm/features/spa/aggregate/interaction.js +1 -1
- package/dist/esm/features/utils/agent-session.js +13 -5
- package/dist/esm/features/utils/aggregate-base.js +25 -2
- package/dist/esm/features/utils/event-buffer.js +12 -3
- package/dist/esm/features/utils/event-store-manager.js +7 -7
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/aggregate/event-aggregator.d.ts +1 -0
- package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -1
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/agent-constants.d.ts +1 -1
- package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
- package/dist/types/common/url/clean-url.d.ts +2 -2
- package/dist/types/common/url/clean-url.d.ts.map +1 -1
- package/dist/types/common/util/attribute-size.d.ts +4 -0
- package/dist/types/common/util/attribute-size.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +0 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +0 -2
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/logging/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/soft_navigations/aggregate/ajax-node.d.ts +2 -2
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +0 -3
- package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +0 -1
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +8 -2
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/utils/agent-session.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +10 -0
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/event-buffer.d.ts +5 -2
- package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
- package/dist/types/features/utils/event-store-manager.d.ts +3 -3
- package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/common/aggregate/event-aggregator.js +4 -0
- package/src/common/config/runtime.js +2 -0
- package/src/common/constants/agent-constants.js +1 -1
- package/src/common/event-emitter/contextual-ee.js +3 -2
- package/src/common/url/clean-url.js +2 -1
- package/src/common/util/attribute-size.js +24 -0
- package/src/features/ajax/aggregate/index.js +7 -0
- package/src/features/generic_events/aggregate/index.js +1 -12
- package/src/features/generic_events/constants.js +0 -2
- package/src/features/logging/aggregate/index.js +3 -20
- package/src/features/page_view_timing/aggregate/index.js +3 -0
- package/src/features/soft_navigations/aggregate/ajax-node.js +4 -4
- package/src/features/soft_navigations/aggregate/bel-node.js +0 -5
- package/src/features/soft_navigations/aggregate/index.js +13 -10
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +3 -2
- package/src/features/soft_navigations/aggregate/interaction.js +14 -8
- package/src/features/spa/aggregate/interaction.js +1 -1
- package/src/features/utils/agent-session.js +13 -2
- package/src/features/utils/aggregate-base.js +22 -2
- package/src/features/utils/event-buffer.js +12 -3
- package/src/features/utils/event-store-manager.js +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.294.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.293.0...v1.294.0) (2025-07-23)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Harvest early ([#1513](https://github.com/newrelic/newrelic-browser-agent/issues/1513)) ([d347eaa](https://github.com/newrelic/newrelic-browser-agent/commit/d347eaa76e26a9a4fc8e5c191de6a3c737712c68))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* report empty previousUrl as undefined ([#1526](https://github.com/newrelic/newrelic-browser-agent/issues/1526)) ([e3ca824](https://github.com/newrelic/newrelic-browser-agent/commit/e3ca824847e8f91a5da8cca8bf7717001c16929b))
|
|
17
|
+
|
|
6
18
|
## [1.293.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.292.1...v1.293.0) (2025-07-01)
|
|
7
19
|
|
|
8
20
|
|
|
@@ -17,6 +17,9 @@ var _aggregator = require("./aggregator");
|
|
|
17
17
|
class EventAggregator {
|
|
18
18
|
#aggregator = new _aggregator.Aggregator();
|
|
19
19
|
#savedNamesToBuckets = {};
|
|
20
|
+
byteSize() {
|
|
21
|
+
return 0; // EventAggregator does not currently track byte size like EventBuffer does, but will in a future update. This is a placeholder to maintain interface consistency.
|
|
22
|
+
}
|
|
20
23
|
isEmpty({
|
|
21
24
|
aggregatorTypes
|
|
22
25
|
}) {
|
|
@@ -42,6 +42,10 @@ const RuntimeModel = {
|
|
|
42
42
|
releaseIds: {},
|
|
43
43
|
session: undefined,
|
|
44
44
|
timeKeeper: undefined,
|
|
45
|
+
/** a proxy is set in agent-session to track jsAttributes changes for harvesting mechanics */
|
|
46
|
+
jsAttributesMetadata: {
|
|
47
|
+
bytes: 0
|
|
48
|
+
},
|
|
45
49
|
get harvestCount() {
|
|
46
50
|
return ++_harvestCount;
|
|
47
51
|
}
|
|
@@ -8,6 +8,6 @@ exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = vo
|
|
|
8
8
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
|
-
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE =
|
|
11
|
+
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 16000;
|
|
12
12
|
const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
|
|
13
13
|
const DEFAULT_KEY = exports.DEFAULT_KEY = 'NR_CONTAINER_AGENT';
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.
|
|
20
|
+
const VERSION = exports.VERSION = "1.294.0-rc.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.
|
|
20
|
+
const VERSION = exports.VERSION = "1.294.0-rc.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -87,12 +87,13 @@ function ee(old, debugId) {
|
|
|
87
87
|
if (old && bubble) old.emit(type, args, contextOrStore);
|
|
88
88
|
var ctx = context(contextOrStore);
|
|
89
89
|
var handlersArray = listeners(type);
|
|
90
|
-
var len = handlersArray.length;
|
|
91
90
|
|
|
92
91
|
// Apply each handler function in the order they were added
|
|
93
92
|
// to the context with the arguments
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
handlersArray.forEach(handler => {
|
|
95
|
+
handler.apply(ctx, args);
|
|
96
|
+
});
|
|
96
97
|
|
|
97
98
|
// Buffer after emitting for consistent ordering
|
|
98
99
|
var bufferGroup = getBuffer()[bufferGroupMap[type]];
|
|
@@ -14,10 +14,11 @@ var patternWithoutHash = /([^?#]*)().*/;
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Cleans a URL by removing the query string and fragment (hash portion).
|
|
17
|
-
* @param {string} url - The original URL to be cleaned.
|
|
17
|
+
* @param {string} [url] - The original URL to be cleaned.
|
|
18
18
|
* @param {boolean} [keepHash=false] - Whether to preserve the hash portion of the URL.
|
|
19
19
|
* @returns {string} The cleaned URL.
|
|
20
20
|
*/
|
|
21
21
|
function cleanURL(url, keepHash) {
|
|
22
|
+
if (!url) return url;
|
|
22
23
|
return url.replace(keepHash ? patternWithHash : patternWithoutHash, '$1$2');
|
|
23
24
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.trackObjectAttributeSize = trackObjectAttributeSize;
|
|
7
|
+
var _stringify = require("./stringify");
|
|
8
|
+
/**
|
|
9
|
+
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
10
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function trackObjectAttributeSize(parent, object) {
|
|
14
|
+
const originalAttribute = parent[object] ??= {};
|
|
15
|
+
const output = {
|
|
16
|
+
bytes: Object.keys(originalAttribute).reduce((acc, key) => acc + key.length + (0, _stringify.stringify)(originalAttribute[key]).length, 0)
|
|
17
|
+
};
|
|
18
|
+
// proxy attribute to calculate its size when changed
|
|
19
|
+
parent[object] = new Proxy(originalAttribute, {
|
|
20
|
+
set(target, prop, value) {
|
|
21
|
+
output.bytes += prop.length + (0, _stringify.stringify)(value).length;
|
|
22
|
+
target[prop] = value;
|
|
23
|
+
return true;
|
|
24
|
+
},
|
|
25
|
+
deleteProperty(target, prop) {
|
|
26
|
+
output.bytes -= prop.length + (0, _stringify.stringify)(target[prop]).length;
|
|
27
|
+
return delete target[prop];
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return output;
|
|
31
|
+
}
|
|
@@ -25,6 +25,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
25
25
|
(0, _denyList.setDenyList)(agentRef.runtime.denyList);
|
|
26
26
|
this.underSpaEvents = {};
|
|
27
27
|
const classThis = this;
|
|
28
|
+
if (!agentRef.init.ajax.block_internal) {
|
|
29
|
+
// if the agent is tracking ITSELF, it can spawn endless ajax requests early if they are large from custom attributes, so we just disable early harvest for ajax in this case.
|
|
30
|
+
super.canHarvestEarly = false;
|
|
31
|
+
} else {
|
|
32
|
+
super.customAttributesAreSeparate = true;
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
// --- v Used by old spa feature
|
|
30
36
|
this.ee.on('interactionDone', (interaction, wasSaved) => {
|
|
@@ -12,7 +12,6 @@ var _aggregateBase = require("../../utils/aggregate-base");
|
|
|
12
12
|
var _console = require("../../../common/util/console");
|
|
13
13
|
var _now = require("../../../common/timing/now");
|
|
14
14
|
var _registerHandler = require("../../../common/event-emitter/register-handler");
|
|
15
|
-
var _constants2 = require("../../metrics/constants");
|
|
16
15
|
var _traverse = require("../../../common/util/traverse");
|
|
17
16
|
var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
|
|
18
17
|
var _iframe = require("../../../common/dom/iframe");
|
|
@@ -26,7 +25,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
26
25
|
static featureName = _constants.FEATURE_NAME;
|
|
27
26
|
constructor(agentRef) {
|
|
28
27
|
super(agentRef, _constants.FEATURE_NAME);
|
|
29
|
-
this.eventsPerHarvest = 1000;
|
|
30
28
|
this.referrerUrl = _runtime.isBrowserScope && document.referrer ? (0, _cleanUrl.cleanURL)(document.referrer) : undefined;
|
|
31
29
|
this.waitForFlags(['ins']).then(([ins]) => {
|
|
32
30
|
if (!ins) {
|
|
@@ -284,18 +282,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
284
282
|
/** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
|
|
285
283
|
...obj
|
|
286
284
|
};
|
|
287
|
-
|
|
288
|
-
if (!addedEvent && !this.events.isEmpty(undefined, targetEntityGuid)) {
|
|
289
|
-
/** could not add the event because it pushed the buffer over the limit
|
|
290
|
-
* so we harvest early, and try to add it again now that the buffer is cleared
|
|
291
|
-
* if it fails again, we do nothing
|
|
292
|
-
*/
|
|
293
|
-
this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
|
|
294
|
-
this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
295
|
-
targetEntityGuid
|
|
296
|
-
});
|
|
297
|
-
this.events.add(eventAttributes);
|
|
298
|
-
}
|
|
285
|
+
this.events.add(eventAttributes, targetEntityGuid);
|
|
299
286
|
}
|
|
300
287
|
serializer(eventBuffer) {
|
|
301
288
|
return (0, _traverse.applyFnToProps)({
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RESERVED_EVENT_TYPES = exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.
|
|
6
|
+
exports.RESERVED_EVENT_TYPES = exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
/**
|
|
9
9
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
@@ -11,8 +11,6 @@ var _features = require("../../loaders/features/features");
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.genericEvents;
|
|
14
|
-
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
|
|
15
|
-
const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
|
|
16
14
|
const OBSERVED_EVENTS = exports.OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'];
|
|
17
15
|
const OBSERVED_WINDOW_EVENTS = exports.OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
|
|
18
16
|
const RAGE_CLICK_THRESHOLD_EVENTS = exports.RAGE_CLICK_THRESHOLD_EVENTS = 4;
|
|
@@ -12,7 +12,6 @@ var _constants = require("../constants");
|
|
|
12
12
|
var _log = require("../shared/log");
|
|
13
13
|
var _utils = require("../shared/utils");
|
|
14
14
|
var _traverse = require("../../../common/util/traverse");
|
|
15
|
-
var _agentConstants = require("../../../common/constants/agent-constants");
|
|
16
15
|
var _target = require("../../../common/util/target");
|
|
17
16
|
var _constants2 = require("../../../common/session/constants");
|
|
18
17
|
var _constants3 = require("../../session_replay/constants");
|
|
@@ -27,6 +26,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
27
26
|
constructor(agentRef) {
|
|
28
27
|
super(agentRef, _constants.FEATURE_NAME);
|
|
29
28
|
this.isSessionTrackingEnabled = (0, _featureGates.canEnableSessionTracking)(agentRef.init) && agentRef.runtime.session;
|
|
29
|
+
super.customAttributesAreSeparate = true;
|
|
30
30
|
|
|
31
31
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
32
32
|
this.ee.on(_constants2.SESSION_EVENTS.RESET, () => {
|
|
@@ -90,28 +90,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
90
90
|
}
|
|
91
91
|
if (typeof message !== 'string' || !message) return (0, _console.warn)(32);
|
|
92
92
|
const log = new _log.Log(Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp)), message, attributes, level);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const failToHarvestMessage = 'Logging/Harvest/Failed/Seen';
|
|
96
|
-
if (logBytes > _agentConstants.MAX_PAYLOAD_SIZE) {
|
|
97
|
-
// cannot possibly send this, even with an empty buffer
|
|
98
|
-
this.reportSupportabilityMetric(failToHarvestMessage, logBytes);
|
|
99
|
-
(0, _console.warn)(31, log.message.slice(0, 25) + '...');
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
if (this.events.wouldExceedMaxSize(logBytes, targetEntityGuid)) {
|
|
103
|
-
this.reportSupportabilityMetric('Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes);
|
|
104
|
-
this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
105
|
-
targetEntityGuid
|
|
106
|
-
}); // force a harvest synchronously to try adding again
|
|
107
|
-
}
|
|
108
|
-
if (!this.events.add(log, targetEntityGuid)) {
|
|
109
|
-
// still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
|
|
110
|
-
this.reportSupportabilityMetric(failToHarvestMessage, logBytes);
|
|
111
|
-
(0, _console.warn)(31, log.message.slice(0, 25) + '...');
|
|
112
|
-
} else {
|
|
113
|
-
this.reportSupportabilityMetric('Logging/Event/Added/Seen');
|
|
114
|
-
}
|
|
93
|
+
this.events.add(log, targetEntityGuid);
|
|
115
94
|
}
|
|
116
95
|
serializer(eventBuffer, targetEntityGuid) {
|
|
117
96
|
const target = this.agentRef.runtime.entityManager.get(targetEntityGuid);
|
|
@@ -38,6 +38,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
38
38
|
super(agentRef, _constants.FEATURE_NAME);
|
|
39
39
|
this.curSessEndRecorded = false;
|
|
40
40
|
this.firstIxnRecorded = false;
|
|
41
|
+
super.customAttributesAreSeparate = true;
|
|
41
42
|
(0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
|
|
42
43
|
// Add the time of _window pagehide event_ firing to the next PVT harvest == NRDB windowUnload attr:
|
|
43
44
|
(0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.addTiming('unload', msTimestamp, null), this.featureName, this.ee);
|
|
@@ -139,6 +140,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
139
140
|
|
|
140
141
|
// serialize array of timing data
|
|
141
142
|
serializer(eventBuffer) {
|
|
143
|
+
if (!eventBuffer?.length) return '';
|
|
142
144
|
var addString = (0, _belSerializer.getAddStringContext)(this.agentRef.runtime.obfuscator);
|
|
143
145
|
var payload = 'bel.6;';
|
|
144
146
|
for (var i = 0; i < eventBuffer.length; i++) {
|
|
@@ -13,8 +13,8 @@ var _belNode = require("./bel-node");
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
class AjaxNode extends _belNode.BelNode {
|
|
16
|
-
constructor(
|
|
17
|
-
super(
|
|
16
|
+
constructor(ajaxEvent) {
|
|
17
|
+
super();
|
|
18
18
|
this.belType = _constants.NODE_TYPE.AJAX;
|
|
19
19
|
this.method = ajaxEvent.method;
|
|
20
20
|
this.status = ajaxEvent.status;
|
|
@@ -30,8 +30,8 @@ class AjaxNode extends _belNode.BelNode {
|
|
|
30
30
|
this.start = ajaxEvent.startTime; // 5000 --- 5500 --> 10500
|
|
31
31
|
this.end = ajaxEvent.endTime;
|
|
32
32
|
}
|
|
33
|
-
serialize(parentStartTimestamp) {
|
|
34
|
-
const addString = (0, _belSerializer.getAddStringContext)(
|
|
33
|
+
serialize(parentStartTimestamp, agentRef) {
|
|
34
|
+
const addString = (0, _belSerializer.getAddStringContext)(agentRef.runtime.obfuscator);
|
|
35
35
|
const nodeList = [];
|
|
36
36
|
|
|
37
37
|
// IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
|
|
@@ -25,6 +25,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
25
25
|
domObserver
|
|
26
26
|
}) {
|
|
27
27
|
super(agentRef, _constants.FEATURE_NAME);
|
|
28
|
+
super.customAttributesAreSeparate = true;
|
|
28
29
|
this.interactionsToHarvest = this.events;
|
|
29
30
|
this.domObserver = domObserver;
|
|
30
31
|
this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentRef);
|
|
@@ -33,7 +34,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
33
34
|
if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true; // mark the hard page load as first of its session
|
|
34
35
|
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
35
36
|
const ixn = this.initialPageLoadInteraction;
|
|
36
|
-
this.
|
|
37
|
+
this.events.add(ixn); // add the iPL ixn to the buffer for harvest
|
|
37
38
|
this.initialPageLoadInteraction = null;
|
|
38
39
|
});
|
|
39
40
|
_timeToFirstByte.timeToFirstByte.subscribe(({
|
|
@@ -80,7 +81,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
80
81
|
let firstIxnStartTime;
|
|
81
82
|
const serializedIxnList = [];
|
|
82
83
|
for (const interaction of eventBuffer) {
|
|
83
|
-
serializedIxnList.push(interaction.serialize(firstIxnStartTime));
|
|
84
|
+
serializedIxnList.push(interaction.serialize(firstIxnStartTime, this.agentRef));
|
|
84
85
|
if (firstIxnStartTime === undefined) firstIxnStartTime = Math.floor(interaction.start); // careful not to match or overwrite on 0 value!
|
|
85
86
|
}
|
|
86
87
|
return "bel.7;".concat(serializedIxnList.join(';'));
|
|
@@ -91,7 +92,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
91
92
|
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing, e.g. by 'waitForEnd' api option
|
|
92
93
|
|
|
93
94
|
const oldURL = eventName === _constants.INTERACTION_TRIGGERS[3] ? this.latestHistoryUrl : undefined; // see related comment in 'newURL' handler above, 'popstate'
|
|
94
|
-
this.interactionInProgress = new _interaction.Interaction(
|
|
95
|
+
this.interactionInProgress = new _interaction.Interaction(eventName, startedAt, this.latestRouteSetByApi, oldURL);
|
|
95
96
|
if (eventName === _constants.INTERACTION_TRIGGERS[0]) {
|
|
96
97
|
// 'click'
|
|
97
98
|
const sourceElemText = getActionText(sourceElem);
|
|
@@ -107,7 +108,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
107
108
|
setClosureHandlers() {
|
|
108
109
|
this.interactionInProgress.on('finished', () => {
|
|
109
110
|
const ref = this.interactionInProgress;
|
|
110
|
-
this.
|
|
111
|
+
this.events.add(this.interactionInProgress); // add the ixn to the buffer for harvest
|
|
111
112
|
this.interactionInProgress = null;
|
|
112
113
|
this.domObserver.disconnect(); // can stop observing whenever our interaction logic completes a cycle
|
|
113
114
|
|
|
@@ -134,9 +135,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
134
135
|
*/
|
|
135
136
|
if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress;
|
|
136
137
|
let saveIxn;
|
|
137
|
-
const [
|
|
138
|
-
|
|
139
|
-
}] = this.interactionsToHarvest.get();
|
|
138
|
+
const interactionsBuffer = this.interactionsToHarvest.get()?.[0]?.data;
|
|
139
|
+
if (!interactionsBuffer) return undefined; // no interactions have been staged yet, so nothing to search through)
|
|
140
140
|
for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) {
|
|
141
141
|
// reverse search for the latest completed interaction for efficiency
|
|
142
142
|
const finishedInteraction = interactionsBuffer[idx];
|
|
@@ -161,15 +161,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
161
161
|
// no interaction was happening when this ajax started, so give it back to Ajax feature for processing
|
|
162
162
|
(0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee);
|
|
163
163
|
} else {
|
|
164
|
-
if (associatedInteraction.status === _constants.INTERACTION_STATUS.FIN) processAjax(
|
|
164
|
+
if (associatedInteraction.status === _constants.INTERACTION_STATUS.FIN) processAjax(event, associatedInteraction); // tack ajax onto the ixn object awaiting harvest
|
|
165
165
|
else {
|
|
166
166
|
// same thing as above, just at a later time -- if the interaction in progress is cancelled, just send the event back to ajax feat unmodified
|
|
167
|
-
associatedInteraction.on('finished', () => processAjax(
|
|
167
|
+
associatedInteraction.on('finished', () => processAjax(event, associatedInteraction));
|
|
168
168
|
associatedInteraction.on('cancelled', () => (0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee));
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
function processAjax(
|
|
172
|
-
const newNode = new _ajaxNode.AjaxNode(
|
|
171
|
+
function processAjax(event, parentInteraction) {
|
|
172
|
+
const newNode = new _ajaxNode.AjaxNode(event);
|
|
173
173
|
parentInteraction.addChild(newNode);
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -208,7 +208,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
208
208
|
if (this.associatedInteraction?.trigger === _constants.IPL_TRIGGER_NAME) this.associatedInteraction = null; // the api get-interaction method cannot target IPL
|
|
209
209
|
if (!this.associatedInteraction) {
|
|
210
210
|
// This new api-driven interaction will be the target of any subsequent .interaction() call, until it is closed by EITHER .end() OR the regular seenHistoryAndDomChange process.
|
|
211
|
-
this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(
|
|
211
|
+
this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(_constants.API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
|
|
212
212
|
thisClass.domObserver.observe(document.body, {
|
|
213
213
|
attributes: true,
|
|
214
214
|
childList: true,
|
|
@@ -17,10 +17,11 @@ var _constants = require("../constants");
|
|
|
17
17
|
|
|
18
18
|
class InitialPageLoadInteraction extends _interaction.Interaction {
|
|
19
19
|
constructor(agentRef) {
|
|
20
|
-
super(
|
|
20
|
+
super(_constants.IPL_TRIGGER_NAME, 0, null);
|
|
21
21
|
this.queueTime = agentRef.info.queueTime;
|
|
22
22
|
this.appTime = agentRef.info.applicationTime;
|
|
23
|
-
|
|
23
|
+
/** @type {string|undefined} we assign as undefined if no referrer value is available so that URL grouping is not applied to an empty string at ingest */
|
|
24
|
+
this.oldURL = document.referrer || undefined;
|
|
24
25
|
}
|
|
25
26
|
get firstPaint() {
|
|
26
27
|
return _firstPaint.firstPaint.current.value;
|
|
@@ -36,8 +36,8 @@ class Interaction extends _belNode.BelNode {
|
|
|
36
36
|
keepOpenUntilEndApi = false;
|
|
37
37
|
onDone = [];
|
|
38
38
|
cancellationTimer;
|
|
39
|
-
constructor(
|
|
40
|
-
super(
|
|
39
|
+
constructor(uiEvent, uiEventTimestamp, currentRouteKnown, currentUrl) {
|
|
40
|
+
super();
|
|
41
41
|
this.belType = _constants.NODE_TYPE.INTERACTION;
|
|
42
42
|
this.trigger = uiEvent;
|
|
43
43
|
this.start = uiEventTimestamp;
|
|
@@ -78,10 +78,6 @@ class Interaction extends _belNode.BelNode {
|
|
|
78
78
|
#finish(customEndTime = 0) {
|
|
79
79
|
clearTimeout(this.cancellationTimer);
|
|
80
80
|
this.end = Math.max(this.domTimestamp, this.historyTimestamp, customEndTime);
|
|
81
|
-
this.customAttributes = {
|
|
82
|
-
...this.info.jsAttributes,
|
|
83
|
-
...this.customAttributes
|
|
84
|
-
}; // attrs specific to this interaction should have precedence over the general custom attrs
|
|
85
81
|
this.status = _constants.INTERACTION_STATUS.FIN;
|
|
86
82
|
|
|
87
83
|
// Run all the callbacks awaiting this interaction to finish.
|
|
@@ -112,9 +108,16 @@ class Interaction extends _belNode.BelNode {
|
|
|
112
108
|
get firstPaint() {}
|
|
113
109
|
get firstContentfulPaint() {}
|
|
114
110
|
get navTiming() {}
|
|
115
|
-
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Serializes (BEL) the interaction data for transmission.
|
|
114
|
+
* @param {Number} firstStartTimeOfPayload timestamp
|
|
115
|
+
* @param {Agent} agentRef Pass in the agent reference directly so that the event itself doesnt need to store the pointers and ruin the evaluation of the event size by including unused object references.
|
|
116
|
+
* @returns {String} A string that is the serialized representation of this interaction.
|
|
117
|
+
*/
|
|
118
|
+
serialize(firstStartTimeOfPayload, agentRef) {
|
|
116
119
|
const isFirstIxnOfPayload = firstStartTimeOfPayload === undefined;
|
|
117
|
-
const addString = (0, _belSerializer.getAddStringContext)(
|
|
120
|
+
const addString = (0, _belSerializer.getAddStringContext)(agentRef.runtime.obfuscator);
|
|
118
121
|
const nodeList = [];
|
|
119
122
|
let ixnType;
|
|
120
123
|
if (this.trigger === _constants.IPL_TRIGGER_NAME) ixnType = _constants.INTERACTION_TYPE.INITIAL_PAGE_LOAD;else if (this.newURL !== this.oldURL) ixnType = _constants.INTERACTION_TYPE.ROUTE_CHANGE;else ixnType = _constants.INTERACTION_TYPE.UNSPECIFIED;
|
|
@@ -131,12 +134,16 @@ class Interaction extends _belNode.BelNode {
|
|
|
131
134
|
(0, _belSerializer.numeric)(this.callbackDuration),
|
|
132
135
|
// not relative
|
|
133
136
|
addString(this.trigger), addString((0, _cleanUrl.cleanURL)(this.initialPageURL, true)), addString((0, _cleanUrl.cleanURL)(this.oldURL, true)), addString((0, _cleanUrl.cleanURL)(this.newURL, true)), addString(this.customName), ixnType, (0, _belSerializer.nullable)(this.queueTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.appTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.oldRoute, addString, true) + (0, _belSerializer.nullable)(this.newRoute, addString, true) + addString(this.id), addString(this.nodeId), (0, _belSerializer.nullable)(this.firstPaint, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.firstContentfulPaint, _belSerializer.numeric)];
|
|
134
|
-
const
|
|
135
|
-
|
|
137
|
+
const customAttributes = {
|
|
138
|
+
...agentRef.info.jsAttributes,
|
|
139
|
+
...this.customAttributes
|
|
140
|
+
}; // attrs specific to this interaction should have precedence over the general custom attrs
|
|
141
|
+
const allAttachedNodes = (0, _belSerializer.addCustomAttributes)(customAttributes || {}, addString); // start with all custom attributes
|
|
142
|
+
if (agentRef.info.atts) allAttachedNodes.push('a,' + addString(agentRef.info.atts)); // add apm provided attributes
|
|
136
143
|
/* Querypack encoder+decoder quirkiness:
|
|
137
144
|
- If first ixn node of payload is being processed, its children's start time must be offset by this node's start. (firstStartTime should be undefined.)
|
|
138
145
|
- Else for subsequent ixns in the same payload, we go back to using that first ixn node's start to offset their children's start. */
|
|
139
|
-
this.children.forEach(node => allAttachedNodes.push(node.serialize(isFirstIxnOfPayload ? this.start : firstStartTimeOfPayload))); // recursively add the serialized string of every child of this (ixn) bel node
|
|
146
|
+
this.children.forEach(node => allAttachedNodes.push(node.serialize(isFirstIxnOfPayload ? this.start : firstStartTimeOfPayload, agentRef))); // recursively add the serialized string of every child of this (ixn) bel node
|
|
140
147
|
|
|
141
148
|
fields[1] = (0, _belSerializer.numeric)(allAttachedNodes.length);
|
|
142
149
|
nodeList.push(fields);
|
|
@@ -34,7 +34,7 @@ function Interaction(eventName, timestamp, url, routeName, onFinished, agentRef)
|
|
|
34
34
|
attrs.initialPageURL = _runtime.initialLocation;
|
|
35
35
|
attrs.oldRoute = routeName;
|
|
36
36
|
attrs.newURL = url;
|
|
37
|
-
attrs.oldURL = eventName === 'initialPageLoad' ? document.referrer : url;
|
|
37
|
+
attrs.oldURL = eventName === 'initialPageLoad' ? document.referrer || undefined : url; // document referrer can return '' and flipper url grouping gets weird with empty strings. Pass undefined to skip flipper.
|
|
38
38
|
attrs.custom = {};
|
|
39
39
|
attrs.store = {};
|
|
40
40
|
}
|
|
@@ -10,6 +10,8 @@ var _registerHandler = require("../../common/event-emitter/register-handler");
|
|
|
10
10
|
var _sessionEntity = require("../../common/session/session-entity");
|
|
11
11
|
var _localStorage = require("../../common/storage/local-storage.js");
|
|
12
12
|
var _constants = require("../../common/session/constants");
|
|
13
|
+
var _info = require("../../common/config/info");
|
|
14
|
+
var _attributeSize = require("../../common/util/attribute-size");
|
|
13
15
|
/**
|
|
14
16
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
15
17
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -29,13 +31,19 @@ function setupAgentSession(agentRef) {
|
|
|
29
31
|
|
|
30
32
|
// Retrieve & re-add all of the persisted setCustomAttribute|setUserId k-v from previous page load(s), if any was stored.
|
|
31
33
|
const customSessionData = agentRef.runtime.session.state.custom;
|
|
32
|
-
if (customSessionData) {
|
|
34
|
+
if (customSessionData && Object.keys(customSessionData).length) {
|
|
33
35
|
/** stored attributes from previous page should not take precedence over attributes stored on this page via API before the page load */
|
|
34
|
-
agentRef.info
|
|
35
|
-
...
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
agentRef.info = (0, _info.mergeInfo)({
|
|
37
|
+
...agentRef.info,
|
|
38
|
+
jsAttributes: {
|
|
39
|
+
...customSessionData,
|
|
40
|
+
...agentRef.info.jsAttributes
|
|
41
|
+
}
|
|
42
|
+
});
|
|
38
43
|
}
|
|
44
|
+
|
|
45
|
+
/** track changes to the jsAttributes field over time for aiding with harvest mechanics */
|
|
46
|
+
agentRef.runtime.jsAttributesMetadata = (0, _attributeSize.trackObjectAttributeSize)(agentRef.info, 'jsAttributes');
|
|
39
47
|
const sharedEE = _contextualEe.ee.get(agentRef.agentIdentifier);
|
|
40
48
|
|
|
41
49
|
// any calls to newrelic.setCustomAttribute(<persisted>) will need to be added to:
|
|
@@ -20,6 +20,7 @@ var _eventBuffer = require("./event-buffer");
|
|
|
20
20
|
var _handle = require("../../common/event-emitter/handle");
|
|
21
21
|
var _constants = require("../metrics/constants");
|
|
22
22
|
var _eventAggregator = require("../../common/aggregate/event-aggregator");
|
|
23
|
+
var _agentConstants = require("../../common/constants/agent-constants");
|
|
23
24
|
/**
|
|
24
25
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
25
26
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -36,6 +37,12 @@ class AggregateBase extends _featureBase.FeatureBase {
|
|
|
36
37
|
this.agentRef = agentRef;
|
|
37
38
|
this.checkConfiguration(agentRef);
|
|
38
39
|
this.doOnceForAllAggregate(agentRef);
|
|
40
|
+
|
|
41
|
+
/** @type {Boolean} indicates if custom attributes are combined in each event payload for size estimation purposes. this is set to true in derived classes that need to evaluate custom attributes separately from the event payload */
|
|
42
|
+
this.customAttributesAreSeparate = false;
|
|
43
|
+
/** @type {Boolean} indicates if the feature can harvest early. This is set to false in derived classes that need to block early harvests, like ajax under certain conditions */
|
|
44
|
+
this.canHarvestEarly = true; // this is set to false in derived classes that need to block early harvests, like ajax under certain conditions
|
|
45
|
+
|
|
39
46
|
this.harvestOpts = {}; // features aggregate classes can define custom opts for when their harvest is called
|
|
40
47
|
|
|
41
48
|
const agentEntityGuid = this.agentRef?.runtime?.appMetadata?.agents?.[0]?.entityGuid;
|
|
@@ -65,17 +72,33 @@ class AggregateBase extends _featureBase.FeatureBase {
|
|
|
65
72
|
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
66
73
|
case _features.FEATURE_NAMES.jserrors:
|
|
67
74
|
case _features.FEATURE_NAMES.metrics:
|
|
68
|
-
this.events = this.agentRef.sharedAggregator ??= new _eventStoreManager.EventStoreManager(this.agentRef, _eventAggregator.EventAggregator, entityGuid,
|
|
75
|
+
this.events = this.agentRef.sharedAggregator ??= new _eventStoreManager.EventStoreManager(this.agentRef, _eventAggregator.EventAggregator, entityGuid, {
|
|
76
|
+
featureName: 'shared_aggregator'
|
|
77
|
+
});
|
|
69
78
|
break;
|
|
70
79
|
/** All other features get EventBuffer in the ESM by default. Note: PVE is included here, but event buffer will always be empty so future harvests will still not happen by interval or EOL.
|
|
71
80
|
This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
|
|
72
81
|
Its easier to just keep an empty event buffer in place. */
|
|
73
82
|
default:
|
|
74
|
-
this.events = new _eventStoreManager.EventStoreManager(this.agentRef, _eventBuffer.EventBuffer, entityGuid, this
|
|
83
|
+
this.events = new _eventStoreManager.EventStoreManager(this.agentRef, _eventBuffer.EventBuffer, entityGuid, this);
|
|
75
84
|
break;
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Evaluates whether a harvest should be made early by estimating the size of the current payload. Currently, this only happens if the event storage is EventBuffer, since that triggers this method directly.
|
|
90
|
+
* If conditions are met, a new harvest will be triggered immediately.
|
|
91
|
+
* @returns void
|
|
92
|
+
*/
|
|
93
|
+
decideEarlyHarvest() {
|
|
94
|
+
if (!this.canHarvestEarly) return;
|
|
95
|
+
const estimatedSize = this.events.byteSize() + (this.customAttributesAreSeparate ? this.agentRef.runtime.jsAttributesMetadata.bytes : 0);
|
|
96
|
+
if (estimatedSize > _agentConstants.IDEAL_PAYLOAD_SIZE) {
|
|
97
|
+
this.agentRef.runtime.harvester.triggerHarvestFor(this);
|
|
98
|
+
this.reportSupportabilityMetric("".concat(this.featureName, "/Harvest/Early/Seen"), estimatedSize);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
/**
|
|
80
103
|
* New handler for waiting for multiple flags. Useful when expecting multiple flags simultaneously (ex. stn vs sr)
|
|
81
104
|
* @param {string[]} flagNames
|
|
@@ -18,10 +18,13 @@ class EventBuffer {
|
|
|
18
18
|
#rawBytesBackup;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Creates an event buffer that can hold feature-processed events.
|
|
22
|
+
* @param {Number} maxPayloadSize The maximum size of the payload that can be stored in this buffer.
|
|
23
|
+
* @param {Object} [featureAgg] - the feature aggregate instance
|
|
22
24
|
*/
|
|
23
|
-
constructor(maxPayloadSize = _agentConstants.MAX_PAYLOAD_SIZE) {
|
|
25
|
+
constructor(maxPayloadSize = _agentConstants.MAX_PAYLOAD_SIZE, featureAgg) {
|
|
24
26
|
this.maxPayloadSize = maxPayloadSize;
|
|
27
|
+
this.featureAgg = featureAgg;
|
|
25
28
|
}
|
|
26
29
|
isEmpty() {
|
|
27
30
|
return this.#buffer.length === 0;
|
|
@@ -43,9 +46,15 @@ class EventBuffer {
|
|
|
43
46
|
*/
|
|
44
47
|
add(event) {
|
|
45
48
|
const addSize = (0, _stringify.stringify)(event)?.length || 0; // (estimate) # of bytes a directly stringified event it would take to send
|
|
46
|
-
if (this.#rawBytes + addSize > this.maxPayloadSize)
|
|
49
|
+
if (this.#rawBytes + addSize > this.maxPayloadSize) {
|
|
50
|
+
const smTag = inject => "EventBuffer/".concat(inject, "/Dropped/Bytes");
|
|
51
|
+
this.featureAgg?.reportSupportabilityMetric(smTag(this.featureAgg.featureName), addSize); // bytes dropped for this feature will aggregate with this metric tag
|
|
52
|
+
this.featureAgg?.reportSupportabilityMetric(smTag('Combined'), addSize); // all bytes dropped across all features will aggregate with this metric tag
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
47
55
|
this.#buffer.push(event);
|
|
48
56
|
this.#rawBytes += addSize;
|
|
57
|
+
this.featureAgg?.decideEarlyHarvest(); // check if we should harvest early with new data
|
|
49
58
|
return true;
|
|
50
59
|
}
|
|
51
60
|
|
|
@@ -22,14 +22,14 @@ class EventStoreManager {
|
|
|
22
22
|
* @param {object} agentRef - reference to base agent class
|
|
23
23
|
* @param {EventBuffer|EventAggregator} storageClass - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
|
|
24
24
|
* @param {string} [defaultEntityGuid] - the entity guid to use as the default storage instance; if not provided, a new one is created
|
|
25
|
-
* @param {
|
|
25
|
+
* @param {Object} featureAgg - the feature aggregate instance that initialized this manager
|
|
26
26
|
*/
|
|
27
|
-
constructor(agentRef, storageClass, defaultEntityGuid,
|
|
27
|
+
constructor(agentRef, storageClass, defaultEntityGuid, featureAgg) {
|
|
28
28
|
this.agentRef = agentRef;
|
|
29
29
|
this.entityManager = agentRef.runtime.entityManager;
|
|
30
30
|
this.StorageClass = storageClass;
|
|
31
|
-
this.appStorageMap = new Map([[_agentConstants.DEFAULT_KEY, new this.StorageClass()]]);
|
|
32
|
-
this.
|
|
31
|
+
this.appStorageMap = new Map([[_agentConstants.DEFAULT_KEY, new this.StorageClass(_agentConstants.MAX_PAYLOAD_SIZE, featureAgg)]]);
|
|
32
|
+
this.featureAgg = featureAgg;
|
|
33
33
|
this.setEventStore(defaultEntityGuid);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -46,7 +46,7 @@ class EventStoreManager {
|
|
|
46
46
|
/** we should already have an event store for the default */
|
|
47
47
|
if (!targetEntityGuid) return;
|
|
48
48
|
/** if the target is the container agent, SHARE the default storage -- otherwise create a new event store */
|
|
49
|
-
const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(_agentConstants.DEFAULT_KEY) : new this.StorageClass();
|
|
49
|
+
const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(_agentConstants.DEFAULT_KEY) : new this.StorageClass(_agentConstants.MAX_PAYLOAD_SIZE, this.featureAgg);
|
|
50
50
|
this.appStorageMap.set(targetEntityGuid, eventStorage);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -78,7 +78,7 @@ class EventStoreManager {
|
|
|
78
78
|
drained: !!_featureFlags.activatedFeatures?.[this.agentRef.agentIdentifier],
|
|
79
79
|
type: 'data',
|
|
80
80
|
name: 'buffer',
|
|
81
|
-
feature: this.featureName,
|
|
81
|
+
feature: this.featureAgg.featureName,
|
|
82
82
|
data: event
|
|
83
83
|
});
|
|
84
84
|
return this.#getEventStore(targetEntityGuid).add(event);
|