@newrelic/browser-agent 1.236.0 → 1.237.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/config/state/init.js +1 -0
- package/dist/cjs/common/config/state/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/deny-list/deny-list.js +14 -10
- package/dist/cjs/common/harvest/harvest.js +8 -22
- package/dist/cjs/common/util/submit-data.js +4 -36
- package/dist/cjs/common/wrap/wrap-jsonp.js +12 -6
- package/dist/cjs/features/ajax/aggregate/index.js +24 -27
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/dist/cjs/features/jserrors/constants.js +2 -4
- package/dist/cjs/features/jserrors/instrument/index.js +79 -88
- package/dist/cjs/features/jserrors/instrument/uncaught-error.js +22 -0
- package/dist/cjs/features/metrics/aggregate/index.js +8 -0
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +23 -19
- package/dist/cjs/features/session_replay/aggregate/index.js +65 -34
- package/dist/cjs/features/session_trace/aggregate/index.js +3 -4
- package/dist/cjs/features/spa/aggregate/index.js +1 -1
- package/dist/cjs/features/utils/instrument-base.js +6 -8
- package/dist/cjs/loaders/agent-base.js +87 -0
- package/dist/cjs/loaders/agent.js +41 -1
- package/dist/cjs/loaders/api/api.js +1 -1
- package/dist/cjs/loaders/api/interaction-types.js +87 -0
- package/dist/cjs/loaders/configure/configure.js +3 -1
- package/dist/cjs/loaders/micro-agent.js +3 -1
- package/dist/esm/common/config/state/init.js +1 -0
- package/dist/esm/common/config/state/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/deny-list/deny-list.js +14 -10
- package/dist/esm/common/harvest/harvest.js +7 -22
- package/dist/esm/common/util/submit-data.js +4 -35
- package/dist/esm/common/wrap/wrap-jsonp.js +12 -6
- package/dist/esm/features/ajax/aggregate/index.js +25 -28
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/dist/esm/features/jserrors/constants.js +1 -2
- package/dist/esm/features/jserrors/instrument/index.js +78 -87
- package/dist/esm/features/jserrors/instrument/uncaught-error.js +15 -0
- package/dist/esm/features/metrics/aggregate/index.js +8 -0
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +23 -19
- package/dist/esm/features/session_replay/aggregate/index.js +65 -34
- package/dist/esm/features/session_trace/aggregate/index.js +3 -4
- package/dist/esm/features/spa/aggregate/index.js +1 -1
- package/dist/esm/features/utils/instrument-base.js +7 -9
- package/dist/esm/loaders/agent-base.js +80 -0
- package/dist/esm/loaders/agent.js +41 -1
- package/dist/esm/loaders/api/api.js +1 -1
- package/dist/esm/loaders/api/interaction-types.js +80 -0
- package/dist/esm/loaders/configure/configure.js +5 -3
- package/dist/esm/loaders/micro-agent.js +3 -1
- package/dist/types/common/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts +6 -6
- package/dist/types/common/util/submit-data.d.ts +2 -20
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts +2 -2
- package/dist/types/common/wrap/wrap-jsonp.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts +5 -5
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/constants.d.ts +0 -1
- package/dist/types/features/jserrors/constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts +0 -13
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/uncaught-error.d.ts +15 -0
- package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +1 -0
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +16 -30
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +59 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -0
- package/dist/types/loaders/agent.d.ts +35 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/interaction-types.d.ts +122 -0
- package/dist/types/loaders/api/interaction-types.d.ts.map +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +9 -9
- package/dist/types/loaders/micro-agent.d.ts +3 -2
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/state/init.js +1 -1
- package/src/common/config/state/runtime.js +2 -1
- package/src/common/deny-list/deny-list.js +11 -11
- package/src/common/deny-list/deny-list.test.js +31 -0
- package/src/common/harvest/harvest.js +8 -18
- package/src/common/harvest/harvest.test.js +16 -36
- package/src/common/util/__mocks__/submit-data.js +0 -1
- package/src/common/util/submit-data.js +2 -24
- package/src/common/util/submit-data.test.js +0 -56
- package/src/common/wrap/wrap-jsonp.js +11 -6
- package/src/features/ajax/aggregate/index.js +25 -31
- package/src/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/src/features/jserrors/constants.js +0 -1
- package/src/features/jserrors/instrument/index.js +91 -87
- package/src/features/jserrors/instrument/uncaught-error.js +15 -0
- package/src/features/metrics/aggregate/index.js +8 -0
- package/src/features/page_view_event/aggregate/initialized-features.js +18 -14
- package/src/features/session_replay/aggregate/index.component-test.js +17 -56
- package/src/features/session_replay/aggregate/index.js +47 -28
- package/src/features/session_trace/aggregate/index.js +3 -4
- package/src/features/spa/aggregate/index.js +1 -1
- package/src/features/utils/instrument-base.js +6 -9
- package/src/features/utils/instrument-base.test.js +7 -0
- package/src/loaders/agent-base.js +81 -0
- package/src/loaders/agent.js +42 -1
- package/src/loaders/api/api.js +1 -1
- package/src/loaders/api/interaction-types.js +80 -0
- package/src/loaders/configure/configure.js +14 -4
- package/src/loaders/micro-agent.js +4 -1
- package/dist/cjs/features/jserrors/instrument/debug.js +0 -40
- package/dist/esm/features/jserrors/instrument/debug.js +0 -38
- package/dist/types/features/jserrors/instrument/debug.d.ts +0 -2
- package/dist/types/features/jserrors/instrument/debug.d.ts.map +0 -1
- package/src/features/jserrors/instrument/debug.js +0 -36
|
@@ -30,7 +30,8 @@ const model = {
|
|
|
30
30
|
releaseIds: {},
|
|
31
31
|
session: undefined,
|
|
32
32
|
xhrWrappable: typeof _runtime.globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
|
|
33
|
-
version: _env.VERSION
|
|
33
|
+
version: _env.VERSION,
|
|
34
|
+
denyList: undefined
|
|
34
35
|
};
|
|
35
36
|
const _cache = {};
|
|
36
37
|
function getRuntime(id) {
|
|
@@ -47,24 +47,28 @@ function setDenyList(denyListConfig) {
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
for (var i = 0; i < denyListConfig.length; i++) {
|
|
50
|
-
|
|
50
|
+
let url = denyListConfig[i];
|
|
51
|
+
if (!url) continue; // ignore bad values like undefined or empty strings
|
|
52
|
+
|
|
51
53
|
if (url.indexOf('http://') === 0) {
|
|
52
54
|
url = url.substring(7);
|
|
53
55
|
} else if (url.indexOf('https://') === 0) {
|
|
54
56
|
url = url.substring(8);
|
|
55
57
|
}
|
|
56
|
-
|
|
58
|
+
const firstSlash = url.indexOf('/');
|
|
59
|
+
let host, pathname;
|
|
57
60
|
if (firstSlash > 0) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
pathname: url.substring(firstSlash)
|
|
61
|
-
});
|
|
61
|
+
host = url.substring(0, firstSlash);
|
|
62
|
+
pathname = url.substring(firstSlash);
|
|
62
63
|
} else {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
pathname: ''
|
|
66
|
-
});
|
|
64
|
+
host = url;
|
|
65
|
+
pathname = '';
|
|
67
66
|
}
|
|
67
|
+
let [hostname, port] = host.split(':');
|
|
68
|
+
denyList.push({
|
|
69
|
+
hostname,
|
|
70
|
+
pathname
|
|
71
|
+
});
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
/**
|
|
@@ -17,6 +17,7 @@ var _traverse = require("../util/traverse");
|
|
|
17
17
|
var _sharedContext = require("../context/shared-context");
|
|
18
18
|
var _env = require("../constants/env.npm");
|
|
19
19
|
var _runtime = require("../constants/runtime");
|
|
20
|
+
var _console = require("../util/console");
|
|
20
21
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
21
22
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
22
23
|
/*
|
|
@@ -24,6 +25,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
24
25
|
* SPDX-License-Identifier: Apache-2.0
|
|
25
26
|
*/
|
|
26
27
|
|
|
28
|
+
const warnings = {};
|
|
29
|
+
|
|
27
30
|
/**
|
|
28
31
|
* @typedef {import('./types.js').NetworkSendSpec} NetworkSendSpec
|
|
29
32
|
* @typedef {import('./types.js').HarvestEndpointIdentifier} HarvestEndpointIdentifier
|
|
@@ -31,7 +34,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
31
34
|
* @typedef {import('./types.js').FeatureHarvestCallback} FeatureHarvestCallback
|
|
32
35
|
* @typedef {import('./types.js').FeatureHarvestCallbackOptions} FeatureHarvestCallbackOptions
|
|
33
36
|
*/
|
|
34
|
-
|
|
35
37
|
class Harvest extends _sharedContext.SharedContext {
|
|
36
38
|
constructor(parent) {
|
|
37
39
|
super(parent); // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
@@ -142,13 +144,15 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
142
144
|
payloadParams = payloadParams.substring(1);
|
|
143
145
|
}
|
|
144
146
|
const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
|
|
145
|
-
const gzip = qs
|
|
147
|
+
const gzip = !!qs?.attributes?.includes('gzip');
|
|
146
148
|
if (!gzip) {
|
|
147
149
|
if (endpoint === 'events') {
|
|
148
150
|
body = body.e;
|
|
149
151
|
} else {
|
|
150
152
|
body = (0, _stringify.stringify)(body);
|
|
151
153
|
}
|
|
154
|
+
/** Warn --once per endpoint-- if the agent tries to send large payloads */
|
|
155
|
+
if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) (0, _console.warn)("The Browser Agent is attempting to send a very large payload to /".concat(endpoint, ". This is usually tied to large amounts of custom attributes. Please check your configurations."));
|
|
152
156
|
}
|
|
153
157
|
if (!body || body.length === 0 || body === '{}' || body === '[]') {
|
|
154
158
|
// If body is null, undefined, or an empty object or array, send an empty string instead
|
|
@@ -195,23 +199,6 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
195
199
|
cbFinished(cbResult);
|
|
196
200
|
}, (0, _eventListenerOpts.eventListenerOpts)(false));
|
|
197
201
|
}
|
|
198
|
-
|
|
199
|
-
// if beacon request failed, retry with an alternative method -- will not happen for workers
|
|
200
|
-
if (!result && submitMethod === submitData.beacon) {
|
|
201
|
-
// browsers that support sendBeacon also support fetch with keepalive - IE will not retry unload calls
|
|
202
|
-
submitMethod = submitData.fetchKeepAlive;
|
|
203
|
-
try {
|
|
204
|
-
submitMethod({
|
|
205
|
-
url: fullUrl,
|
|
206
|
-
body,
|
|
207
|
-
headers
|
|
208
|
-
});
|
|
209
|
-
} catch (e) {
|
|
210
|
-
// Ignore error in final harvest
|
|
211
|
-
} finally {
|
|
212
|
-
result = true;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
202
|
return result;
|
|
216
203
|
}
|
|
217
204
|
|
|
@@ -271,9 +258,8 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
271
258
|
cleanPayload() {
|
|
272
259
|
let payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
273
260
|
const clean = input => {
|
|
274
|
-
if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array)
|
|
275
|
-
|
|
276
|
-
}
|
|
261
|
+
if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array || Array.isArray(input)) return input;
|
|
262
|
+
if (typeof input === 'string') return input.length > 0 ? input : null;
|
|
277
263
|
return Object.entries(input || {}).reduce((accumulator, _ref2) => {
|
|
278
264
|
let [key, value] = _ref2;
|
|
279
265
|
if (typeof value === 'number' || typeof value === 'string' && value.length > 0 || typeof value === 'object' && Object.keys(value || {}).length > 0) {
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.beacon = beacon;
|
|
7
|
-
exports.fetchKeepAlive = fetchKeepAlive;
|
|
8
7
|
exports.getSubmitMethod = getSubmitMethod;
|
|
9
8
|
exports.xhr = xhr;
|
|
10
9
|
var _runtime = require("../constants/runtime");
|
|
@@ -15,7 +14,7 @@ var _runtime = require("../constants/runtime");
|
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
|
-
* @typedef {xhr|
|
|
17
|
+
* @typedef {xhr|beacon} NetworkMethods
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
20
|
/**
|
|
@@ -72,36 +71,6 @@ function xhr(_ref) {
|
|
|
72
71
|
return request;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
/**
|
|
76
|
-
* Send via fetch with keepalive true
|
|
77
|
-
* @param {Object} args - The args.
|
|
78
|
-
* @param {string} args.url - The URL to send to.
|
|
79
|
-
* @param {string=} args.body - The Stringified body.
|
|
80
|
-
* @param {string=} [args.method=POST] - The XHR method to use.
|
|
81
|
-
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
|
|
82
|
-
* @returns {XMLHttpRequest}
|
|
83
|
-
*/
|
|
84
|
-
function fetchKeepAlive(_ref2) {
|
|
85
|
-
let {
|
|
86
|
-
url,
|
|
87
|
-
body = null,
|
|
88
|
-
method = 'POST',
|
|
89
|
-
headers = [{
|
|
90
|
-
key: 'content-type',
|
|
91
|
-
value: 'text/plain'
|
|
92
|
-
}]
|
|
93
|
-
} = _ref2;
|
|
94
|
-
return fetch(url, {
|
|
95
|
-
method,
|
|
96
|
-
headers: headers.reduce((aggregator, header) => {
|
|
97
|
-
aggregator.push([header.key, header.value]);
|
|
98
|
-
return aggregator;
|
|
99
|
-
}, []),
|
|
100
|
-
body,
|
|
101
|
-
keepalive: true
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
74
|
/**
|
|
106
75
|
* Send via sendBeacon. Do NOT call this function outside of a guaranteed web window environment.
|
|
107
76
|
* @param {Object} args - The args
|
|
@@ -109,11 +78,11 @@ function fetchKeepAlive(_ref2) {
|
|
|
109
78
|
* @param {string=} args.body - The Stringified body
|
|
110
79
|
* @returns {boolean}
|
|
111
80
|
*/
|
|
112
|
-
function beacon(
|
|
81
|
+
function beacon(_ref2) {
|
|
113
82
|
let {
|
|
114
83
|
url,
|
|
115
84
|
body
|
|
116
|
-
} =
|
|
85
|
+
} = _ref2;
|
|
117
86
|
try {
|
|
118
87
|
// Navigator has to be bound to ensure it does not error in some browsers
|
|
119
88
|
// https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
|
|
@@ -121,8 +90,7 @@ function beacon(_ref3) {
|
|
|
121
90
|
return send(url, body);
|
|
122
91
|
} catch (err) {
|
|
123
92
|
// if sendBeacon still trys to throw an illegal invocation error,
|
|
124
|
-
// we can catch here and return
|
|
125
|
-
// fetchKeepAlive to try to send
|
|
93
|
+
// we can catch here and return
|
|
126
94
|
return false;
|
|
127
95
|
}
|
|
128
96
|
}
|
|
@@ -93,13 +93,19 @@ function wrapJsonP(sharedEE) {
|
|
|
93
93
|
var matches = src.match(CALLBACK_REGEX);
|
|
94
94
|
return matches ? matches[1] : null;
|
|
95
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Traverse a nested object using a '.'-delimited string wherein each substring piece maps to each subsequent object property layer.
|
|
99
|
+
* @param {string} longKey
|
|
100
|
+
* @param {object} obj
|
|
101
|
+
* @returns The final nested object referred to by initial longKey.
|
|
102
|
+
*/
|
|
96
103
|
function discoverValue(longKey, obj) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
104
|
+
if (!longKey) return obj; // end of object recursion depth when no more key levels
|
|
105
|
+
const matches = longKey.match(VALUE_REGEX);
|
|
106
|
+
// if 'longKey' was not undefined, that is it at least had 1 level left, then the regexp would've at least matched 1st group
|
|
107
|
+
const key = matches[1];
|
|
108
|
+
const remaining = matches[3];
|
|
103
109
|
return discoverValue(remaining, obj[key]);
|
|
104
110
|
}
|
|
105
111
|
function discoverParent(key) {
|
|
@@ -25,13 +25,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
25
25
|
static featureName = _constants.FEATURE_NAME;
|
|
26
26
|
constructor(agentIdentifier, aggregator) {
|
|
27
27
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
28
|
+
const agentInit = (0, _config.getConfiguration)(agentIdentifier);
|
|
29
|
+
const allAjaxIsEnabled = agentInit.ajax.enabled !== false;
|
|
30
|
+
(0, _registerHandler.registerHandler)('xhr', storeXhr, this.featureName, this.ee);
|
|
31
|
+
if (!allAjaxIsEnabled) {
|
|
32
|
+
(0, _drain.drain)(this.agentIdentifier, this.featureName);
|
|
33
|
+
return; // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const denyList = (0, _config.getRuntime)(agentIdentifier).denyList;
|
|
37
|
+
(0, _denyList.setDenyList)(denyList);
|
|
28
38
|
let ajaxEvents = [];
|
|
29
39
|
let spaAjaxEvents = {};
|
|
30
40
|
let sentAjaxEvents = [];
|
|
31
|
-
let scheduler;
|
|
32
41
|
const ee = this.ee;
|
|
33
|
-
const harvestTimeSeconds =
|
|
34
|
-
const MAX_PAYLOAD_SIZE =
|
|
42
|
+
const harvestTimeSeconds = agentInit.ajax.harvestTimeSeconds || 10;
|
|
43
|
+
const MAX_PAYLOAD_SIZE = agentInit.ajax.maxPayloadSize || 1000000;
|
|
35
44
|
|
|
36
45
|
// Exposes these methods to browser test files -- future TO DO: can be removed once these fns are extracted from the constructor into class func
|
|
37
46
|
this.storeXhr = storeXhr;
|
|
@@ -48,24 +57,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
48
57
|
delete spaAjaxEvents[interaction.id];
|
|
49
58
|
});
|
|
50
59
|
ee.on('interactionDiscarded', interaction => {
|
|
51
|
-
if (!spaAjaxEvents[interaction.id]
|
|
60
|
+
if (!spaAjaxEvents[interaction.id]) return;
|
|
52
61
|
spaAjaxEvents[interaction.id].forEach(function (item) {
|
|
53
62
|
// move it from the spaAjaxEvents buffer to the ajaxEvents buffer for harvesting here
|
|
54
63
|
ajaxEvents.push(item);
|
|
55
64
|
});
|
|
56
65
|
delete spaAjaxEvents[interaction.id];
|
|
57
66
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
68
|
-
}
|
|
67
|
+
const scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
68
|
+
onFinished: onEventsHarvestFinished,
|
|
69
|
+
getPayload: prepareHarvest
|
|
70
|
+
}, this);
|
|
71
|
+
ee.on("drain-".concat(this.featureName), () => {
|
|
72
|
+
scheduler.startTimer(harvestTimeSeconds);
|
|
73
|
+
});
|
|
74
|
+
(0, _drain.drain)(this.agentIdentifier, this.featureName);
|
|
75
|
+
return;
|
|
69
76
|
function storeXhr(params, metrics, startTime, endTime, type) {
|
|
70
77
|
metrics.time = startTime;
|
|
71
78
|
|
|
@@ -80,9 +87,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
80
87
|
|
|
81
88
|
// store as metric
|
|
82
89
|
aggregator.store('xhr', hash, params, metrics);
|
|
83
|
-
if (!allAjaxIsEnabled
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
90
|
+
if (!allAjaxIsEnabled) return;
|
|
86
91
|
if (!(0, _denyList.shouldCollectEvent)(params)) {
|
|
87
92
|
if (params.hostname === (0, _config.getInfo)(agentIdentifier).errorBeacon) {
|
|
88
93
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, _features.FEATURE_NAMES.metrics, ee);
|
|
@@ -164,7 +169,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
164
169
|
return tooBig ? getPayload(events, maxPayloadSize, ++chunks) : payload;
|
|
165
170
|
}
|
|
166
171
|
function onEventsHarvestFinished(result) {
|
|
167
|
-
if (result.retry && sentAjaxEvents.length > 0
|
|
172
|
+
if (result.retry && sentAjaxEvents.length > 0) {
|
|
168
173
|
ajaxEvents.unshift(...sentAjaxEvents);
|
|
169
174
|
sentAjaxEvents = [];
|
|
170
175
|
}
|
|
@@ -213,14 +218,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
213
218
|
return this.payload.length * 2 > maxPayloadSize;
|
|
214
219
|
};
|
|
215
220
|
}
|
|
216
|
-
function allAjaxIsEnabled() {
|
|
217
|
-
var enabled = (0, _config.getConfigurationValue)(agentIdentifier, 'ajax.enabled');
|
|
218
|
-
if (enabled === false) {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
(0, _drain.drain)(this.agentIdentifier, this.featureName);
|
|
224
221
|
}
|
|
225
222
|
}
|
|
226
223
|
exports.Aggregate = Aggregate;
|
|
@@ -229,7 +229,7 @@ function computeStackTraceBySourceAndLine(ex) {
|
|
|
229
229
|
mode: 'sourceline',
|
|
230
230
|
name: className,
|
|
231
231
|
message: ex.message,
|
|
232
|
-
stackString:
|
|
232
|
+
stackString: className + ': ' + ex.message + '\n in evaluated code',
|
|
233
233
|
frames: [{
|
|
234
234
|
func: 'evaluated code'
|
|
235
235
|
}]
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.FEATURE_NAME = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
const FEATURE_NAME = _features.FEATURE_NAMES.jserrors;
|
|
9
|
-
exports.FEATURE_NAME = FEATURE_NAME;
|
|
10
|
-
const NR_ERR_PROP = 'nr@seenError';
|
|
11
|
-
exports.NR_ERR_PROP = NR_ERR_PROP;
|
|
9
|
+
exports.FEATURE_NAME = FEATURE_NAME;
|
|
@@ -6,16 +6,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.Instrument = void 0;
|
|
7
7
|
var _handle = require("../../../common/event-emitter/handle");
|
|
8
8
|
var _now = require("../../../common/timing/now");
|
|
9
|
-
var _getOrSet = require("../../../common/util/get-or-set");
|
|
10
|
-
var _wrap = require("../../../common/wrap");
|
|
11
|
-
require("./debug");
|
|
12
9
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
13
10
|
var _constants = require("../constants");
|
|
14
11
|
var _features = require("../../../loaders/features/features");
|
|
15
12
|
var _runtime = require("../../../common/constants/runtime");
|
|
16
13
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
17
|
-
var _config = require("../../../common/config/config");
|
|
18
14
|
var _stringify = require("../../../common/util/stringify");
|
|
15
|
+
var _uncaughtError = require("./uncaught-error");
|
|
19
16
|
/*
|
|
20
17
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
21
18
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -23,51 +20,44 @@ var _stringify = require("../../../common/util/stringify");
|
|
|
23
20
|
|
|
24
21
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
25
22
|
static featureName = _constants.FEATURE_NAME;
|
|
23
|
+
#seenErrors = new Set();
|
|
26
24
|
constructor(agentIdentifier, aggregator) {
|
|
27
25
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
28
26
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
|
|
29
|
-
// skipNext counter to keep track of uncaught
|
|
30
|
-
// errors that will be the same as caught errors.
|
|
31
|
-
this.skipNext = 0;
|
|
32
27
|
try {
|
|
33
28
|
// this try-catch can be removed when IE11 is completely unsupported & gone
|
|
34
29
|
this.removeOnAbort = new AbortController();
|
|
35
30
|
} catch (e) {}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(0, _getOrSet.getOrSet)(err, _constants.NR_ERR_PROP, function getVal() {
|
|
43
|
-
return true;
|
|
44
|
-
});
|
|
45
|
-
this.thrown = true;
|
|
46
|
-
(0, _handle.handle)('err', [err, (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, thisInstrument.ee);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
thisInstrument.ee.on('fn-end', function () {
|
|
50
|
-
if (!thisInstrument.abortHandler) return;
|
|
51
|
-
if (!this.thrown && thisInstrument.skipNext > 0) thisInstrument.skipNext -= 1;
|
|
31
|
+
|
|
32
|
+
// Capture function errors early in case the spa feature is loaded
|
|
33
|
+
this.ee.on('fn-err', (args, obj, error) => {
|
|
34
|
+
if (!this.abortHandler || this.#seenErrors.has(error)) return;
|
|
35
|
+
this.#seenErrors.add(error);
|
|
36
|
+
(0, _handle.handle)('err', [this.#castError(error), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
52
37
|
});
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
this.ee.on('internal-error', error => {
|
|
39
|
+
if (!this.abortHandler) return;
|
|
40
|
+
(0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
55
41
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
_runtime.globalScope.onerror = this.onerrorHandler.bind(this);
|
|
60
|
-
_runtime.globalScope.addEventListener('unhandledrejection', e => {
|
|
61
|
-
/** rejections can contain data of any type -- this is an effort to keep the message human readable */
|
|
62
|
-
const err = castReasonToError(e.reason);
|
|
63
|
-
(0, _handle.handle)('err', [err, (0, _now.now)(), false, {
|
|
42
|
+
_runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
|
|
43
|
+
if (!this.abortHandler) return;
|
|
44
|
+
(0, _handle.handle)('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), (0, _now.now)(), false, {
|
|
64
45
|
unhandledPromiseRejection: 1
|
|
65
46
|
}], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
66
47
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
67
|
-
(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
48
|
+
_runtime.globalScope.addEventListener('error', errorEvent => {
|
|
49
|
+
if (!this.abortHandler) return;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* If the spa feature is loaded, errors may already have been captured in the `fn-err` listener above.
|
|
53
|
+
* This ensures those errors are not captured twice.
|
|
54
|
+
*/
|
|
55
|
+
if (this.#seenErrors.has(errorEvent.error)) {
|
|
56
|
+
this.#seenErrors.delete(errorEvent.error);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
(0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
60
|
+
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
71
61
|
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
72
62
|
this.importAggregator();
|
|
73
63
|
}
|
|
@@ -75,66 +65,67 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
75
65
|
/** Restoration and resource release tasks to be done if JS error loader is being aborted. Unwind changes to globals. */
|
|
76
66
|
#abort() {
|
|
77
67
|
this.removeOnAbort?.abort();
|
|
68
|
+
this.#seenErrors.clear();
|
|
78
69
|
this.abortHandler = undefined; // weakly allow this abort op to run only once
|
|
79
70
|
}
|
|
80
71
|
|
|
81
72
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* @param {
|
|
85
|
-
* @
|
|
86
|
-
* @param {number} lineno
|
|
87
|
-
* @param {number} column
|
|
88
|
-
* @param {Error | *} errorObj
|
|
89
|
-
* @returns
|
|
73
|
+
* Any value can be used with the `throw` keyword. This function ensures that the value is
|
|
74
|
+
* either a proper Error instance or attempts to convert it to an UncaughtError instance.
|
|
75
|
+
* @param {any} error The value thrown
|
|
76
|
+
* @returns {Error|UncaughtError} The converted error instance
|
|
90
77
|
*/
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
#castError(error) {
|
|
79
|
+
if (error instanceof Error) {
|
|
80
|
+
return error;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The thrown value may contain a message property. If it does, try to treat the thrown
|
|
85
|
+
* value as an Error-like object.
|
|
86
|
+
*/
|
|
87
|
+
if (typeof error?.message !== 'undefined') {
|
|
88
|
+
return new _uncaughtError.UncaughtError(error.message, error.filename || error.sourceURL, error.lineno || error.line, error.colno || error.col);
|
|
89
|
+
}
|
|
90
|
+
return new _uncaughtError.UncaughtError(typeof error === 'string' ? error : (0, _stringify.stringify)(error));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Attempts to convert a PromiseRejectionEvent object to an Error object
|
|
95
|
+
* @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
|
|
96
|
+
* @returns {Error} An Error object with the message as the casted reason
|
|
97
|
+
*/
|
|
98
|
+
#castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
99
|
+
let prefix = 'Unhandled Promise Rejection: ';
|
|
100
|
+
if (promiseRejectionEvent?.reason instanceof Error) {
|
|
96
101
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message;
|
|
103
|
+
return promiseRejectionEvent.reason;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return promiseRejectionEvent.reason;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
|
|
108
|
+
if (typeof promiseRejectionEvent.reason === 'undefined') return new _uncaughtError.UncaughtError(prefix);
|
|
109
|
+
const error = this.#castError(promiseRejectionEvent.reason);
|
|
110
|
+
error.message = prefix + error.message;
|
|
111
|
+
return error;
|
|
103
112
|
}
|
|
104
|
-
}
|
|
105
113
|
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.message = message || 'Uncaught error with no additional information';
|
|
115
|
-
this.sourceURL = filename;
|
|
116
|
-
this.line = lineno;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Attempts to cast an unhandledPromiseRejection reason (reject(...)) to an Error object
|
|
121
|
-
* @param {*} reason - The reason property from an unhandled promise rejection
|
|
122
|
-
* @returns {Error} - An Error object with the message as the casted reason
|
|
123
|
-
*/
|
|
124
|
-
function castReasonToError(reason) {
|
|
125
|
-
let prefix = 'Unhandled Promise Rejection: ';
|
|
126
|
-
if (reason instanceof Error) {
|
|
127
|
-
try {
|
|
128
|
-
reason.message = prefix + reason.message;
|
|
129
|
-
return reason;
|
|
130
|
-
} catch (e) {
|
|
131
|
-
return reason;
|
|
114
|
+
/**
|
|
115
|
+
* Attempts to convert an ErrorEvent object to an Error object
|
|
116
|
+
* @param {ErrorEvent} errorEvent The error event
|
|
117
|
+
* @returns {Error|UncaughtError} The error event converted to an Error object
|
|
118
|
+
*/
|
|
119
|
+
#castErrorEvent(errorEvent) {
|
|
120
|
+
if (errorEvent.error instanceof Error) {
|
|
121
|
+
return errorEvent.error;
|
|
132
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Older browsers do not contain the `error` property on the ErrorEvent instance.
|
|
126
|
+
* https://caniuse.com/mdn-api_errorevent_error
|
|
127
|
+
*/
|
|
128
|
+
return new _uncaughtError.UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno);
|
|
133
129
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return new Error(prefix + (0, _stringify.stringify)(reason));
|
|
137
|
-
} catch (err) {
|
|
138
|
-
return new Error(prefix);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
130
|
+
}
|
|
131
|
+
exports.Instrument = Instrument;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UncaughtError = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Represents an uncaught non Error type error. This class does
|
|
9
|
+
* not extend the Error class to prevent an invalid stack trace
|
|
10
|
+
* from being created. Use this class to cast thrown errors that
|
|
11
|
+
* do not use the Error class (strings, etc) to an object.
|
|
12
|
+
*/
|
|
13
|
+
class UncaughtError {
|
|
14
|
+
constructor(message, filename, lineno, colno) {
|
|
15
|
+
this.name = 'UncaughtError';
|
|
16
|
+
this.message = message;
|
|
17
|
+
this.sourceURL = filename;
|
|
18
|
+
this.line = lineno;
|
|
19
|
+
this.column = colno;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.UncaughtError = UncaughtError;
|
|
@@ -154,6 +154,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
154
154
|
// Capture metrics for size of custom attributes
|
|
155
155
|
const jsAttributes = (0, _stringify.stringify)(info.jsAttributes);
|
|
156
156
|
this.storeSupportabilityMetrics('PageSession/Feature/CustomData/Bytes', jsAttributes === '{}' ? 0 : jsAttributes.length);
|
|
157
|
+
|
|
158
|
+
// Capture metrics for performance markers and measures
|
|
159
|
+
if (typeof performance !== 'undefined') {
|
|
160
|
+
const markers = performance.getEntriesByType('mark');
|
|
161
|
+
const measures = performance.getEntriesByType('measure');
|
|
162
|
+
this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
|
|
163
|
+
this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
|
|
164
|
+
}
|
|
157
165
|
} catch (e) {
|
|
158
166
|
// do nothing
|
|
159
167
|
}
|