@newrelic/browser-agent 1.262.0 → 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 +13 -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 +1 -1
- 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 +1 -2
- package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
- package/dist/cjs/features/logging/aggregate/index.js +5 -5
- package/dist/cjs/features/logging/constants.js +2 -5
- 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/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 +9 -7
- 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 +1 -1
- 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 +1 -2
- package/dist/esm/common/wrap/wrap-xhr.js +1 -1
- package/dist/esm/features/logging/aggregate/index.js +6 -6
- package/dist/esm/features/logging/constants.js +1 -4
- 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/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 +9 -7
- 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/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/constants.d.ts +0 -3
- package/dist/types/features/logging/constants.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 +1 -1
- 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 +1 -2
- package/src/common/wrap/wrap-xhr.js +1 -1
- package/src/features/logging/aggregate/index.js +6 -6
- package/src/features/logging/constants.js +0 -4
- 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/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 +9 -7
- package/src/loaders/micro-agent.js +5 -5
|
@@ -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
|
}
|
|
@@ -99,7 +99,7 @@ function drainGroup(agentIdentifier, group) {
|
|
|
99
99
|
let activateGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
100
100
|
const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee;
|
|
101
101
|
const handlers = defaultRegister.handlers; // other storage in registerHandler
|
|
102
|
-
if (!baseEE.backlog || !handlers) return;
|
|
102
|
+
if (baseEE.aborted || !baseEE.backlog || !handlers) return;
|
|
103
103
|
|
|
104
104
|
// Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
|
|
105
105
|
if (activateGroup) {
|
|
@@ -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
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* This module is used by: jserrors, spa.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { LOGGING_FAILURE_MESSAGE } from '../../features/logging/constants';
|
|
11
10
|
import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
|
|
12
11
|
import { EventContext } from '../event-emitter/event-context';
|
|
13
12
|
import { warn } from '../util/console';
|
|
@@ -22,7 +21,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
|
22
21
|
*/
|
|
23
22
|
// eslint-disable-next-line
|
|
24
23
|
export function wrapLogger(sharedEE, parent, loggerFn, context) {
|
|
25
|
-
if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(
|
|
24
|
+
if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(29);
|
|
26
25
|
const ee = scopedEE(sharedEE);
|
|
27
26
|
const wrapFn = wfn(ee);
|
|
28
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,7 +6,7 @@ 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
11
|
import { isValidLogLevel } from '../shared/utils';
|
|
12
12
|
export class Aggregate extends AggregateBase {
|
|
@@ -45,7 +45,7 @@ export class Aggregate extends AggregateBase {
|
|
|
45
45
|
if (this.blocked) return;
|
|
46
46
|
if (!attributes || typeof attributes !== 'object') attributes = {};
|
|
47
47
|
if (typeof level === 'string') level = level.toUpperCase();
|
|
48
|
-
if (!isValidLogLevel(level)) return warn(
|
|
48
|
+
if (!isValidLogLevel(level)) return warn(30, level);
|
|
49
49
|
try {
|
|
50
50
|
if (typeof message !== 'string') {
|
|
51
51
|
const stringified = stringify(message);
|
|
@@ -57,19 +57,19 @@ export class Aggregate extends AggregateBase {
|
|
|
57
57
|
if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
|
|
58
58
|
}
|
|
59
59
|
} catch (err) {
|
|
60
|
-
warn(
|
|
60
|
+
warn(16, message);
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
if (typeof message !== 'string' || !message) return warn(
|
|
63
|
+
if (typeof message !== 'string' || !message) return warn(32);
|
|
64
64
|
if (message.length > MAX_PAYLOAD_SIZE) {
|
|
65
65
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length]);
|
|
66
|
-
return warn(
|
|
66
|
+
return warn(31, message.slice(0, 25) + '...');
|
|
67
67
|
}
|
|
68
68
|
const log = new Log(this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp), message, attributes, level);
|
|
69
69
|
const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10; // timestamp == 10 chars
|
|
70
70
|
if (logBytes > MAX_PAYLOAD_SIZE) {
|
|
71
71
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes]);
|
|
72
|
-
return warn(
|
|
72
|
+
return warn(31, log.message.slice(0, 25) + '...');
|
|
73
73
|
}
|
|
74
74
|
if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
|
|
75
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;
|
|
@@ -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;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { globalScope, isBrowserScope, originTime } from '../../../common/constants/runtime';
|
|
2
2
|
import { addPT, addPN } from '../../../common/timing/nav-timing';
|
|
3
3
|
import { stringify } from '../../../common/util/stringify';
|
|
4
|
-
import { getInfo, getRuntime } from '../../../common/config/config';
|
|
4
|
+
import { getInfo, getRuntime, isConfigured } from '../../../common/config/config';
|
|
5
5
|
import { Harvest } from '../../../common/harvest/harvest';
|
|
6
6
|
import * as CONSTANTS from '../constants';
|
|
7
7
|
import { getActivatedFeaturesFlags } from './initialized-features';
|
|
@@ -25,6 +25,10 @@ export class Aggregate extends AggregateBase {
|
|
|
25
25
|
this.firstByteToWindowLoad = 0; // our "frontend" duration
|
|
26
26
|
this.firstByteToDomContent = 0; // our "dom processing" duration
|
|
27
27
|
this.timeKeeper = new TimeKeeper(this.agentIdentifier);
|
|
28
|
+
if (!isConfigured(agentIdentifier)) {
|
|
29
|
+
this.ee.abort();
|
|
30
|
+
return warn(43);
|
|
31
|
+
}
|
|
28
32
|
if (isBrowserScope) {
|
|
29
33
|
timeToFirstByte.subscribe(_ref => {
|
|
30
34
|
let {
|
|
@@ -47,7 +51,6 @@ export class Aggregate extends AggregateBase {
|
|
|
47
51
|
const info = getInfo(this.agentIdentifier);
|
|
48
52
|
const agentRuntime = getRuntime(this.agentIdentifier);
|
|
49
53
|
const harvester = new Harvest(this);
|
|
50
|
-
if (!info.beacon) return;
|
|
51
54
|
if (info.queueTime) this.aggregator.store('measures', 'qt', {
|
|
52
55
|
value: info.queueTime
|
|
53
56
|
});
|
|
@@ -140,11 +143,24 @@ export class Aggregate extends AggregateBase {
|
|
|
140
143
|
this.timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
|
|
141
144
|
if (!this.timeKeeper.ready) throw new Error('TimeKeeper not ready');
|
|
142
145
|
agentRuntime.timeKeeper = this.timeKeeper;
|
|
146
|
+
|
|
147
|
+
// Check if the time diff is such that we need to capture a supportability metric
|
|
148
|
+
if (this.timeKeeper.localTimeDiff >= 12 * 60 * 60 * 1000) {
|
|
149
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed12Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
150
|
+
} else if (this.timeKeeper.localTimeDiff >= 6 * 60 * 60 * 1000) {
|
|
151
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed6Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
152
|
+
} else if (this.timeKeeper.localTimeDiff >= 60 * 60 * 1000) {
|
|
153
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed1Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
154
|
+
}
|
|
143
155
|
} catch (error) {
|
|
144
|
-
|
|
156
|
+
if (error?.message?.indexOf('invalid format') > 0) {
|
|
157
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/InvalidFormat'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
158
|
+
} else {
|
|
159
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
160
|
+
}
|
|
145
161
|
drain(this.agentIdentifier, FEATURE_NAMES.metrics, true);
|
|
146
162
|
this.ee.abort();
|
|
147
|
-
warn(
|
|
163
|
+
warn(17, error);
|
|
148
164
|
return;
|
|
149
165
|
}
|
|
150
166
|
try {
|
|
@@ -157,7 +173,7 @@ export class Aggregate extends AggregateBase {
|
|
|
157
173
|
this.drain();
|
|
158
174
|
} catch (err) {
|
|
159
175
|
this.ee.abort();
|
|
160
|
-
warn(
|
|
176
|
+
warn(18, err);
|
|
161
177
|
}
|
|
162
178
|
}
|
|
163
179
|
});
|
|
@@ -416,7 +416,7 @@ export class Aggregate extends AggregateBase {
|
|
|
416
416
|
abort() {
|
|
417
417
|
let reason = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
418
418
|
let data = arguments.length > 1 ? arguments[1] : undefined;
|
|
419
|
-
warn(
|
|
419
|
+
warn(33, reason.message);
|
|
420
420
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ["SessionReplay/Abort/".concat(reason.sm), data], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
421
421
|
this.blocked = true;
|
|
422
422
|
this.mode = MODE.OFF;
|
|
@@ -33,7 +33,7 @@ export class Recorder {
|
|
|
33
33
|
/** Config to inform to inline stylesheet contents (true default) */
|
|
34
34
|
this.shouldInlineStylesheets = getConfigurationValue(this.parent.agentIdentifier, 'session_replay.inline_stylesheet');
|
|
35
35
|
/** A flag that can be set to false by failing conversions to stop the fetching process */
|
|
36
|
-
this.shouldFix = this.shouldInlineStylesheets;
|
|
36
|
+
this.shouldFix = this.shouldInlineStylesheets && getConfigurationValue(this.parent.agentIdentifier, 'session_replay.fix_stylesheets');
|
|
37
37
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
38
38
|
this.stopRecording = () => {/* no-op until set by rrweb initializer */};
|
|
39
39
|
}
|
|
@@ -693,7 +693,7 @@ export class Aggregate extends AggregateBase {
|
|
|
693
693
|
if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.routeChange) smCategory = 'RouteChange';else smCategory = 'Custom';
|
|
694
694
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ["Spa/Interaction/".concat(smCategory, "/Duration/Ms"), Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE);
|
|
695
695
|
scheduler?.scheduleHarvest(0);
|
|
696
|
-
if (!scheduler) warn(
|
|
696
|
+
if (!scheduler) warn(19);
|
|
697
697
|
}
|
|
698
698
|
function isEnabled() {
|
|
699
699
|
var enabled = getConfigurationValue(agentIdentifier, 'spa.enabled');
|
|
@@ -49,8 +49,9 @@ export class AggregateBase extends FeatureBase {
|
|
|
49
49
|
checkConfiguration() {
|
|
50
50
|
// NOTE: This check has to happen at aggregator load time
|
|
51
51
|
if (!isConfigured(this.agentIdentifier)) {
|
|
52
|
+
const cdn = gosCDN();
|
|
52
53
|
let jsAttributes = {
|
|
53
|
-
...
|
|
54
|
+
...cdn.info?.jsAttributes
|
|
54
55
|
};
|
|
55
56
|
try {
|
|
56
57
|
jsAttributes = {
|
|
@@ -63,9 +64,9 @@ export class AggregateBase extends FeatureBase {
|
|
|
63
64
|
configure({
|
|
64
65
|
agentIdentifier: this.agentIdentifier
|
|
65
66
|
}, {
|
|
66
|
-
...
|
|
67
|
+
...cdn,
|
|
67
68
|
info: {
|
|
68
|
-
...
|
|
69
|
+
...cdn.info,
|
|
69
70
|
jsAttributes
|
|
70
71
|
},
|
|
71
72
|
runtime: getRuntime(this.agentIdentifier)
|
|
@@ -87,7 +87,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
87
87
|
session = setupAgentSession(this.agentIdentifier);
|
|
88
88
|
}
|
|
89
89
|
} catch (e) {
|
|
90
|
-
warn(
|
|
90
|
+
warn(20, e);
|
|
91
91
|
this.ee.emit('internal-error', [e]);
|
|
92
92
|
if (this.featureName === FEATURE_NAMES.sessionReplay) this.abortHandler?.(); // SR should stop recording if session DNE
|
|
93
93
|
}
|
|
@@ -111,7 +111,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
111
111
|
this.featAggregate = new Aggregate(this.agentIdentifier, this.aggregator, argsObjFromInstrument);
|
|
112
112
|
loadedSuccessfully(true);
|
|
113
113
|
} catch (e) {
|
|
114
|
-
warn(
|
|
114
|
+
warn(34, e);
|
|
115
115
|
this.abortHandler?.(); // undo any important alterations made to the page
|
|
116
116
|
// not supported yet but nice to do: "abort" this agent's EE for this feature specifically
|
|
117
117
|
drain(this.agentIdentifier, this.featureName, true);
|
|
@@ -28,7 +28,7 @@ export class AgentBase {
|
|
|
28
28
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
29
29
|
args[_key - 1] = arguments[_key];
|
|
30
30
|
}
|
|
31
|
-
if (typeof this.api?.[methodName] !== 'function') warn(
|
|
31
|
+
if (typeof this.api?.[methodName] !== 'function') warn(35, methodName);else return this.api[methodName](...args);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -13,7 +13,6 @@ import { Instrument as PageViewEvent } from '../features/page_view_event/instrum
|
|
|
13
13
|
import { Aggregator } from '../common/aggregate/aggregator';
|
|
14
14
|
import { gosNREUM, setNREUMInitializedAgent } from '../common/window/nreum';
|
|
15
15
|
import { warn } from '../common/util/console';
|
|
16
|
-
import { stringify } from '../common/util/stringify';
|
|
17
16
|
import { globalScope } from '../common/constants/runtime';
|
|
18
17
|
|
|
19
18
|
/**
|
|
@@ -30,7 +29,7 @@ export class Agent extends AgentBase {
|
|
|
30
29
|
if (!globalScope) {
|
|
31
30
|
// We could not determine the runtime environment. Short-circuite the agent here
|
|
32
31
|
// to avoid possible exceptions later that may cause issues with customer's application.
|
|
33
|
-
warn(
|
|
32
|
+
warn(21);
|
|
34
33
|
return;
|
|
35
34
|
}
|
|
36
35
|
this.sharedAggregator = new Aggregator({
|
|
@@ -69,11 +68,11 @@ export class Agent extends AgentBase {
|
|
|
69
68
|
if (!this.runSoftNavOverSpa && InstrumentCtor.featureName === FEATURE_NAMES.softNav) return;
|
|
70
69
|
const dependencies = getFeatureDependencyNames(InstrumentCtor.featureName);
|
|
71
70
|
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
|
|
72
|
-
if (!hasAllDeps) warn(
|
|
71
|
+
if (!hasAllDeps) warn(36, InstrumentCtor.featureName);
|
|
73
72
|
this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator);
|
|
74
73
|
});
|
|
75
74
|
} catch (err) {
|
|
76
|
-
warn(
|
|
75
|
+
warn(22, err);
|
|
77
76
|
for (const featName in this.features) {
|
|
78
77
|
// this.features hold only features that have been instantiated
|
|
79
78
|
this.features[featName].abortHandler?.();
|
|
@@ -36,7 +36,7 @@ export function setTopLevelCallers() {
|
|
|
36
36
|
let returnVals = [];
|
|
37
37
|
Object.values(nr.initializedAgents).forEach(val => {
|
|
38
38
|
if (!val || !val.api) {
|
|
39
|
-
warn(
|
|
39
|
+
warn(38, fnName);
|
|
40
40
|
} else if (val.exposed && val.api[fnName]) {
|
|
41
41
|
returnVals.push(val.api[fnName](...args));
|
|
42
42
|
}
|
|
@@ -62,6 +62,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
62
62
|
customAttributes = {},
|
|
63
63
|
level = LOG_LEVELS.INFO
|
|
64
64
|
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
65
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/log/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
|
|
65
66
|
bufferLog(instanceEE, message, customAttributes, level);
|
|
66
67
|
};
|
|
67
68
|
apiInterface.wrapLogger = function (parent, functionName) {
|
|
@@ -69,6 +70,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
69
70
|
customAttributes = {},
|
|
70
71
|
level = LOG_LEVELS.INFO
|
|
71
72
|
} = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
73
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/wrapLogger/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
|
|
72
74
|
wrapLogger(instanceEE, parent, functionName, {
|
|
73
75
|
customAttributes,
|
|
74
76
|
level
|
|
@@ -113,11 +115,11 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
113
115
|
apiInterface.setCustomAttribute = function (name, value) {
|
|
114
116
|
let persistAttribute = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
115
117
|
if (typeof name !== 'string') {
|
|
116
|
-
warn(
|
|
118
|
+
warn(39, typeof name);
|
|
117
119
|
return;
|
|
118
120
|
}
|
|
119
121
|
if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
|
|
120
|
-
warn(
|
|
122
|
+
warn(40, typeof value);
|
|
121
123
|
return;
|
|
122
124
|
}
|
|
123
125
|
return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute);
|
|
@@ -129,7 +131,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
129
131
|
*/
|
|
130
132
|
apiInterface.setUserId = function (value) {
|
|
131
133
|
if (!(typeof value === 'string' || value === null)) {
|
|
132
|
-
warn(
|
|
134
|
+
warn(41, typeof value);
|
|
133
135
|
return;
|
|
134
136
|
}
|
|
135
137
|
return appendJsAttribute('enduser.id', value, 'setUserId', true);
|
|
@@ -142,7 +144,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
142
144
|
*/
|
|
143
145
|
apiInterface.setApplicationVersion = function (value) {
|
|
144
146
|
if (!(typeof value === 'string' || value === null)) {
|
|
145
|
-
warn(
|
|
147
|
+
warn(42, typeof value);
|
|
146
148
|
return;
|
|
147
149
|
}
|
|
148
150
|
return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
|
|
@@ -152,7 +154,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
152
154
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
|
|
153
155
|
instanceEE.emit('manual-start-all');
|
|
154
156
|
} catch (err) {
|
|
155
|
-
warn(
|
|
157
|
+
warn(23, err);
|
|
156
158
|
}
|
|
157
159
|
};
|
|
158
160
|
apiInterface[SR_EVENT_EMITTER_TYPES.RECORD] = function () {
|
|
@@ -221,7 +223,7 @@ export function setAPI(agentIdentifier, forceDrain) {
|
|
|
221
223
|
setAPI(agentIdentifier);
|
|
222
224
|
drain(agentIdentifier, 'api');
|
|
223
225
|
}).catch(err => {
|
|
224
|
-
warn(
|
|
226
|
+
warn(27, err);
|
|
225
227
|
instanceEE.abort();
|
|
226
228
|
});
|
|
227
229
|
}
|