@newrelic/browser-agent 1.261.1 → 1.262.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/drain/drain.js +21 -15
  5. package/dist/cjs/common/wrap/wrap-logger.js +3 -0
  6. package/dist/cjs/features/logging/aggregate/index.js +27 -2
  7. package/dist/cjs/features/logging/shared/utils.js +2 -18
  8. package/dist/cjs/features/session_replay/instrument/index.js +1 -1
  9. package/dist/cjs/features/session_replay/shared/utils.js +0 -5
  10. package/dist/cjs/features/session_trace/aggregate/index.js +1 -1
  11. package/dist/cjs/features/utils/instrument-base.js +9 -2
  12. package/dist/cjs/loaders/api/api.js +5 -10
  13. package/dist/esm/common/constants/env.cdn.js +1 -1
  14. package/dist/esm/common/constants/env.npm.js +1 -1
  15. package/dist/esm/common/drain/drain.js +21 -15
  16. package/dist/esm/common/wrap/wrap-logger.js +3 -0
  17. package/dist/esm/features/logging/aggregate/index.js +28 -3
  18. package/dist/esm/features/logging/shared/utils.js +2 -18
  19. package/dist/esm/features/session_replay/instrument/index.js +1 -1
  20. package/dist/esm/features/session_replay/shared/utils.js +0 -4
  21. package/dist/esm/features/session_trace/aggregate/index.js +1 -1
  22. package/dist/esm/features/utils/instrument-base.js +10 -3
  23. package/dist/esm/loaders/api/api.js +7 -12
  24. package/dist/types/common/drain/drain.d.ts.map +1 -1
  25. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  26. package/dist/types/features/logging/aggregate/index.d.ts +1 -1
  27. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  28. package/dist/types/features/logging/shared/utils.d.ts +1 -1
  29. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  30. package/dist/types/features/session_replay/shared/utils.d.ts +0 -1
  31. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  32. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  33. package/dist/types/loaders/api/api.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/common/drain/drain.js +20 -15
  36. package/src/common/wrap/wrap-logger.js +3 -0
  37. package/src/features/logging/aggregate/index.js +30 -3
  38. package/src/features/logging/shared/utils.js +2 -19
  39. package/src/features/session_replay/instrument/index.js +1 -1
  40. package/src/features/session_replay/shared/utils.js +0 -5
  41. package/src/features/session_trace/aggregate/index.js +1 -1
  42. package/src/features/utils/instrument-base.js +9 -3
  43. package/src/loaders/api/api.js +7 -12
  44. package/src/features/session_replay/shared/__mocks__/utils.js +0 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,27 @@
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.262.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.261.2...v1.262.0) (2024-07-09)
7
+
8
+
9
+ ### Features
10
+
11
+ * logging bundle optimization ([#1089](https://github.com/newrelic/newrelic-browser-agent/issues/1089)) ([83d7d1e](https://github.com/newrelic/newrelic-browser-agent/commit/83d7d1ed1863772e5515d128d0710cb456dafafd))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * address call to stopTimer on undefined scheduler ([#1104](https://github.com/newrelic/newrelic-browser-agent/issues/1104)) ([5022134](https://github.com/newrelic/newrelic-browser-agent/commit/5022134783152e7eebbd54608eda5a09e8fe0ebb))
17
+ * Release backlog memory when features are blocked by RUM ([#1102](https://github.com/newrelic/newrelic-browser-agent/issues/1102)) ([5eb9164](https://github.com/newrelic/newrelic-browser-agent/commit/5eb91646314b5a225a1a902ecb0b58b57b5ee74f))
18
+ * safeguard api calls ([#1103](https://github.com/newrelic/newrelic-browser-agent/issues/1103)) ([3d815a3](https://github.com/newrelic/newrelic-browser-agent/commit/3d815a3988583911322541007bd5e176a5bba4c1))
19
+
20
+ ## [1.261.2](https://github.com/newrelic/newrelic-browser-agent/compare/v1.261.1...v1.261.2) (2024-07-01)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * recordReplay will restart replays on same session page loads ([#1093](https://github.com/newrelic/newrelic-browser-agent/issues/1093)) ([cecddbe](https://github.com/newrelic/newrelic-browser-agent/commit/cecddbebc445dabcf3ce48c1fc88311198be6d0a))
26
+
6
27
  ## [1.261.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.261.0...v1.261.1) (2024-06-26)
7
28
 
8
29
 
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.261.1";
15
+ const VERSION = exports.VERSION = "1.262.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.261.1";
15
+ const VERSION = exports.VERSION = "1.262.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -42,8 +42,9 @@ function registerDrain(agentIdentifier, group) {
42
42
  * @param {*} group - The named "bucket" to be removed from the registry
43
43
  */
44
44
  function deregisterDrain(agentIdentifier, group) {
45
- curateRegistry(agentIdentifier);
45
+ if (!agentIdentifier || !registry[agentIdentifier]) return;
46
46
  if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group);
47
+ drainGroup(agentIdentifier, group, false);
47
48
  if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier);
48
49
  }
49
50
 
@@ -103,27 +104,32 @@ function checkCanDrainAll(agentIdentifier) {
103
104
  * @param {*} group - The name of a particular feature's event "bucket".
104
105
  */
105
106
  function drainGroup(agentIdentifier, group) {
107
+ let activateGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
106
108
  const baseEE = agentIdentifier ? _contextualEe.ee.get(agentIdentifier) : _contextualEe.ee;
107
109
  const handlers = _registerHandler.registerHandler.handlers; // other storage in registerHandler
108
110
  if (!baseEE.backlog || !handlers) return;
109
- var bufferedEventsInGroup = baseEE.backlog[group];
110
- var groupHandlers = handlers[group]; // each group in the registerHandler storage
111
- if (groupHandlers) {
112
- // We don't cache the length of the buffer while looping because events might still be added while processing.
113
- for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) {
114
- // eslint-disable-line no-unmodified-loop-condition
115
- emitEvent(bufferedEventsInGroup[i], groupHandlers);
116
- }
117
- (0, _mapOwn.mapOwn)(groupHandlers, function (eventType, handlerRegistrationList) {
118
- (0, _mapOwn.mapOwn)(handlerRegistrationList, function (i, registration) {
119
- // registration is an array of: [targetEE, eventHandler]
120
- registration[0].on(eventType, registration[1]);
111
+
112
+ // Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
113
+ if (activateGroup) {
114
+ const bufferedEventsInGroup = baseEE.backlog[group];
115
+ const groupHandlers = handlers[group]; // each group in the registerHandler storage
116
+ if (groupHandlers) {
117
+ // We don't cache the length of the buffer while looping because events might still be added while processing.
118
+ for (let i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) {
119
+ // eslint-disable-line no-unmodified-loop-condition
120
+ emitEvent(bufferedEventsInGroup[i], groupHandlers);
121
+ }
122
+ (0, _mapOwn.mapOwn)(groupHandlers, function (eventType, handlerRegistrationList) {
123
+ (0, _mapOwn.mapOwn)(handlerRegistrationList, function (i, registration) {
124
+ // registration is an array of: [targetEE, eventHandler]
125
+ registration[0].on(eventType, registration[1]);
126
+ });
121
127
  });
122
- });
128
+ }
123
129
  }
124
130
  if (!baseEE.isolatedBacklog) delete handlers[group];
125
131
  baseEE.backlog[group] = null;
126
- baseEE.emit('drain-' + group, []);
132
+ baseEE.emit('drain-' + group, []); // TODO: Code exists purely for a unit test and needs to be refined
127
133
  }
128
134
 
129
135
  /**
@@ -5,8 +5,10 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.scopedEE = scopedEE;
7
7
  exports.wrapLogger = wrapLogger;
8
+ var _constants = require("../../features/logging/constants");
8
9
  var _contextualEe = require("../event-emitter/contextual-ee");
9
10
  var _eventContext = require("../event-emitter/event-context");
11
+ var _console = require("../util/console");
10
12
  var _wrapFunction = require("./wrap-function");
11
13
  /*
12
14
  * Copyright 2020 New Relic Corporation. All rights reserved.
@@ -26,6 +28,7 @@ var _wrapFunction = require("./wrap-function");
26
28
  */
27
29
  // eslint-disable-next-line
28
30
  function wrapLogger(sharedEE, parent, loggerFn, context) {
31
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return (0, _console.warn)(_constants.LOGGING_FAILURE_MESSAGE + 'invalid argument(s)');
29
32
  const ee = scopedEE(sharedEE);
30
33
  const wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee);
31
34
 
@@ -14,6 +14,7 @@ var _constants = require("../../metrics/constants");
14
14
  var _aggregateBase = require("../../utils/aggregate-base");
15
15
  var _constants2 = require("../constants");
16
16
  var _log = require("../shared/log");
17
+ var _utils = require("../shared/utils");
17
18
  class Aggregate extends _aggregateBase.AggregateBase {
18
19
  static featureName = _constants2.FEATURE_NAME;
19
20
  #agentRuntime;
@@ -44,13 +45,37 @@ class Aggregate extends _aggregateBase.AggregateBase {
44
45
  this.drain();
45
46
  });
46
47
  }
47
- handleLog(timestamp, message, attributes, level) {
48
+ handleLog(timestamp, message) {
49
+ let attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
50
+ let level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _constants2.LOG_LEVELS.INFO;
48
51
  if (this.blocked) return;
52
+ if (!attributes || typeof attributes !== 'object') attributes = {};
53
+ if (typeof level === 'string') level = level.toUpperCase();
54
+ if (!(0, _utils.isValidLogLevel)(level)) return (0, _console.warn)(_constants2.LOGGING_LEVEL_FAILURE_MESSAGE + level, Object.values(_constants2.LOG_LEVELS));
55
+ try {
56
+ if (typeof message !== 'string') {
57
+ const stringified = (0, _stringify.stringify)(message);
58
+ /**
59
+ * Error instances convert to `{}` when stringified
60
+ * Symbol converts to '' when stringified
61
+ * other cases tbd
62
+ * */
63
+ if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
64
+ }
65
+ } catch (err) {
66
+ (0, _console.warn)('could not cast log message to string', message);
67
+ return;
68
+ }
69
+ if (typeof message !== 'string' || !message) return (0, _console.warn)(_constants2.LOGGING_IGNORED + 'invalid message');
70
+ if (message.length > _constants2.MAX_PAYLOAD_SIZE) {
71
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length]);
72
+ return (0, _console.warn)(_constants2.LOGGING_IGNORED + '> ' + _constants2.MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...');
73
+ }
49
74
  const log = new _log.Log(this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp), message, attributes, level);
50
75
  const logBytes = log.message.length + (0, _stringify.stringify)(log.attributes).length + log.level.length + 10; // timestamp == 10 chars
51
76
  if (logBytes > _constants2.MAX_PAYLOAD_SIZE) {
52
77
  (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes]);
53
- return (0, _console.warn)(_constants2.LOGGING_IGNORED + '> ' + _constants2.MAX_PAYLOAD_SIZE + ' bytes', log.message.slice(0, 25) + '...');
78
+ return (0, _console.warn)(_constants2.LOGGING_IGNORED + '> ' + _constants2.MAX_PAYLOAD_SIZE + ' bytes: ', log.message.slice(0, 25) + '...');
54
79
  }
55
80
  if (this.estimatedBytes + logBytes >= _constants2.MAX_PAYLOAD_SIZE) {
56
81
  (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.estimatedBytes + logBytes]);
@@ -7,8 +7,6 @@ exports.bufferLog = bufferLog;
7
7
  exports.isValidLogLevel = isValidLogLevel;
8
8
  var _handle = require("../../../common/event-emitter/handle");
9
9
  var _now = require("../../../common/timing/now");
10
- var _console = require("../../../common/util/console");
11
- var _stringify = require("../../../common/util/stringify");
12
10
  var _features = require("../../../loaders/features/features");
13
11
  var _constants = require("../../metrics/constants");
14
12
  var _constants2 = require("../constants");
@@ -21,30 +19,16 @@ var _constants2 = require("../constants");
21
19
  function bufferLog(ee, message) {
22
20
  let customAttributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
23
21
  let level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _constants2.LOG_LEVELS.INFO;
24
- try {
25
- if (typeof message !== 'string') {
26
- const stringified = (0, _stringify.stringify)(message);
27
- /**
28
- * Error instances convert to `{}` when stringified
29
- * Symbol converts to '' when stringified
30
- * other cases tbd
31
- * */
32
- if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
33
- }
34
- } catch (err) {
35
- (0, _console.warn)('could not cast log message to string', message);
36
- return;
37
- }
38
22
  (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/logging/".concat(level.toLowerCase(), "/called")], undefined, _features.FEATURE_NAMES.metrics, ee);
39
23
  (0, _handle.handle)(_constants2.LOGGING_EVENT_EMITTER_CHANNEL, [(0, _now.now)(), message, customAttributes, level], undefined, _features.FEATURE_NAMES.logging, ee);
40
24
  }
41
25
 
42
26
  /**
43
27
  * Checks if a supplied log level is acceptable for use in generating a log event
44
- * @param {string} level
28
+ * @param {string} level -- must be cast to uppercase before running this test
45
29
  * @returns {boolean}
46
30
  */
47
31
  function isValidLogLevel(level) {
48
32
  if (typeof level !== 'string') return false;
49
- return Object.values(_constants2.LOG_LEVELS).some(logLevel => logLevel.toUpperCase() === level.toUpperCase());
33
+ return Object.values(_constants2.LOG_LEVELS).some(logLevel => logLevel === level);
50
34
  }
@@ -32,7 +32,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
32
32
  session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
33
33
  } catch (err) {}
34
34
  if ((0, _utils.hasReplayPrerequisite)(agentIdentifier)) {
35
- this.ee.on('recordReplay', () => this.#apiStartOrRestartReplay());
35
+ this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.RECORD, () => this.#apiStartOrRestartReplay());
36
36
  }
37
37
  if (this.#canPreloadRecorder(session)) {
38
38
  this.#mode = session?.sessionReplayMode;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.buildNRMetaNode = buildNRMetaNode;
7
- exports.canImportReplayAgg = canImportReplayAgg;
8
7
  exports.hasReplayPrerequisite = hasReplayPrerequisite;
9
8
  exports.isPreloadAllowed = isPreloadAllowed;
10
9
  var _config = require("../../../common/config/config");
@@ -20,10 +19,6 @@ function hasReplayPrerequisite(agentId) {
20
19
  function isPreloadAllowed(agentId) {
21
20
  return (0, _config.getConfigurationValue)(agentId, 'session_replay.preload') === true && hasReplayPrerequisite(agentId);
22
21
  }
23
- function canImportReplayAgg(agentId, sessionMgr) {
24
- if (!hasReplayPrerequisite(agentId)) return false;
25
- return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
26
- }
27
22
  function buildNRMetaNode(timestamp, timeKeeper) {
28
23
  const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp);
29
24
  return {
@@ -242,7 +242,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
242
242
  this.agentRuntime.session.write({
243
243
  sessionTraceMode: this.mode
244
244
  });
245
- this.scheduler.stopTimer();
245
+ this.scheduler?.stopTimer();
246
246
  }
247
247
  }
248
248
  exports.Aggregate = Aggregate;
@@ -137,8 +137,15 @@ class InstrumentBase extends _featureBase.FeatureBase {
137
137
  * @returns
138
138
  */
139
139
  #shouldImportAgg(featureName, session) {
140
- if (featureName === _features.FEATURE_NAMES.sessionReplay) return (0, _utils.canImportReplayAgg)(this.agentIdentifier, session);
141
- return !(featureName === _features.FEATURE_NAMES.sessionTrace && !session);
140
+ switch (featureName) {
141
+ case _features.FEATURE_NAMES.sessionReplay:
142
+ // the session manager must be initialized successfully for Replay & Trace features
143
+ return (0, _utils.hasReplayPrerequisite)(this.agentIdentifier) && !!session;
144
+ case _features.FEATURE_NAMES.sessionTrace:
145
+ return !!session;
146
+ default:
147
+ return true;
148
+ }
142
149
  }
143
150
  }
144
151
  exports.InstrumentBase = InstrumentBase;
@@ -43,7 +43,9 @@ function setTopLevelCallers() {
43
43
  }
44
44
  let returnVals = [];
45
45
  Object.values(nr.initializedAgents).forEach(val => {
46
- if (val.exposed && val.api[fnName]) {
46
+ if (!val || !val.api) {
47
+ (0, _console.warn)("Call to api '".concat(fnName, "' made before agent fully initialized."));
48
+ } else if (val.exposed && val.api[fnName]) {
47
49
  returnVals.push(val.api[fnName](...args));
48
50
  }
49
51
  });
@@ -68,23 +70,16 @@ function setAPI(agentIdentifier, forceDrain) {
68
70
  customAttributes = {},
69
71
  level = _constants4.LOG_LEVELS.INFO
70
72
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
71
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {};
72
- if (typeof message !== 'string' || !message) return (0, _console.warn)(_constants4.LOGGING_IGNORED + 'invalid message');
73
- if (!(0, _utils.isValidLogLevel)(level)) return (0, _console.warn)(_constants4.LOGGING_LEVEL_FAILURE_MESSAGE + level, _constants4.LOG_LEVELS);
74
- if (message.length > _constants4.MAX_PAYLOAD_SIZE) return (0, _console.warn)(_constants4.LOGGING_IGNORED + '> ' + _constants4.MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...');
75
- (0, _utils.bufferLog)(instanceEE, message, customAttributes, level.toUpperCase());
73
+ (0, _utils.bufferLog)(instanceEE, message, customAttributes, level);
76
74
  };
77
75
  apiInterface.wrapLogger = function (parent, functionName) {
78
76
  let {
79
77
  customAttributes = {},
80
78
  level = _constants4.LOG_LEVELS.INFO
81
79
  } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
82
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {};
83
- if (!(typeof parent === 'object' && !!parent && typeof functionName === 'string' && !!functionName && typeof parent[functionName] === 'function' && typeof customAttributes === 'object')) return (0, _console.warn)(_constants4.LOGGING_FAILURE_MESSAGE + 'invalid argument(s)');
84
- if (!(0, _utils.isValidLogLevel)(level)) return (0, _console.warn)(_constants4.LOGGING_FAILURE_MESSAGE + _constants4.LOGGING_LEVEL_FAILURE_MESSAGE + level, _constants4.LOG_LEVELS);
85
80
  (0, _wrapLogger.wrapLogger)(instanceEE, parent, functionName, {
86
81
  customAttributes,
87
- level: level.toUpperCase()
82
+ level
88
83
  });
89
84
  };
90
85
 
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.261.1";
9
+ export const VERSION = "1.262.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.261.1";
9
+ export const VERSION = "1.262.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -34,8 +34,9 @@ export function registerDrain(agentIdentifier, group) {
34
34
  * @param {*} group - The named "bucket" to be removed from the registry
35
35
  */
36
36
  export function deregisterDrain(agentIdentifier, group) {
37
- curateRegistry(agentIdentifier);
37
+ if (!agentIdentifier || !registry[agentIdentifier]) return;
38
38
  if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group);
39
+ drainGroup(agentIdentifier, group, false);
39
40
  if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier);
40
41
  }
41
42
 
@@ -95,27 +96,32 @@ function checkCanDrainAll(agentIdentifier) {
95
96
  * @param {*} group - The name of a particular feature's event "bucket".
96
97
  */
97
98
  function drainGroup(agentIdentifier, group) {
99
+ let activateGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
98
100
  const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee;
99
101
  const handlers = defaultRegister.handlers; // other storage in registerHandler
100
102
  if (!baseEE.backlog || !handlers) return;
101
- var bufferedEventsInGroup = baseEE.backlog[group];
102
- var groupHandlers = handlers[group]; // each group in the registerHandler storage
103
- if (groupHandlers) {
104
- // We don't cache the length of the buffer while looping because events might still be added while processing.
105
- for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) {
106
- // eslint-disable-line no-unmodified-loop-condition
107
- emitEvent(bufferedEventsInGroup[i], groupHandlers);
108
- }
109
- mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
110
- mapOwn(handlerRegistrationList, function (i, registration) {
111
- // registration is an array of: [targetEE, eventHandler]
112
- registration[0].on(eventType, registration[1]);
103
+
104
+ // Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
105
+ if (activateGroup) {
106
+ const bufferedEventsInGroup = baseEE.backlog[group];
107
+ const groupHandlers = handlers[group]; // each group in the registerHandler storage
108
+ if (groupHandlers) {
109
+ // We don't cache the length of the buffer while looping because events might still be added while processing.
110
+ for (let i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) {
111
+ // eslint-disable-line no-unmodified-loop-condition
112
+ emitEvent(bufferedEventsInGroup[i], groupHandlers);
113
+ }
114
+ mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
115
+ mapOwn(handlerRegistrationList, function (i, registration) {
116
+ // registration is an array of: [targetEE, eventHandler]
117
+ registration[0].on(eventType, registration[1]);
118
+ });
113
119
  });
114
- });
120
+ }
115
121
  }
116
122
  if (!baseEE.isolatedBacklog) delete handlers[group];
117
123
  baseEE.backlog[group] = null;
118
- baseEE.emit('drain-' + group, []);
124
+ baseEE.emit('drain-' + group, []); // TODO: Code exists purely for a unit test and needs to be refined
119
125
  }
120
126
 
121
127
  /**
@@ -7,8 +7,10 @@
7
7
  * This module is used by: jserrors, spa.
8
8
  */
9
9
 
10
+ import { LOGGING_FAILURE_MESSAGE } from '../../features/logging/constants';
10
11
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
11
12
  import { EventContext } from '../event-emitter/event-context';
13
+ import { warn } from '../util/console';
12
14
  import { createWrapperWithEmitter as wfn } from './wrap-function';
13
15
 
14
16
  /**
@@ -20,6 +22,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function';
20
22
  */
21
23
  // eslint-disable-next-line
22
24
  export function wrapLogger(sharedEE, parent, loggerFn, context) {
25
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)');
23
26
  const ee = scopedEE(sharedEE);
24
27
  const wrapFn = wfn(ee);
25
28
 
@@ -6,8 +6,9 @@ import { warn } from '../../../common/util/console';
6
6
  import { stringify } from '../../../common/util/stringify';
7
7
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
8
8
  import { AggregateBase } from '../../utils/aggregate-base';
9
- import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, MAX_PAYLOAD_SIZE } from '../constants';
9
+ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants';
10
10
  import { Log } from '../shared/log';
11
+ import { isValidLogLevel } from '../shared/utils';
11
12
  export class Aggregate extends AggregateBase {
12
13
  static featureName = FEATURE_NAME;
13
14
  #agentRuntime;
@@ -38,13 +39,37 @@ export class Aggregate extends AggregateBase {
38
39
  this.drain();
39
40
  });
40
41
  }
41
- handleLog(timestamp, message, attributes, level) {
42
+ handleLog(timestamp, message) {
43
+ let attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
44
+ let level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : LOG_LEVELS.INFO;
42
45
  if (this.blocked) return;
46
+ if (!attributes || typeof attributes !== 'object') attributes = {};
47
+ if (typeof level === 'string') level = level.toUpperCase();
48
+ if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, Object.values(LOG_LEVELS));
49
+ try {
50
+ if (typeof message !== 'string') {
51
+ const stringified = stringify(message);
52
+ /**
53
+ * Error instances convert to `{}` when stringified
54
+ * Symbol converts to '' when stringified
55
+ * other cases tbd
56
+ * */
57
+ if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
58
+ }
59
+ } catch (err) {
60
+ warn('could not cast log message to string', message);
61
+ return;
62
+ }
63
+ if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message');
64
+ if (message.length > MAX_PAYLOAD_SIZE) {
65
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length]);
66
+ return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...');
67
+ }
43
68
  const log = new Log(this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp), message, attributes, level);
44
69
  const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10; // timestamp == 10 chars
45
70
  if (logBytes > MAX_PAYLOAD_SIZE) {
46
71
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes]);
47
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes', log.message.slice(0, 25) + '...');
72
+ return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', log.message.slice(0, 25) + '...');
48
73
  }
