@newrelic/browser-agent 1.261.2 → 1.263.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 +27 -0
- package/dist/cjs/common/config/state/configurable.js +4 -4
- package/dist/cjs/common/config/state/init.js +5 -3
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/context/shared-context.js +2 -2
- package/dist/cjs/common/drain/drain.js +22 -16
- package/dist/cjs/common/harvest/harvest.js +1 -1
- package/dist/cjs/common/session/session-entity.js +2 -2
- package/dist/cjs/common/timing/time-keeper.js +13 -2
- package/dist/cjs/common/util/console.js +3 -4
- package/dist/cjs/common/util/obfuscate.js +3 -3
- package/dist/cjs/common/wrap/wrap-logger.js +2 -0
- package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
- package/dist/cjs/features/logging/aggregate/index.js +27 -2
- package/dist/cjs/features/logging/constants.js +2 -5
- package/dist/cjs/features/logging/shared/utils.js +2 -18
- package/dist/cjs/features/metrics/aggregate/index.js +16 -0
- package/dist/cjs/features/page_view_event/aggregate/index.js +20 -4
- package/dist/cjs/features/session_replay/aggregate/index.js +1 -1
- package/dist/cjs/features/session_replay/shared/recorder.js +1 -1
- package/dist/cjs/features/session_trace/aggregate/index.js +1 -1
- package/dist/cjs/features/spa/aggregate/index.js +1 -1
- package/dist/cjs/features/utils/aggregate-base.js +4 -3
- package/dist/cjs/features/utils/instrument-base.js +2 -2
- package/dist/cjs/loaders/agent-base.js +1 -1
- package/dist/cjs/loaders/agent.js +3 -4
- package/dist/cjs/loaders/api/api.js +13 -16
- package/dist/cjs/loaders/micro-agent.js +5 -5
- package/dist/esm/common/config/state/configurable.js +4 -4
- package/dist/esm/common/config/state/init.js +5 -3
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/context/shared-context.js +2 -2
- package/dist/esm/common/drain/drain.js +22 -16
- package/dist/esm/common/harvest/harvest.js +1 -1
- package/dist/esm/common/session/session-entity.js +2 -2
- package/dist/esm/common/timing/time-keeper.js +12 -2
- package/dist/esm/common/util/console.js +3 -4
- package/dist/esm/common/util/obfuscate.js +3 -3
- package/dist/esm/common/wrap/wrap-logger.js +2 -0
- package/dist/esm/common/wrap/wrap-xhr.js +1 -1
- package/dist/esm/features/logging/aggregate/index.js +28 -3
- package/dist/esm/features/logging/constants.js +1 -4
- package/dist/esm/features/logging/shared/utils.js +2 -18
- package/dist/esm/features/metrics/aggregate/index.js +16 -0
- package/dist/esm/features/page_view_event/aggregate/index.js +21 -5
- package/dist/esm/features/session_replay/aggregate/index.js +1 -1
- package/dist/esm/features/session_replay/shared/recorder.js +1 -1
- package/dist/esm/features/session_trace/aggregate/index.js +1 -1
- package/dist/esm/features/spa/aggregate/index.js +1 -1
- package/dist/esm/features/utils/aggregate-base.js +4 -3
- package/dist/esm/features/utils/instrument-base.js +2 -2
- package/dist/esm/loaders/agent-base.js +1 -1
- package/dist/esm/loaders/agent.js +3 -4
- package/dist/esm/loaders/api/api.js +15 -18
- package/dist/esm/loaders/micro-agent.js +5 -5
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/drain/drain.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts +2 -1
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
- package/dist/types/common/util/console.d.ts +1 -1
- package/dist/types/common/util/console.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/constants.d.ts +0 -3
- package/dist/types/features/logging/constants.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/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/state/configurable.js +4 -4
- package/src/common/config/state/init.js +4 -3
- package/src/common/context/shared-context.js +2 -2
- package/src/common/drain/drain.js +21 -16
- package/src/common/harvest/harvest.js +1 -1
- package/src/common/session/session-entity.js +2 -2
- package/src/common/timing/time-keeper.js +14 -2
- package/src/common/util/console.js +3 -4
- package/src/common/util/obfuscate.js +3 -3
- package/src/common/wrap/wrap-logger.js +2 -0
- package/src/common/wrap/wrap-xhr.js +1 -1
- package/src/features/logging/aggregate/index.js +30 -3
- package/src/features/logging/constants.js +0 -4
- package/src/features/logging/shared/utils.js +2 -19
- package/src/features/metrics/aggregate/index.js +12 -0
- package/src/features/page_view_event/aggregate/index.js +22 -5
- package/src/features/session_replay/aggregate/index.js +1 -1
- package/src/features/session_replay/shared/recorder.js +1 -1
- package/src/features/session_trace/aggregate/index.js +1 -1
- package/src/features/spa/aggregate/index.js +1 -1
- package/src/features/utils/aggregate-base.js +4 -3
- package/src/features/utils/instrument-base.js +2 -2
- package/src/loaders/agent-base.js +1 -1
- package/src/loaders/agent.js +3 -4
- package/src/loaders/api/api.js +15 -18
- package/src/loaders/micro-agent.js +5 -5
|
@@ -15,7 +15,6 @@ var _instrument = require("../features/page_view_event/instrument");
|
|
|
15
15
|
var _aggregator = require("../common/aggregate/aggregator");
|
|
16
16
|
var _nreum = require("../common/window/nreum");
|
|
17
17
|
var _console = require("../common/util/console");
|
|
18
|
-
var _stringify = require("../common/util/stringify");
|
|
19
18
|
var _runtime = require("../common/constants/runtime");
|
|
20
19
|
// important side effects
|
|
21
20
|
|
|
@@ -39,7 +38,7 @@ class Agent extends _agentBase.AgentBase {
|
|
|
39
38
|
if (!_runtime.globalScope) {
|
|
40
39
|
// We could not determine the runtime environment. Short-circuite the agent here
|
|
41
40
|
// to avoid possible exceptions later that may cause issues with customer's application.
|
|
42
|
-
(0, _console.warn)(
|
|
41
|
+
(0, _console.warn)(21);
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
44
|
this.sharedAggregator = new _aggregator.Aggregator({
|
|
@@ -78,11 +77,11 @@ class Agent extends _agentBase.AgentBase {
|
|
|
78
77
|
if (!this.runSoftNavOverSpa && InstrumentCtor.featureName === _features.FEATURE_NAMES.softNav) return;
|
|
79
78
|
const dependencies = (0, _featureDependencies.getFeatureDependencyNames)(InstrumentCtor.featureName);
|
|
80
79
|
const hasAllDeps = dependencies.every(featName => featName in this.features); // any other feature(s) this depends on should've been initialized on prior iterations by priority order
|
|
81
|
-
if (!hasAllDeps) (0, _console.warn)(
|
|
80
|
+
if (!hasAllDeps) (0, _console.warn)(36, InstrumentCtor.featureName);
|
|
82
81
|
this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator);
|
|
83
82
|
});
|
|
84
83
|
} catch (err) {
|
|
85
|
-
(0, _console.warn)(
|
|
84
|
+
(0, _console.warn)(22, err);
|
|
86
85
|
for (const featName in this.features) {
|
|
87
86
|
// this.features hold only features that have been instantiated
|
|
88
87
|
this.features[featName].abortHandler?.();
|
|
@@ -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)(38, fnName);
|
|
48
|
+
} else if (val.exposed && val.api[fnName]) {
|
|
47
49
|
returnVals.push(val.api[fnName](...args));
|
|
48
50
|
}
|
|
49
51
|
});
|
|
@@ -68,23 +70,18 @@ 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
|
-
|
|
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, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/log/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
74
|
+
(0, _utils.bufferLog)(instanceEE, message, customAttributes, level);
|
|
76
75
|
};
|
|
77
76
|
apiInterface.wrapLogger = function (parent, functionName) {
|
|
78
77
|
let {
|
|
79
78
|
customAttributes = {},
|
|
80
79
|
level = _constants4.LOG_LEVELS.INFO
|
|
81
80
|
} = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
82
|
-
|
|
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);
|
|
81
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/wrapLogger/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
85
82
|
(0, _wrapLogger.wrapLogger)(instanceEE, parent, functionName, {
|
|
86
83
|
customAttributes,
|
|
87
|
-
level
|
|
84
|
+
level
|
|
88
85
|
});
|
|
89
86
|
};
|
|
90
87
|
|
|
@@ -126,11 +123,11 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
126
123
|
apiInterface.setCustomAttribute = function (name, value) {
|
|
127
124
|
let persistAttribute = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
128
125
|
if (typeof name !== 'string') {
|
|
129
|
-
(0, _console.warn)(
|
|
126
|
+
(0, _console.warn)(39, typeof name);
|
|
130
127
|
return;
|
|
131
128
|
}
|
|
132
129
|
if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
|
|
133
|
-
(0, _console.warn)(
|
|
130
|
+
(0, _console.warn)(40, typeof value);
|
|
134
131
|
return;
|
|
135
132
|
}
|
|
136
133
|
return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute);
|
|
@@ -142,7 +139,7 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
142
139
|
*/
|
|
143
140
|
apiInterface.setUserId = function (value) {
|
|
144
141
|
if (!(typeof value === 'string' || value === null)) {
|
|
145
|
-
(0, _console.warn)(
|
|
142
|
+
(0, _console.warn)(41, typeof value);
|
|
146
143
|
return;
|
|
147
144
|
}
|
|
148
145
|
return appendJsAttribute('enduser.id', value, 'setUserId', true);
|
|
@@ -155,7 +152,7 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
155
152
|
*/
|
|
156
153
|
apiInterface.setApplicationVersion = function (value) {
|
|
157
154
|
if (!(typeof value === 'string' || value === null)) {
|
|
158
|
-
(0, _console.warn)(
|
|
155
|
+
(0, _console.warn)(42, typeof value);
|
|
159
156
|
return;
|
|
160
157
|
}
|
|
161
158
|
return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
|
|
@@ -165,7 +162,7 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
165
162
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
166
163
|
instanceEE.emit('manual-start-all');
|
|
167
164
|
} catch (err) {
|
|
168
|
-
(0, _console.warn)(
|
|
165
|
+
(0, _console.warn)(23, err);
|
|
169
166
|
}
|
|
170
167
|
};
|
|
171
168
|
apiInterface[_constants2.SR_EVENT_EMITTER_TYPES.RECORD] = function () {
|
|
@@ -234,7 +231,7 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
234
231
|
setAPI(agentIdentifier);
|
|
235
232
|
(0, _drain.drain)(agentIdentifier, 'api');
|
|
236
233
|
}).catch(err => {
|
|
237
|
-
(0, _console.warn)(
|
|
234
|
+
(0, _console.warn)(27, err);
|
|
238
235
|
instanceEE.abort();
|
|
239
236
|
});
|
|
240
237
|
}
|
|
@@ -65,11 +65,11 @@ class MicroAgent extends _agentBase.AgentBase {
|
|
|
65
65
|
const featNames = nonAutoFeatures;
|
|
66
66
|
if (features === undefined) features = featNames;else {
|
|
67
67
|
features = Array.isArray(features) && features.length ? features : [features];
|
|
68
|
-
if (features.some(f => !featNames.includes(f))) return (0, _console.warn)(
|
|
68
|
+
if (features.some(f => !featNames.includes(f))) return (0, _console.warn)(37, featNames);
|
|
69
69
|
if (!features.includes(_features.FEATURE_NAMES.pageViewEvent)) features.push(_features.FEATURE_NAMES.pageViewEvent);
|
|
70
70
|
}
|
|
71
71
|
} catch (err) {
|
|
72
|
-
(0, _console.warn)(
|
|
72
|
+
(0, _console.warn)(23, err);
|
|
73
73
|
}
|
|
74
74
|
try {
|
|
75
75
|
const enabledFeatures = (0, _enabledFeatures.getEnabledFeatures)(this.agentIdentifier);
|
|
@@ -77,7 +77,7 @@ class MicroAgent extends _agentBase.AgentBase {
|
|
|
77
77
|
// a biproduct of doing this is that the "session manager" is automatically handled through importing this feature
|
|
78
78
|
this.features.page_view_event = new _instrument.Instrument(this.agentIdentifier, this.sharedAggregator);
|
|
79
79
|
} catch (err) {
|
|
80
|
-
(0, _console.warn)(
|
|
80
|
+
(0, _console.warn)(24, err);
|
|
81
81
|
}
|
|
82
82
|
(0, _load.onWindowLoad)(() => {
|
|
83
83
|
// these features do not import an "instrument" file, meaning they are only hooked up to the API.
|
|
@@ -93,13 +93,13 @@ class MicroAgent extends _agentBase.AgentBase {
|
|
|
93
93
|
Aggregate
|
|
94
94
|
} = _ref2;
|
|
95
95
|
this.features[f] = new Aggregate(this.agentIdentifier, this.sharedAggregator);
|
|
96
|
-
}).catch(err => (0, _console.warn)(
|
|
96
|
+
}).catch(err => (0, _console.warn)(25, err));
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
return true;
|
|
101
101
|
} catch (err) {
|
|
102
|
-
(0, _console.warn)(
|
|
102
|
+
(0, _console.warn)(26, err);
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { warn } from '../../util/console';
|
|
2
2
|
export function getModeledObject(obj, model) {
|
|
3
3
|
try {
|
|
4
|
-
if (!obj || typeof obj !== 'object') return warn(
|
|
5
|
-
if (!model || typeof model !== 'object') return warn(
|
|
4
|
+
if (!obj || typeof obj !== 'object') return warn(3);
|
|
5
|
+
if (!model || typeof model !== 'object') return warn(4);
|
|
6
6
|
// allow getters and setters to pass from model to target
|
|
7
7
|
const output = Object.create(Object.getPrototypeOf(model), Object.getOwnPropertyDescriptors(model));
|
|
8
8
|
const target = Object.keys(output).length === 0 ? obj : output;
|
|
@@ -15,11 +15,11 @@ export function getModeledObject(obj, model) {
|
|
|
15
15
|
}
|
|
16
16
|
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
17
17
|
} catch (e) {
|
|
18
|
-
warn(
|
|
18
|
+
warn(1, e);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
return output;
|
|
22
22
|
} catch (err) {
|
|
23
|
-
warn(
|
|
23
|
+
warn(2, err);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -109,6 +109,8 @@ const model = () => {
|
|
|
109
109
|
// serialize images for collection without public asset url -- right now this is only useful for testing as it easily generates payloads too large to be harvested
|
|
110
110
|
inline_stylesheet: true,
|
|
111
111
|
// serialize css for collection without public asset url
|
|
112
|
+
fix_stylesheets: true,
|
|
113
|
+
// fetch missing stylesheet resources for inlining, only works if 'inline_stylesheet' is also true
|
|
112
114
|
// recording config settings
|
|
113
115
|
mask_all_inputs: true,
|
|
114
116
|
// this has a getter/setter to facilitate validation of the selectors
|
|
@@ -116,7 +118,7 @@ const model = () => {
|
|
|
116
118
|
return hiddenState.mask_selector;
|
|
117
119
|
},
|
|
118
120
|
set mask_text_selector(val) {
|
|
119
|
-
if (isValidSelector(val)) hiddenState.mask_selector = "".concat(val, ",").concat(nrMask);else if (val === '' || val === null) hiddenState.mask_selector = nrMask;else warn(
|
|
121
|
+
if (isValidSelector(val)) hiddenState.mask_selector = "".concat(val, ",").concat(nrMask);else if (val === '' || val === null) hiddenState.mask_selector = nrMask;else warn(5, val);
|
|
120
122
|
},
|
|
121
123
|
// these properties only have getters because they are enforcable constants and should error if someone tries to override them
|
|
122
124
|
get block_class() {
|
|
@@ -134,7 +136,7 @@ const model = () => {
|
|
|
134
136
|
return hiddenState.block_selector;
|
|
135
137
|
},
|
|
136
138
|
set block_selector(val) {
|
|
137
|
-
if (isValidSelector(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') warn(
|
|
139
|
+
if (isValidSelector(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') warn(6, val);
|
|
138
140
|
},
|
|
139
141
|
// password: must always be present and true no matter what customer sets
|
|
140
142
|
get mask_input_options() {
|
|
@@ -144,7 +146,7 @@ const model = () => {
|
|
|
144
146
|
if (val && typeof val === 'object') hiddenState.mask_input_options = {
|
|
145
147
|
...val,
|
|
146
148
|
password: true
|
|
147
|
-
};else warn(
|
|
149
|
+
};else warn(7, val);
|
|
148
150
|
}
|
|
149
151
|
},
|
|
150
152
|
session_trace: {
|
|
@@ -6,7 +6,7 @@ const model = {
|
|
|
6
6
|
export class SharedContext {
|
|
7
7
|
constructor(context) {
|
|
8
8
|
try {
|
|
9
|
-
if (typeof context !== 'object') return warn(
|
|
9
|
+
if (typeof context !== 'object') return warn(8);
|
|
10
10
|
this.sharedContext = {};
|
|
11
11
|
Object.assign(this.sharedContext, model);
|
|
12
12
|
Object.entries(context).forEach(_ref => {
|
|
@@ -14,7 +14,7 @@ export class SharedContext {
|
|
|
14
14
|
if (Object.keys(model).includes(key)) this.sharedContext[key] = value;
|
|
15
15
|
});
|
|
16
16
|
} catch (err) {
|
|
17
|
-
warn(
|
|
17
|
+
warn(9, err);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -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
|
-
if (!baseEE.backlog || !handlers) return;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
if (baseEE.aborted || !baseEE.backlog || !handlers) return;
|
|
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
|
/**
|
|
@@ -148,7 +148,7 @@ export class Harvest extends SharedContext {
|
|
|
148
148
|
body = stringify(body);
|
|
149
149
|
}
|
|
150
150
|
/** Warn --once per endpoint-- if the agent tries to send large payloads */
|
|
151
|
-
if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(
|
|
151
|
+
if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(28, endpoint);
|
|
152
152
|
}
|
|
153
153
|
if (!body || body.length === 0 || body === '{}' || body === '[]') {
|
|
154
154
|
// If body is null, undefined, or an empty object or array, send an empty string instead
|
|
@@ -190,7 +190,7 @@ export class SessionEntity {
|
|
|
190
190
|
}
|
|
191
191
|
return obj;
|
|
192
192
|
} catch (e) {
|
|
193
|
-
warn(
|
|
193
|
+
warn(10, e);
|
|
194
194
|
// storage is inaccessible
|
|
195
195
|
return {};
|
|
196
196
|
}
|
|
@@ -216,7 +216,7 @@ export class SessionEntity {
|
|
|
216
216
|
return data;
|
|
217
217
|
} catch (e) {
|
|
218
218
|
// storage is inaccessible
|
|
219
|
-
warn(
|
|
219
|
+
warn(11, e);
|
|
220
220
|
return null;
|
|
221
221
|
}
|
|
222
222
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { originTime } from '../constants/runtime';
|
|
2
2
|
import { getRuntime } from '../config/config';
|
|
3
|
+
const rfc2616Regex = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0-3][0-9]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-9]{4}) ([01][0-9]|2[0-3])(:[0-5][0-9]){2} GMT$/;
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Class used to adjust the timestamp of harvested data to New Relic server time. This
|
|
@@ -29,7 +30,7 @@ export class TimeKeeper {
|
|
|
29
30
|
/**
|
|
30
31
|
* Represents whether the timekeeper is in a state that it can accurately convert
|
|
31
32
|
* timestamps.
|
|
32
|
-
* @type {
|
|
33
|
+
* @type {boolean}
|
|
33
34
|
*/
|
|
34
35
|
#ready = false;
|
|
35
36
|
constructor(agentIdentifier) {
|
|
@@ -42,6 +43,9 @@ export class TimeKeeper {
|
|
|
42
43
|
get correctedOriginTime() {
|
|
43
44
|
return this.#correctedOriginTime;
|
|
44
45
|
}
|
|
46
|
+
get localTimeDiff() {
|
|
47
|
+
return this.#localTimeDiff;
|
|
48
|
+
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
51
|
* Process a rum request to calculate NR server time.
|
|
@@ -50,12 +54,16 @@ export class TimeKeeper {
|
|
|
50
54
|
* @param endTime {number} The end time of the RUM request
|
|
51
55
|
*/
|
|
52
56
|
processRumRequest(rumRequest, startTime, endTime) {
|
|
53
|
-
this.processStoredDiff();
|
|
57
|
+
this.processStoredDiff(); // Check session entity for stored time diff
|
|
54
58
|
if (this.#ready) return; // Server time calculated from session entity
|
|
59
|
+
|
|
55
60
|
const responseDateHeader = rumRequest.getResponseHeader('Date');
|
|
56
61
|
if (!responseDateHeader) {
|
|
57
62
|
throw new Error('Missing date header on rum response.');
|
|
58
63
|
}
|
|
64
|
+
if (!rfc2616Regex.test(responseDateHeader)) {
|
|
65
|
+
throw new Error('Date header invalid format.');
|
|
66
|
+
}
|
|
59
67
|
const medianRumOffset = (endTime - startTime) / 2;
|
|
60
68
|
const serverOffset = startTime + medianRumOffset;
|
|
61
69
|
|
|
@@ -92,6 +100,8 @@ export class TimeKeeper {
|
|
|
92
100
|
|
|
93
101
|
/** Process the session entity and use the info to set the main time calculations if present */
|
|
94
102
|
processStoredDiff() {
|
|
103
|
+
if (this.#ready) return; // Time diff has already been calculated
|
|
104
|
+
|
|
95
105
|
const storedServerTimeDiff = this.#session?.read()?.serverTimeDiff;
|
|
96
106
|
if (typeof storedServerTimeDiff === 'number' && !isNaN(storedServerTimeDiff)) {
|
|
97
107
|
this.#localTimeDiff = storedServerTimeDiff;
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* @param {*} [secondary] Secondary data to include, usually an error or object
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
|
-
export function warn(
|
|
8
|
-
if (typeof console.
|
|
9
|
-
console.
|
|
10
|
-
if (secondary) console.warn(secondary);
|
|
7
|
+
export function warn(code, secondary) {
|
|
8
|
+
if (typeof console.debug !== 'function') return;
|
|
9
|
+
console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
|
|
11
10
|
}
|
|
@@ -45,15 +45,15 @@ export function validateRules(rules) {
|
|
|
45
45
|
var invalidRegexDetected = false;
|
|
46
46
|
for (var i = 0; i < rules.length; i++) {
|
|
47
47
|
if (!('regex' in rules[i])) {
|
|
48
|
-
warn(
|
|
48
|
+
warn(12);
|
|
49
49
|
invalidRegexDetected = true;
|
|
50
50
|
} else if (typeof rules[i].regex !== 'string' && !(rules[i].regex instanceof RegExp)) {
|
|
51
|
-
warn(
|
|
51
|
+
warn(13);
|
|
52
52
|
invalidRegexDetected = true;
|
|
53
53
|
}
|
|
54
54
|
var replacement = rules[i].replacement;
|
|
55
55
|
if (replacement && typeof replacement !== 'string') {
|
|
56
|
-
warn(
|
|
56
|
+
warn(14);
|
|
57
57
|
invalidReplacementDetected = true;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { EventContext } from '../event-emitter/event-context';
|
|
12
|
+
import { warn } from '../util/console';
|
|
12
13
|
import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -20,6 +21,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
|
20
21
|
*/
|
|
21
22
|
// eslint-disable-next-line
|
|
22
23
|
export function wrapLogger(sharedEE, parent, loggerFn, context) {
|
|
24
|
+
if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(29);
|
|
23
25
|
const ee = scopedEE(sharedEE);
|
|
24
26
|
const wrapFn = wfn(ee);
|
|
25
27
|
|
|
@@ -49,7 +49,7 @@ export function wrapXhr(sharedEE) {
|
|
|
49
49
|
ee.emit('new-xhr', [xhr], context);
|
|
50
50
|
xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false));
|
|
51
51
|
} catch (e) {
|
|
52
|
-
warn(
|
|
52
|
+
warn(15, e);
|
|
53
53
|
try {
|
|
54
54
|
ee.emit('internal-error', [e]);
|
|
55
55
|
} catch (err) {
|
|
@@ -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,
|
|
9
|
+
import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, 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(30, level);
|
|
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(16, message);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (typeof message !== 'string' || !message) return warn(32);
|
|
64
|
+
if (message.length > MAX_PAYLOAD_SIZE) {
|
|
65
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length]);
|
|
66
|
+
return warn(31, 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(
|
|
72
|
+
return warn(31, 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]);
|
|
@@ -8,7 +8,4 @@ export const LOG_LEVELS = {
|
|
|
8
8
|
};
|
|
9
9
|
export const LOGGING_EVENT_EMITTER_CHANNEL = 'log';
|
|
10
10
|
export const FEATURE_NAME = FEATURE_NAMES.logging;
|
|
11
|
-
export const MAX_PAYLOAD_SIZE = 1000000;
|
|
12
|
-
export const LOGGING_FAILURE_MESSAGE = 'failed to wrap logger: ';
|
|
13
|
-
export const LOGGING_LEVEL_FAILURE_MESSAGE = 'invalid log level: ';
|
|
14
|
-
export const LOGGING_IGNORED = 'ignored log: ';
|
|
11
|
+
export const MAX_PAYLOAD_SIZE = 1000000;
|
|
@@ -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
|
}
|
|
@@ -102,6 +102,22 @@ export class Aggregate extends AggregateBase {
|
|
|
102
102
|
// Check if proxy for either chunks or beacon is being used
|
|
103
103
|
if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed');
|
|
104
104
|
if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed');
|
|
105
|
+
if (isBrowserScope && window.MutationObserver) {
|
|
106
|
+
this.storeSupportabilityMetrics('Generic/VideoElement/Added', window.document.querySelectorAll('video').length);
|
|
107
|
+
const mo = new MutationObserver(records => {
|
|
108
|
+
records.forEach(record => {
|
|
109
|
+
record.addedNodes.forEach(addedNode => {
|
|
110
|
+
if (addedNode instanceof HTMLVideoElement) {
|
|
111
|
+
this.storeSupportabilityMetrics('Generic/VideoElement/Added', 1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
mo.observe(window.document.body, {
|
|
117
|
+
childList: true,
|
|
118
|
+
subtree: true
|
|
119
|
+
});
|
|
120
|
+
}
|
|
105
121
|
}
|
|
106
122
|
eachSessionChecks() {
|
|
107
123
|
if (!isBrowserScope) return;
|