@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.
- package/CHANGELOG.md +21 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/drain/drain.js +21 -15
- package/dist/cjs/common/wrap/wrap-logger.js +3 -0
- package/dist/cjs/features/logging/aggregate/index.js +27 -2
- package/dist/cjs/features/logging/shared/utils.js +2 -18
- package/dist/cjs/features/session_replay/instrument/index.js +1 -1
- package/dist/cjs/features/session_replay/shared/utils.js +0 -5
- package/dist/cjs/features/session_trace/aggregate/index.js +1 -1
- package/dist/cjs/features/utils/instrument-base.js +9 -2
- package/dist/cjs/loaders/api/api.js +5 -10
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/drain/drain.js +21 -15
- package/dist/esm/common/wrap/wrap-logger.js +3 -0
- package/dist/esm/features/logging/aggregate/index.js +28 -3
- package/dist/esm/features/logging/shared/utils.js +2 -18
- package/dist/esm/features/session_replay/instrument/index.js +1 -1
- package/dist/esm/features/session_replay/shared/utils.js +0 -4
- package/dist/esm/features/session_trace/aggregate/index.js +1 -1
- package/dist/esm/features/utils/instrument-base.js +10 -3
- package/dist/esm/loaders/api/api.js +7 -12
- package/dist/types/common/drain/drain.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/shared/utils.d.ts +1 -1
- package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +0 -1
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/drain/drain.js +20 -15
- package/src/common/wrap/wrap-logger.js +3 -0
- package/src/features/logging/aggregate/index.js +30 -3
- package/src/features/logging/shared/utils.js +2 -19
- package/src/features/session_replay/instrument/index.js +1 -1
- package/src/features/session_replay/shared/utils.js +0 -5
- package/src/features/session_trace/aggregate/index.js +1 -1
- package/src/features/utils/instrument-base.js +9 -3
- package/src/loaders/api/api.js +7 -12
- 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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
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
|
|
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(
|
|
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 {
|
|
@@ -137,8 +137,15 @@ class InstrumentBase extends _featureBase.FeatureBase {
|
|
|
137
137
|
* @returns
|
|
138
138
|
*/
|
|
139
139
|
#shouldImportAgg(featureName, session) {
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
82
|
+
level
|
|
88
83
|
});
|
|
89
84
|
};
|
|
90
85
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
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
|
|
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(
|
|
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 {
|
|
@@ -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 {
|
|
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
|
-
|
|
136
|
-
|
|
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 {
|
|
20
|
-
import { bufferLog
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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":"
|
|
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
|
|
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":"
|
|
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":"
|
|
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
|
|
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;;
|
|
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,
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
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(
|
|
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 {
|
|
@@ -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 {
|
|
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
|
-
|
|
131
|
-
|
|
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
|
}
|
package/src/loaders/api/api.js
CHANGED
|
@@ -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 {
|
|
20
|
-
import { bufferLog
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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()
|