49
74
  if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
50
75
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.estimatedBytes + logBytes]);
@@ -1,7 +1,5 @@
1
1
  import { handle } from '../../../common/event-emitter/handle';
2
2
  import { now } from '../../../common/timing/now';
3
- import { warn } from '../../../common/util/console';
4
- import { stringify } from '../../../common/util/stringify';
5
3
  import { FEATURE_NAMES } from '../../../loaders/features/features';
6
4
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
7
5
  import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants';
@@ -15,30 +13,16 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants';
15
13
  export function bufferLog(ee, message) {
16
14
  let customAttributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
17
15
  let level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : LOG_LEVELS.INFO;
18
- try {
19
- if (typeof message !== 'string') {
20
- const stringified = stringify(message);
21
- /**
22
- * Error instances convert to `{}` when stringified
23
- * Symbol converts to '' when stringified
24
- * other cases tbd
25
- * */
26
- if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
27
- }
28
- } catch (err) {
29
- warn('could not cast log message to string', message);
30
- return;
31
- }
32
16
  handle(SUPPORTABILITY_METRIC_CHANNEL, ["API/logging/".concat(level.toLowerCase(), "/called")], undefined, FEATURE_NAMES.metrics, ee);
33
17
  handle(LOGGING_EVENT_EMITTER_CHANNEL, [now(), message, customAttributes, level], undefined, FEATURE_NAMES.logging, ee);
34
18
  }
35
19
 
36
20
  /**
37
21
  * Checks if a supplied log level is acceptable for use in generating a log event
38
- * @param {string} level
22
+ * @param {string} level -- must be cast to uppercase before running this test
39
23
  * @returns {boolean}
40
24
  */
41
25
  export function isValidLogLevel(level) {
42
26
  if (typeof level !== 'string') return false;
43
- return Object.values(LOG_LEVELS).some(logLevel => logLevel.toUpperCase() === level.toUpperCase());
27
+ return Object.values(LOG_LEVELS).some(logLevel => logLevel === level);
44
28
  }
@@ -26,7 +26,7 @@ export class Instrument extends InstrumentBase {
26
26
  session = JSON.parse(localStorage.getItem("".concat(PREFIX, "_").concat(DEFAULT_KEY)));
27
27
  } catch (err) {}
28
28
  if (hasReplayPrerequisite(agentIdentifier)) {
29
- this.ee.on('recordReplay', () => this.#apiStartOrRestartReplay());
29
+ this.ee.on(SR_EVENT_EMITTER_TYPES.RECORD, () => this.#apiStartOrRestartReplay());
30
30
  }
31
31
  if (this.#canPreloadRecorder(session)) {
32
32
  this.#mode = session?.sessionReplayMode;
@@ -11,10 +11,6 @@ export function hasReplayPrerequisite(agentId) {
11
11
  export function isPreloadAllowed(agentId) {
12
12
  return getConfigurationValue(agentId, 'session_replay.preload') === true && hasReplayPrerequisite(agentId);
13
13
  }
14
- export function canImportReplayAgg(agentId, sessionMgr) {
15
- if (!hasReplayPrerequisite(agentId)) return false;
16
- return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
17
- }
18
14
  export function buildNRMetaNode(timestamp, timeKeeper) {
19
15
  const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp);
20
16
  return {
@@ -236,6 +236,6 @@ export class Aggregate extends AggregateBase {
236
236
  this.agentRuntime.session.write({
237
237
  sessionTraceMode: this.mode
238
238
  });
239
- this.scheduler.stopTimer();
239
+ this.scheduler?.stopTimer();
240
240
  }
241
241
  }
@@ -11,7 +11,7 @@ import { isBrowserScope } from '../../common/constants/runtime';
11
11
  import { warn } from '../../common/util/console';
12
12
  import { FEATURE_NAMES } from '../../loaders/features/features';
13
13
  import { getConfigurationValue } from '../../common/config/config';
14
- import { canImportReplayAgg } from '../session_replay/shared/utils';
14
+ import { hasReplayPrerequisite } from '../session_replay/shared/utils';
15
15
  import { canEnableSessionTracking } from './feature-gates';
16
16
  import { single } from '../../common/util/invoke';
17
17
 
@@ -132,7 +132,14 @@ export class InstrumentBase extends FeatureBase {
132
132
  * @returns
133
133
  */
134
134
  #shouldImportAgg(featureName, session) {
135
- if (featureName === FEATURE_NAMES.sessionReplay) return canImportReplayAgg(this.agentIdentifier, session);
136
- return !(featureName === FEATURE_NAMES.sessionTrace && !session);
135
+ switch (featureName) {
136
+ case FEATURE_NAMES.sessionReplay:
137
+ // the session manager must be initialized successfully for Replay & Trace features
138
+ return hasReplayPrerequisite(this.agentIdentifier) && !!session;
139
+ case FEATURE_NAMES.sessionTrace:
140
+ return !!session;
141
+ default:
142
+ return true;
143
+ }
137
144
  }
138
145
  }
@@ -16,8 +16,8 @@ import { apiMethods, asyncApiMethods } from './api-methods';
16
16
  import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants';
17
17
  import { now } from '../../common/timing/now';
18
18
  import { MODE } from '../../common/session/constants';
19
- import { LOGGING_FAILURE_MESSAGE, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../../features/logging/constants';
20
- import { bufferLog, isValidLogLevel } from '../../features/logging/shared/utils';
19
+ import { LOG_LEVELS } from '../../features/logging/constants';
20
+ import { bufferLog } from '../../features/logging/shared/utils';
21
21
  import { wrapLogger } from '../../common/wrap/wrap-logger';
22
22
  export function setTopLevelCallers() {
23
23
  const nr = gosCDN();
@@ -35,7 +35,9 @@ export function setTopLevelCallers() {
35
35
  }
36
36
  let returnVals = [];
37
37
  Object.values(nr.initializedAgents).forEach(val => {
38
- if (val.exposed && val.api[fnName]) {
38
+ if (!val || !val.api) {
39
+ warn("Call to api '".concat(fnName, "' made before agent fully initialized."));
40
+ } else if (val.exposed && val.api[fnName]) {
39
41
  returnVals.push(val.api[fnName](...args));
40
42
  }
41
43
  });
@@ -60,23 +62,16 @@ export function setAPI(agentIdentifier, forceDrain) {
60
62
  customAttributes = {},
61
63
  level = LOG_LEVELS.INFO
62
64
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
63
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {};
64
- if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message');
65
- if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS);
66
- if (message.length > MAX_PAYLOAD_SIZE) return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...');
67
- bufferLog(instanceEE, message, customAttributes, level.toUpperCase());
65
+ bufferLog(instanceEE, message, customAttributes, level);
68
66
  };
69
67
  apiInterface.wrapLogger = function (parent, functionName) {
70
68
  let {
71
69
  customAttributes = {},
72
70
  level = LOG_LEVELS.INFO
73
71
  } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
74
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {};
75
- if (!(typeof parent === 'object' && !!parent && typeof functionName === 'string' && !!functionName && typeof parent[functionName] === 'function' && typeof customAttributes === 'object')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)');
76
- if (!isValidLogLevel(level)) return warn(LOGGING_FAILURE_MESSAGE + LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS);
77
72
  wrapLogger(instanceEE, parent, functionName, {
78
73
  customAttributes,
79
- level: level.toUpperCase()
74
+ level
80
75
  });
81
76
  };
82
77
 
@@ -1 +1 @@
1
- {"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../../../../src/common/drain/drain.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,+CAHW,MAAM,SACN,MAAM,QAQhB;AAED;;;;GAIG;AACH,iDAHW,GAAC,SACD,GAAC,QAMX;AAYD;;;;;;GAMG;AACH,wCAJW,MAAM,gBACN,MAAM,UACN,OAAO,QAajB"}
1
+ {"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../../../../src/common/drain/drain.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,+CAHW,MAAM,SACN,MAAM,QAQhB;AAED;;;;GAIG;AACH,iDAHW,GAAC,SACD,GAAC,QAOX;AAYD;;;;;;GAMG;AACH,wCAJW,MAAM,gBACN,MAAM,UACN,OAAO,QAajB"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAaA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAmBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
1
+ {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAeA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAoBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -9,7 +9,7 @@ export class Aggregate extends AggregateBase {
9
9
  estimatedBytes: number;
10
10
  harvestTimeSeconds: any;
11
11
  scheduler: HarvestScheduler;
12
- handleLog(timestamp: any, message: any, attributes: any, level: any): void;
12
+ handleLog(timestamp: any, message: any, attributes?: {}, level?: string): void;
13
13
  prepareHarvest(): {
14
14
  qs: {
15
15
  browser_monitoring_key: any;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/aggregate/index.js"],"names":[],"mappings":"AAWA;IACE,2BAAiC;IAGjC,mDA4BC;IAzBC,+BAA+B;IAC/B,oBAAsB;IACtB,4CAA4C;IAC5C,oBAAsB;IACtB,sHAAsH;IACtH,uBAAuB;IAKvB,wBAAmG;IAGjG,4BAKQ;IASZ,2EAoBC;IAED;;;;;;gBAYQ,0FAA0F;;;;;;;;;;;;YAY5F,0DAA0D;;;kBAI/D;IAED,qCAEC;;CACF;8BA7F6B,4BAA4B;iCAJzB,2CAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/aggregate/index.js"],"names":[],"mappings":"AAYA;IACE,2BAAiC;IAGjC,mDA4BC;IAzBC,+BAA+B;IAC/B,oBAAsB;IACtB,4CAA4C;IAC5C,oBAAsB;IACtB,sHAAsH;IACtH,uBAAuB;IAKvB,wBAAmG;IAGjG,4BAKQ;IASZ,+EA8CC;IAED;;;;;;gBAYQ,0FAA0F;;;;;;;;;;;;YAY5F,0DAA0D;;;kBAI/D;IAED,qCAEC;;CACF;8BAxH6B,4BAA4B;iCAJzB,2CAA2C"}
@@ -9,7 +9,7 @@ export function bufferLog(ee: ContextualEE, message: string, customAttributes?:
9
9
  }, level?: enum): void;
10
10
  /**
11
11
  * Checks if a supplied log level is acceptable for use in generating a log event
12
- * @param {string} level
12
+ * @param {string} level -- must be cast to uppercase before running this test
13
13
  * @returns {boolean}
14
14
  */
15
15
  export function isValidLogLevel(level: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/shared/utils.js"],"names":[],"mappings":"AAQA;;;;;KAKK;AACL,8BALa,YAAY,WACZ,MAAM,qBACN;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAC,CAAA;CAAC,UAClB,IAAI,QAoBhB;AAED;;;;GAIG;AACH,uCAHW,MAAM,GACJ,OAAO,CAKnB"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/shared/utils.js"],"names":[],"mappings":"AAMA;;;;;KAKK;AACL,8BALa,YAAY,WACZ,MAAM,qBACN;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAC,CAAA;CAAC,UAClB,IAAI,QAKhB;AAED;;;;GAIG;AACH,uCAHW,MAAM,GACJ,OAAO,CAKnB"}
@@ -1,6 +1,5 @@
1
1
  export function hasReplayPrerequisite(agentId: any): boolean;
2
2
  export function isPreloadAllowed(agentId: any): boolean;
3
- export function canImportReplayAgg(agentId: any, sessionMgr: any): boolean;
4
3
  export function buildNRMetaNode(timestamp: any, timeKeeper: any): {
5
4
  originalTimestamp: any;
6
5
  correctedTimestamp: any;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"AAIA,6DAIC;AAED,wDAEC;AAED,2EAGC;AAED;;;;;;;EAUC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"AAIA,6DAIC;AAED,wDAEC;AAED;;;;;;;EAUC"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAiBA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,cACN,OAAO,mCAAmC,EAAE,UAAU,eACtD,MAAM,8BAqChB;IA9BC,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,kCAAoC;IAiBtC;;;;;OAKG;IACH,mEAiDC;;CAYF;4BA7H2B,gBAAgB"}
1
+ {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAiBA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,cACN,OAAO,mCAAmC,EAAE,UAAU,eACtD,MAAM,8BAqChB;IA9BC,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,kCAAoC;IAiBtC;;;;;OAKG;IACH,mEAiDC;;CAkBF;4BAnI2B,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAsBA,2CAeC;AAID;;;;;;;;;;;;IAsEE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;EAiGvB"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAsBA,2CAiBC;AAID;;;;;;;;;;;;IA+DE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;EAiGvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.261.1",
3
+ "version": "1.262.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -32,8 +32,9 @@ export function registerDrain (agentIdentifier, group) {
32
32
  * @param {*} group - The named "bucket" to be removed from the registry
33
33
  */
34
34
  export function deregisterDrain (agentIdentifier, group) {
35
- curateRegistry(agentIdentifier)
35
+ if (!agentIdentifier || !registry[agentIdentifier]) return
36
36
  if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group)
37
+ drainGroup(agentIdentifier, group, false)
37
38
  if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier)
38
39
  }
39
40
 
@@ -86,29 +87,33 @@ function checkCanDrainAll (agentIdentifier) {
86
87
  * the subscribed handlers for the group.
87
88
  * @param {*} group - The name of a particular feature's event "bucket".
88
89
  */
89
- function drainGroup (agentIdentifier, group) {
90
+ function drainGroup (agentIdentifier, group, activateGroup = true) {
90
91
  const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee
91
92
  const handlers = defaultRegister.handlers // other storage in registerHandler
92
93
  if (!baseEE.backlog || !handlers) return
93
94
 
94
- var bufferedEventsInGroup = baseEE.backlog[group]
95
- var groupHandlers = handlers[group] // each group in the registerHandler storage
96
- if (groupHandlers) {
97
- // We don't cache the length of the buffer while looping because events might still be added while processing.
98
- for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) { // eslint-disable-line no-unmodified-loop-condition
99
- emitEvent(bufferedEventsInGroup[i], groupHandlers)
100
- }
95
+ // Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
96
+ if (activateGroup) {
97
+ const bufferedEventsInGroup = baseEE.backlog[group]
98
+ const groupHandlers = handlers[group] // each group in the registerHandler storage
99
+ if (groupHandlers) {
100
+ // We don't cache the length of the buffer while looping because events might still be added while processing.
101
+ for (let i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) { // eslint-disable-line no-unmodified-loop-condition
102
+ emitEvent(bufferedEventsInGroup[i], groupHandlers)
103
+ }
101
104
 
102
- mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
103
- mapOwn(handlerRegistrationList, function (i, registration) {
104
- // registration is an array of: [targetEE, eventHandler]
105
- registration[0].on(eventType, registration[1])
105
+ mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
106
+ mapOwn(handlerRegistrationList, function (i, registration) {
107
+ // registration is an array of: [targetEE, eventHandler]
108
+ registration[0].on(eventType, registration[1])
109
+ })
106
110
  })
107
- })
111
+ }
108
112
  }
113
+
109
114
  if (!baseEE.isolatedBacklog) delete handlers[group]
110
115
  baseEE.backlog[group] = null
111
- baseEE.emit('drain-' + group, [])
116
+ baseEE.emit('drain-' + group, []) // TODO: Code exists purely for a unit test and needs to be refined
112
117
  }
113
118
 
114
119
  /**
@@ -7,8 +7,10 @@
7
7
  * This module is used by: jserrors, spa.
8
8
  */
9
9
 
10
+ import { LOGGING_FAILURE_MESSAGE } from '../../features/logging/constants'
10
11
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
11
12
  import { EventContext } from '../event-emitter/event-context'
13
+ import { warn } from '../util/console'
12
14
  import { createWrapperWithEmitter as wfn } from './wrap-function'
13
15
 
14
16
  /**
@@ -20,6 +22,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function'
20
22
  */
21
23
  // eslint-disable-next-line
22
24
  export function wrapLogger(sharedEE, parent, loggerFn, context) {
25
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)')
23
26
  const ee = scopedEE(sharedEE)
24
27
  const wrapFn = wfn(ee)
25
28
 
@@ -6,8 +6,9 @@ import { warn } from '../../../common/util/console'
6
6
  import { stringify } from '../../../common/util/stringify'
7
7
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
8
8
  import { AggregateBase } from '../../utils/aggregate-base'
9
- import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, MAX_PAYLOAD_SIZE } from '../constants'
9
+ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants'
10
10
  import { Log } from '../shared/log'
11
+ import { isValidLogLevel } from '../shared/utils'
11
12
 
12
13
  export class Aggregate extends AggregateBase {
13
14
  static featureName = FEATURE_NAME
@@ -43,8 +44,34 @@ export class Aggregate extends AggregateBase {
43
44
  })
44
45
  }
45
46
 
46
- handleLog (timestamp, message, attributes, level) {
47
+ handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO) {
47
48
  if (this.blocked) return
49
+
50
+ if (!attributes || typeof attributes !== 'object') attributes = {}
51
+ if (typeof level === 'string') level = level.toUpperCase()
52
+ if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, Object.values(LOG_LEVELS))
53
+
54
+ try {
55
+ if (typeof message !== 'string') {
56
+ const stringified = stringify(message)
57
+ /**
58
+ * Error instances convert to `{}` when stringified
59
+ * Symbol converts to '' when stringified
60
+ * other cases tbd
61
+ * */
62
+ if (!!stringified && stringified !== '{}') message = stringified
63
+ else message = String(message)
64
+ }
65
+ } catch (err) {
66
+ warn('could not cast log message to string', message)
67
+ return
68
+ }
69
+ if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message')
70
+ if (message.length > MAX_PAYLOAD_SIZE) {
71
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length])
72
+ return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...')
73
+ }
74
+
48
75
  const log = new Log(
49
76
  this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp),
50
77
  message,
@@ -54,7 +81,7 @@ export class Aggregate extends AggregateBase {
54
81
  const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10 // timestamp == 10 chars
55
82
  if (logBytes > MAX_PAYLOAD_SIZE) {
56
83
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes])
57
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes', log.message.slice(0, 25) + '...')
84
+ return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', log.message.slice(0, 25) + '...')
58
85
  }
59
86
 
60
87
  if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
@@ -1,7 +1,5 @@
1
1
  import { handle } from '../../../common/event-emitter/handle'
2
2
  import { now } from '../../../common/timing/now'
3
- import { warn } from '../../../common/util/console'
4
- import { stringify } from '../../../common/util/stringify'
5
3
  import { FEATURE_NAMES } from '../../../loaders/features/features'
6
4
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
7
5
  import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
@@ -13,31 +11,16 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
13
11
  * @param {enum} level - the log level enum
14
12
  */
15
13
  export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO) {
16
- try {
17
- if (typeof message !== 'string') {
18
- const stringified = stringify(message)
19
- /**
20
- * Error instances convert to `{}` when stringified
21
- * Symbol converts to '' when stringified
22
- * other cases tbd
23
- * */
24
- if (!!stringified && stringified !== '{}') message = stringified
25
- else message = String(message)
26
- }
27
- } catch (err) {
28
- warn('could not cast log message to string', message)
29
- return
30
- }
31
14
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
32
15
  handle(LOGGING_EVENT_EMITTER_CHANNEL, [now(), message, customAttributes, level], undefined, FEATURE_NAMES.logging, ee)
33
16
  }
34
17
 
35
18
  /**
36
19
  * Checks if a supplied log level is acceptable for use in generating a log event
37
- * @param {string} level
20
+ * @param {string} level -- must be cast to uppercase before running this test
38
21
  * @returns {boolean}
39
22
  */
40
23
  export function isValidLogLevel (level) {
41
24
  if (typeof level !== 'string') return false
42
- return Object.values(LOG_LEVELS).some(logLevel => logLevel.toUpperCase() === level.toUpperCase())
25
+ return Object.values(LOG_LEVELS).some(logLevel => logLevel === level)
43
26
  }
@@ -28,7 +28,7 @@ export class Instrument extends InstrumentBase {
28
28
  } catch (err) { }
29
29
 
30
30
  if (hasReplayPrerequisite(agentIdentifier)) {
31
- this.ee.on('recordReplay', () => this.#apiStartOrRestartReplay())
31
+ this.ee.on(SR_EVENT_EMITTER_TYPES.RECORD, () => this.#apiStartOrRestartReplay())
32
32
  }
33
33
 
34
34
  if (this.#canPreloadRecorder(session)) {
@@ -12,11 +12,6 @@ export function isPreloadAllowed (agentId) {
12
12
  return getConfigurationValue(agentId, 'session_replay.preload') === true && hasReplayPrerequisite(agentId)
13
13
  }
14
14
 
15
- export function canImportReplayAgg (agentId, sessionMgr) {
16
- if (!hasReplayPrerequisite(agentId)) return false
17
- return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
18
- }
19
-
20
15
  export function buildNRMetaNode (timestamp, timeKeeper) {
21
16
  const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp)
22
17
  return {
@@ -205,6 +205,6 @@ export class Aggregate extends AggregateBase {
205
205
  this.blocked = true
206
206
  this.mode = MODE.OFF
207
207
  this.agentRuntime.session.write({ sessionTraceMode: this.mode })
208
- this.scheduler.stopTimer()
208
+ this.scheduler?.stopTimer()
209
209
  }
210
210
  }
@@ -11,7 +11,7 @@ import { isBrowserScope } from '../../common/constants/runtime'
11
11
  import { warn } from '../../common/util/console'
12
12
  import { FEATURE_NAMES } from '../../loaders/features/features'
13
13
  import { getConfigurationValue } from '../../common/config/config'
14
- import { canImportReplayAgg } from '../session_replay/shared/utils'
14
+ import { hasReplayPrerequisite } from '../session_replay/shared/utils'
15
15
  import { canEnableSessionTracking } from './feature-gates'
16
16
  import { single } from '../../common/util/invoke'
17
17
 
@@ -127,7 +127,13 @@ export class InstrumentBase extends FeatureBase {
127
127
  * @returns
128
128
  */
129
129
  #shouldImportAgg (featureName, session) {
130
- if (featureName === FEATURE_NAMES.sessionReplay) return canImportReplayAgg(this.agentIdentifier, session)
131
- return !(featureName === FEATURE_NAMES.sessionTrace && !session)
130
+ switch (featureName) {
131
+ case FEATURE_NAMES.sessionReplay: // the session manager must be initialized successfully for Replay & Trace features
132
+ return hasReplayPrerequisite(this.agentIdentifier) && !!session
133
+ case FEATURE_NAMES.sessionTrace:
134
+ return !!session
135
+ default:
136
+ return true
137
+ }
132
138
  }
133
139
  }
@@ -16,8 +16,8 @@ import { apiMethods, asyncApiMethods } from './api-methods'
16
16
  import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants'
17
17
  import { now } from '../../common/timing/now'
18
18
  import { MODE } from '../../common/session/constants'
19
- import { LOGGING_FAILURE_MESSAGE, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../../features/logging/constants'
20
- import { bufferLog, isValidLogLevel } from '../../features/logging/shared/utils'
19
+ import { LOG_LEVELS } from '../../features/logging/constants'
20
+ import { bufferLog } from '../../features/logging/shared/utils'
21
21
  import { wrapLogger } from '../../common/wrap/wrap-logger'
22
22
 
23
23
  export function setTopLevelCallers () {
@@ -29,7 +29,9 @@ export function setTopLevelCallers () {
29
29
  function caller (fnName, ...args) {
30
30
  let returnVals = []
31
31
  Object.values(nr.initializedAgents).forEach(val => {
32
- if (val.exposed && val.api[fnName]) {
32
+ if (!val || !val.api) {
33
+ warn(`Call to api '${fnName}' made before agent fully initialized.`)
34
+ } else if (val.exposed && val.api[fnName]) {
33
35
  returnVals.push(val.api[fnName](...args))
34
36
  }
35
37
  })
@@ -55,18 +57,11 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
55
57
  var spaPrefix = prefix + 'ixn-'
56
58
 
57
59
  apiInterface.log = function (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}) {
58
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {}
59
- if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message')
60
- if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS)
61
- if (message.length > MAX_PAYLOAD_SIZE) return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...')
62
- bufferLog(instanceEE, message, customAttributes, level.toUpperCase())
60
+ bufferLog(instanceEE, message, customAttributes, level)
63
61
  }
64
62
 
65
63
  apiInterface.wrapLogger = (parent, functionName, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}) => {
66
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {}
67
- if (!(typeof parent === 'object' && !!parent && typeof functionName === 'string' && !!functionName && typeof parent[functionName] === 'function' && typeof customAttributes === 'object')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)')
68
- if (!isValidLogLevel(level)) return warn(LOGGING_FAILURE_MESSAGE + LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS)
69
- wrapLogger(instanceEE, parent, functionName, { customAttributes, level: level.toUpperCase() })
64
+ wrapLogger(instanceEE, parent, functionName, { customAttributes, level })
70
65
  }
71
66
 
72
67
  // Setup stub functions that queue calls for later processing.
@@ -1 +0,0 @@
1
- export const canImportReplayAgg = jest.fn()