@newrelic/browser-agent 1.274.0 → 1.276.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/init.js +44 -8
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/harvest/harvest.js +21 -0
- package/dist/cjs/common/util/submit-data.js +42 -5
- package/dist/cjs/common/wrap/wrap-events.js +1 -1
- package/dist/cjs/common/wrap/wrap-logger.js +5 -1
- package/dist/cjs/common/wrap/wrap-xhr.js +1 -0
- package/dist/cjs/features/ajax/instrument/index.js +1 -1
- package/dist/cjs/features/generic_events/aggregate/index.js +77 -14
- package/dist/cjs/features/generic_events/constants.js +7 -2
- package/dist/cjs/features/generic_events/instrument/index.js +20 -8
- package/dist/cjs/features/jserrors/aggregate/index.js +3 -2
- package/dist/cjs/features/jserrors/aggregate/internal-errors.js +2 -2
- package/dist/cjs/features/jserrors/instrument/index.js +2 -2
- package/dist/cjs/features/metrics/aggregate/index.js +1 -35
- package/dist/esm/common/config/init.js +39 -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/harvest/harvest.js +21 -0
- package/dist/esm/common/util/submit-data.js +41 -5
- package/dist/esm/common/wrap/wrap-events.js +1 -1
- package/dist/esm/common/wrap/wrap-logger.js +5 -2
- package/dist/esm/common/wrap/wrap-xhr.js +1 -0
- package/dist/esm/features/ajax/instrument/index.js +1 -1
- package/dist/esm/features/generic_events/aggregate/index.js +78 -15
- package/dist/esm/features/generic_events/constants.js +6 -1
- package/dist/esm/features/generic_events/instrument/index.js +21 -9
- package/dist/esm/features/jserrors/aggregate/index.js +3 -2
- package/dist/esm/features/jserrors/aggregate/internal-errors.js +2 -2
- package/dist/esm/features/jserrors/instrument/index.js +2 -2
- package/dist/esm/features/metrics/aggregate/index.js +1 -35
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/util/submit-data.d.ts +18 -1
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +5 -0
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/internal-errors.d.ts +1 -1
- package/dist/types/features/jserrors/aggregate/internal-errors.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +0 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/package.json +13 -1
- package/src/common/config/init.js +21 -3
- package/src/common/harvest/harvest.js +19 -0
- package/src/common/util/submit-data.js +37 -4
- package/src/common/wrap/wrap-events.js +1 -1
- package/src/common/wrap/wrap-logger.js +7 -2
- package/src/common/wrap/wrap-xhr.js +2 -0
- package/src/features/ajax/instrument/index.js +1 -1
- package/src/features/generic_events/aggregate/index.js +79 -16
- package/src/features/generic_events/constants.js +6 -0
- package/src/features/generic_events/instrument/index.js +21 -10
- package/src/features/jserrors/aggregate/index.js +3 -2
- package/src/features/jserrors/aggregate/internal-errors.js +2 -2
- package/src/features/jserrors/instrument/index.js +2 -2
- package/src/features/metrics/aggregate/index.js +1 -33
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FEATURE_FLAGS } from '../../features/generic_events/constants';
|
|
1
2
|
import { LOG_LEVELS } from '../../features/logging/constants';
|
|
2
3
|
import { isValidSelector } from '../dom/query-selector';
|
|
3
4
|
import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS } from '../session/constants';
|
|
@@ -7,6 +8,12 @@ import { getModeledObject } from './configurable';
|
|
|
7
8
|
const nrMask = '[data-nr-mask]';
|
|
8
9
|
const model = () => {
|
|
9
10
|
const hiddenState = {
|
|
11
|
+
feature_flags: [],
|
|
12
|
+
experimental: {
|
|
13
|
+
marks: false,
|
|
14
|
+
measures: false,
|
|
15
|
+
resources: false
|
|
16
|
+
},
|
|
10
17
|
mask_selector: '*',
|
|
11
18
|
block_selector: '[data-nr-block]',
|
|
12
19
|
mask_input_options: {
|
|
@@ -44,7 +51,12 @@ const model = () => {
|
|
|
44
51
|
cors_use_tracecontext_headers: undefined,
|
|
45
52
|
allowed_origins: undefined
|
|
46
53
|
},
|
|
47
|
-
feature_flags
|
|
54
|
+
get feature_flags() {
|
|
55
|
+
return hiddenState.feature_flags;
|
|
56
|
+
},
|
|
57
|
+
set feature_flags(val) {
|
|
58
|
+
hiddenState.feature_flags = val;
|
|
59
|
+
},
|
|
48
60
|
generic_events: {
|
|
49
61
|
enabled: true,
|
|
50
62
|
harvestTimeSeconds: 30,
|
|
@@ -82,8 +94,32 @@ const model = () => {
|
|
|
82
94
|
autoStart: true
|
|
83
95
|
},
|
|
84
96
|
performance: {
|
|
85
|
-
capture_marks
|
|
86
|
-
|
|
97
|
+
get capture_marks() {
|
|
98
|
+
return hiddenState.feature_flags.includes(FEATURE_FLAGS.MARKS) || hiddenState.experimental.marks;
|
|
99
|
+
},
|
|
100
|
+
set capture_marks(val) {
|
|
101
|
+
hiddenState.experimental.marks = val;
|
|
102
|
+
},
|
|
103
|
+
get capture_measures() {
|
|
104
|
+
return hiddenState.feature_flags.includes(FEATURE_FLAGS.MEASURES) || hiddenState.experimental.measures;
|
|
105
|
+
},
|
|
106
|
+
set capture_measures(val) {
|
|
107
|
+
hiddenState.experimental.measures = val;
|
|
108
|
+
},
|
|
109
|
+
resources: {
|
|
110
|
+
// whether to run this subfeature or not in the generic_events feature. false by default through experimental phase, but flipped to true once GA'd
|
|
111
|
+
get enabled() {
|
|
112
|
+
return hiddenState.feature_flags.includes(FEATURE_FLAGS.RESOURCES) || hiddenState.experimental.resources;
|
|
113
|
+
},
|
|
114
|
+
set enabled(val) {
|
|
115
|
+
hiddenState.experimental.resources = val;
|
|
116
|
+
},
|
|
117
|
+
asset_types: [],
|
|
118
|
+
// MDN types to collect, empty array will collect all types
|
|
119
|
+
first_party_domains: [],
|
|
120
|
+
// when included, will decorate the resource as first party if matching
|
|
121
|
+
ignore_newrelic: true // ignore capturing internal agent scripts and harvest calls
|
|
122
|
+
}
|
|
87
123
|
},
|
|
88
124
|
privacy: {
|
|
89
125
|
cookies_enabled: true
|
|
@@ -166,6 +166,27 @@ export class Harvest extends SharedContext {
|
|
|
166
166
|
}
|
|
167
167
|
cbFinished(cbResult);
|
|
168
168
|
}, eventListenerOpts(false));
|
|
169
|
+
} else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
|
|
170
|
+
const harvestScope = this;
|
|
171
|
+
result.then(async function (response) {
|
|
172
|
+
const status = response.status;
|
|
173
|
+
const cbResult = {
|
|
174
|
+
sent: true,
|
|
175
|
+
status,
|
|
176
|
+
fullUrl,
|
|
177
|
+
fetchResponse: response
|
|
178
|
+
};
|
|
179
|
+
if (response.status === 429) {
|
|
180
|
+
cbResult.retry = true;
|
|
181
|
+
cbResult.delay = harvestScope.tooManyRequestsDelay;
|
|
182
|
+
} else if (status === 408 || status === 500 || status === 503) {
|
|
183
|
+
cbResult.retry = true;
|
|
184
|
+
}
|
|
185
|
+
if (opts.needResponse) {
|
|
186
|
+
cbResult.responseText = await response.text();
|
|
187
|
+
}
|
|
188
|
+
cbFinished(cbResult);
|
|
189
|
+
});
|
|
169
190
|
}
|
|
170
191
|
return result;
|
|
171
192
|
}
|
|
@@ -19,11 +19,47 @@ import { isBrowserScope } from '../constants/runtime';
|
|
|
19
19
|
export function getSubmitMethod({
|
|
20
20
|
isFinalHarvest = false
|
|
21
21
|
} = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
if (isFinalHarvest && isBrowserScope) {
|
|
23
|
+
// Use sendBeacon for final harvest
|
|
24
|
+
return beacon;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If not final harvest, or not browserScope, use XHR post if available
|
|
28
|
+
if (typeof XMLHttpRequest !== 'undefined') {
|
|
29
|
+
return xhr;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Fallback for web workers where XMLHttpRequest is not available
|
|
33
|
+
return xhrFetch;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
* @param url
|
|
39
|
+
* @param body
|
|
40
|
+
* @param method
|
|
41
|
+
* @param headers
|
|
42
|
+
* @returns {Promise<Response>}
|
|
43
|
+
*/
|
|
44
|
+
export function xhrFetch({
|
|
45
|
+
url,
|
|
46
|
+
body = null,
|
|
47
|
+
method = 'POST',
|
|
48
|
+
headers = [{
|
|
49
|
+
key: 'content-type',
|
|
50
|
+
value: 'text/plain'
|
|
51
|
+
}]
|
|
52
|
+
}) {
|
|
53
|
+
const objHeaders = {};
|
|
54
|
+
for (const header of headers) {
|
|
55
|
+
objHeaders[header.key] = header.value;
|
|
56
|
+
}
|
|
57
|
+
return fetch(url, {
|
|
58
|
+
headers: objHeaders,
|
|
59
|
+
method,
|
|
60
|
+
body,
|
|
61
|
+
credentials: 'include'
|
|
62
|
+
});
|
|
27
63
|
}
|
|
28
64
|
|
|
29
65
|
/**
|
|
@@ -36,8 +36,8 @@ export function wrapEvents(sharedEE) {
|
|
|
36
36
|
// Guard against instrumenting environments w/o necessary features
|
|
37
37
|
if ('getPrototypeOf' in Object) {
|
|
38
38
|
if (isBrowserScope) findEventListenerProtoAndCb(document, wrapNode);
|
|
39
|
+
if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode);
|
|
39
40
|
findEventListenerProtoAndCb(globalScope, wrapNode);
|
|
40
|
-
findEventListenerProtoAndCb(XHR.prototype, wrapNode);
|
|
41
41
|
}
|
|
42
42
|
ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
|
|
43
43
|
var originalListener = args[1];
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { EventContext } from '../event-emitter/event-context';
|
|
12
12
|
import { warn } from '../util/console';
|
|
13
|
-
import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
13
|
+
import { flag, createWrapperWithEmitter as wfn } from './wrap-function';
|
|
14
|
+
const contextMap = new Map();
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
|
|
@@ -32,9 +33,11 @@ export function wrapLogger(sharedEE, parent, loggerFn, context) {
|
|
|
32
33
|
const ctx = new EventContext(contextId);
|
|
33
34
|
ctx.level = context.level;
|
|
34
35
|
ctx.customAttributes = context.customAttributes;
|
|
36
|
+
const contextLookupKey = parent[loggerFn]?.[flag] || parent[loggerFn];
|
|
37
|
+
contextMap.set(contextLookupKey, ctx);
|
|
35
38
|
|
|
36
39
|
/** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
|
|
37
|
-
wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-',
|
|
40
|
+
wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
|
|
38
41
|
return ee;
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -26,6 +26,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
|
|
|
26
26
|
export function wrapXhr(sharedEE) {
|
|
27
27
|
var baseEE = sharedEE || contextualEE;
|
|
28
28
|
const ee = scopedEE(baseEE);
|
|
29
|
+
if (typeof globalScope.XMLHttpRequest === 'undefined') return ee;
|
|
29
30
|
|
|
30
31
|
// Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
|
|
31
32
|
// then we increment the count to track # of feats using this at runtime.
|
|
@@ -338,7 +338,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
|
|
|
338
338
|
if (hasUndefinedHostname(params)) return; // don't bother with XHR of url with no hostname
|
|
339
339
|
|
|
340
340
|
metrics.duration = now() - this.startTime;
|
|
341
|
-
if (!this.
|
|
341
|
+
if (!this.loadCaptureCalled && xhr.readyState === 4) {
|
|
342
342
|
captureXhrData(this, xhr);
|
|
343
343
|
} else if (params.status == null) {
|
|
344
344
|
params.status = 0;
|
|
@@ -6,17 +6,17 @@ import { stringify } from '../../../common/util/stringify';
|
|
|
6
6
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
7
7
|
import { cleanURL } from '../../../common/url/clean-url';
|
|
8
8
|
import { FEATURE_NAME } from '../constants';
|
|
9
|
-
import { initialLocation, isBrowserScope } from '../../../common/constants/runtime';
|
|
9
|
+
import { globalScope, initialLocation, isBrowserScope } from '../../../common/constants/runtime';
|
|
10
10
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
11
11
|
import { warn } from '../../../common/util/console';
|
|
12
12
|
import { now } from '../../../common/timing/now';
|
|
13
13
|
import { registerHandler } from '../../../common/event-emitter/register-handler';
|
|
14
14
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
15
15
|
import { applyFnToProps } from '../../../common/util/traverse';
|
|
16
|
-
import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants';
|
|
17
16
|
import { FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
|
|
18
17
|
import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
|
|
19
18
|
import { isIFrameWindow } from '../../../common/dom/iframe';
|
|
19
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
20
20
|
export class Aggregate extends AggregateBase {
|
|
21
21
|
static featureName = FEATURE_NAME;
|
|
22
22
|
constructor(agentRef) {
|
|
@@ -30,6 +30,7 @@ export class Aggregate extends AggregateBase {
|
|
|
30
30
|
this.deregisterDrain();
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
+
this.trackSupportabilityMetrics();
|
|
33
34
|
if (agentRef.init.page_action.enabled) {
|
|
34
35
|
registerHandler('api-addPageAction', (timestamp, name, attributes) => {
|
|
35
36
|
this.addEvent({
|
|
@@ -71,19 +72,28 @@ export class Aggregate extends AggregateBase {
|
|
|
71
72
|
...(isIFrameWindow(window) && {
|
|
72
73
|
iframe: true
|
|
73
74
|
}),
|
|
74
|
-
...(
|
|
75
|
+
...(canTrustTargetAttribute('id') && {
|
|
75
76
|
targetId: target.id
|
|
76
77
|
}),
|
|
77
|
-
...(
|
|
78
|
+
...(canTrustTargetAttribute('tagName') && {
|
|
78
79
|
targetTag: target.tagName
|
|
79
80
|
}),
|
|
80
|
-
...(
|
|
81
|
+
...(canTrustTargetAttribute('type') && {
|
|
81
82
|
targetType: target.type
|
|
82
83
|
}),
|
|
83
|
-
...(
|
|
84
|
+
...(canTrustTargetAttribute('className') && {
|
|
84
85
|
targetClass: target.className
|
|
85
86
|
})
|
|
86
87
|
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
|
|
91
|
+
* @param {string} attribute The attribute to check for on the target element
|
|
92
|
+
* @returns {boolean} Whether the target element has the attribute and can be trusted
|
|
93
|
+
*/
|
|
94
|
+
function canTrustTargetAttribute(attribute) {
|
|
95
|
+
return !!(aggregatedUserAction.selectorPath !== 'window' && aggregatedUserAction.selectorPath !== 'document' && target instanceof HTMLElement && target?.[attribute]);
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
} catch (e) {
|
|
89
99
|
// do nothing for now
|
|
@@ -110,10 +120,11 @@ export class Aggregate extends AggregateBase {
|
|
|
110
120
|
const observer = new PerformanceObserver(list => {
|
|
111
121
|
list.getEntries().forEach(entry => {
|
|
112
122
|
try {
|
|
123
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
|
|
113
124
|
this.addEvent({
|
|
114
125
|
eventType: 'BrowserPerformance',
|
|
115
126
|
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
|
|
116
|
-
entryName: entry.name,
|
|
127
|
+
entryName: cleanURL(entry.name),
|
|
117
128
|
entryDuration: entry.duration,
|
|
118
129
|
entryType: type,
|
|
119
130
|
...(entry.detail && {
|
|
@@ -133,6 +144,47 @@ export class Aggregate extends AggregateBase {
|
|
|
133
144
|
// Something failed in our set up, likely the browser does not support PO's... do nothing
|
|
134
145
|
}
|
|
135
146
|
}
|
|
147
|
+
if (isBrowserScope && agentRef.init.performance.resources.enabled) {
|
|
148
|
+
registerHandler('browserPerformance.resource', entry => {
|
|
149
|
+
try {
|
|
150
|
+
// convert the entry to a plain object and separate the name and duration from the object
|
|
151
|
+
// you need to do this to be able to spread it into the addEvent call later, and name and duration
|
|
152
|
+
// would be duplicative of entryName and entryDuration and are protected keys in NR1
|
|
153
|
+
const {
|
|
154
|
+
name,
|
|
155
|
+
duration,
|
|
156
|
+
...entryObject
|
|
157
|
+
} = entry.toJSON();
|
|
158
|
+
let firstParty = false;
|
|
159
|
+
try {
|
|
160
|
+
const entryDomain = new URL(name).hostname;
|
|
161
|
+
const isNr = entryDomain.includes('newrelic.com') || entryDomain.includes('nr-data.net') || entryDomain.includes('nr-local.net');
|
|
162
|
+
/** decide if we should ignore nr-specific assets */
|
|
163
|
+
if (this.agentRef.init.performance.resources.ignore_newrelic && isNr) return;
|
|
164
|
+
/** decide if we should ignore the asset type (empty means allow everything, which is the default) */
|
|
165
|
+
if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
|
|
166
|
+
/** decide if the entryDomain is a first party domain */
|
|
167
|
+
firstParty = entryDomain === globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
|
|
168
|
+
if (firstParty) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
|
|
169
|
+
if (isNr) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
|
|
170
|
+
} catch (err) {
|
|
171
|
+
// couldnt parse the URL, so firstParty will just default to false
|
|
172
|
+
}
|
|
173
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
|
|
174
|
+
const event = {
|
|
175
|
+
...entryObject,
|
|
176
|
+
eventType: 'BrowserPerformance',
|
|
177
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entryObject.startTime)),
|
|
178
|
+
entryName: name,
|
|
179
|
+
entryDuration: duration,
|
|
180
|
+
firstParty
|
|
181
|
+
};
|
|
182
|
+
this.addEvent(event);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
this.ee.emit('internal-error', [err, 'GenericEvents-Resource']);
|
|
185
|
+
}
|
|
186
|
+
}, this.featureName, this.ee);
|
|
187
|
+
}
|
|
136
188
|
this.harvestScheduler = new HarvestScheduler(FEATURE_TO_ENDPOINT[this.featureName], {
|
|
137
189
|
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
138
190
|
onUnload: () => addUserAction?.(this.userActionAggregator.aggregationEvent)
|
|
@@ -181,8 +233,16 @@ export class Aggregate extends AggregateBase {
|
|
|
181
233
|
/** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
|
|
182
234
|
...obj
|
|
183
235
|
};
|
|
184
|
-
this.events.add(eventAttributes);
|
|
185
|
-
this.
|
|
236
|
+
const addedEvent = this.events.add(eventAttributes);
|
|
237
|
+
if (!addedEvent && !this.events.isEmpty()) {
|
|
238
|
+
/** could not add the event because it pushed the buffer over the limit
|
|
239
|
+
* so we harvest early, and try to add it again now that the buffer is cleared
|
|
240
|
+
* if it fails again, we do nothing
|
|
241
|
+
*/
|
|
242
|
+
this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
|
|
243
|
+
this.harvestScheduler.runHarvest();
|
|
244
|
+
this.events.add(eventAttributes);
|
|
245
|
+
}
|
|
186
246
|
}
|
|
187
247
|
serializer(eventBuffer) {
|
|
188
248
|
return applyFnToProps({
|
|
@@ -195,11 +255,14 @@ export class Aggregate extends AggregateBase {
|
|
|
195
255
|
at: this.agentRef.info.atts
|
|
196
256
|
};
|
|
197
257
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
258
|
+
trackSupportabilityMetrics() {
|
|
259
|
+
/** track usage SMs to improve these experimental features */
|
|
260
|
+
const configPerfTag = 'Config/Performance/';
|
|
261
|
+
if (this.agentRef.init.performance.capture_marks) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
|
|
262
|
+
if (this.agentRef.init.performance.capture_measures) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
|
|
263
|
+
if (this.agentRef.init.performance.resources.enabled) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
|
|
264
|
+
if (this.agentRef.init.performance.resources.asset_types?.length !== 0) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
|
|
265
|
+
if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
|
|
266
|
+
if (this.agentRef.init.performance.resources.ignore_newrelic === false) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
|
|
204
267
|
}
|
|
205
268
|
}
|
|
@@ -5,4 +5,9 @@ export const MAX_PAYLOAD_SIZE = 1000000;
|
|
|
5
5
|
export const OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'];
|
|
6
6
|
export const OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
|
|
7
7
|
export const RAGE_CLICK_THRESHOLD_EVENTS = 4;
|
|
8
|
-
export const RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
8
|
+
export const RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
9
|
+
export const FEATURE_FLAGS = {
|
|
10
|
+
MARKS: 'experimental.marks',
|
|
11
|
+
MEASURES: 'experimental.measures',
|
|
12
|
+
RESOURCES: 'experimental.resources'
|
|
13
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { isBrowserScope } from '../../../common/constants/runtime';
|
|
5
|
+
import { globalScope, isBrowserScope } from '../../../common/constants/runtime';
|
|
6
6
|
import { handle } from '../../../common/event-emitter/handle';
|
|
7
7
|
import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts';
|
|
8
8
|
import { InstrumentBase } from '../../utils/instrument-base';
|
|
@@ -11,14 +11,26 @@ export class Instrument extends InstrumentBase {
|
|
|
11
11
|
static featureName = FEATURE_NAME;
|
|
12
12
|
constructor(agentRef, auto = true) {
|
|
13
13
|
super(agentRef, FEATURE_NAME, auto);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
/** config values that gate whether the generic events aggregator should be imported at all */
|
|
15
|
+
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled, agentRef.init.performance.resources.enabled];
|
|
16
|
+
if (isBrowserScope) {
|
|
17
|
+
if (agentRef.init.user_actions.enabled) {
|
|
18
|
+
OBSERVED_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee), true));
|
|
19
|
+
OBSERVED_WINDOW_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee))
|
|
20
|
+
// Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
if (agentRef.init.performance.resources.enabled && globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
|
|
24
|
+
const observer = new PerformanceObserver(list => {
|
|
25
|
+
list.getEntries().forEach(entry => {
|
|
26
|
+
handle('browserPerformance.resource', [entry], undefined, this.featureName, this.ee);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
observer.observe({
|
|
30
|
+
type: 'resource',
|
|
31
|
+
buffered: true
|
|
32
|
+
});
|
|
33
|
+
}
|
|
22
34
|
}
|
|
23
35
|
|
|
24
36
|
/** If any of the sources are active, import the aggregator. otherwise deregister */
|
|
@@ -107,9 +107,10 @@ export class Aggregate extends AggregateBase {
|
|
|
107
107
|
* @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
|
|
108
108
|
* @param {object=} customAttributes any custom attributes to be included in the error payload
|
|
109
109
|
* @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
|
|
110
|
+
* @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
|
|
110
111
|
* @returns
|
|
111
112
|
*/
|
|
112
|
-
storeError(err, time, internal, customAttributes, hasReplay) {
|
|
113
|
+
storeError(err, time, internal, customAttributes, hasReplay, swallowReason) {
|
|
113
114
|
if (!err) return;
|
|
114
115
|
// are we in an interaction
|
|
115
116
|
time = time || now();
|
|
@@ -127,7 +128,7 @@ export class Aggregate extends AggregateBase {
|
|
|
127
128
|
const {
|
|
128
129
|
shouldSwallow,
|
|
129
130
|
reason
|
|
130
|
-
} = evaluateInternalError(stackInfo, internal);
|
|
131
|
+
} = evaluateInternalError(stackInfo, internal, swallowReason);
|
|
131
132
|
if (shouldSwallow) {
|
|
132
133
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Internal/Error/' + reason], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
133
134
|
return;
|
|
@@ -5,10 +5,10 @@ const REASON_SECURITY_POLICY = 'Security-Policy';
|
|
|
5
5
|
* @param {Object} stackInfo - The error stack information.
|
|
6
6
|
* @returns {boolean} - Whether the error should be swallowed or not.
|
|
7
7
|
*/
|
|
8
|
-
export function evaluateInternalError(stackInfo, internal) {
|
|
8
|
+
export function evaluateInternalError(stackInfo, internal, reason) {
|
|
9
9
|
const output = {
|
|
10
10
|
shouldSwallow: internal || false,
|
|
11
|
-
reason: 'Other'
|
|
11
|
+
reason: reason || 'Other'
|
|
12
12
|
};
|
|
13
13
|
const leadingFrame = stackInfo.frames?.[0];
|
|
14
14
|
/** If we cant otherwise determine from the frames and message, the default of internal + reason will be the fallback */
|
|
@@ -20,9 +20,9 @@ export class Instrument extends InstrumentBase {
|
|
|
20
20
|
// this try-catch can be removed when IE11 is completely unsupported & gone
|
|
21
21
|
this.removeOnAbort = new AbortController();
|
|
22
22
|
} catch (e) {}
|
|
23
|
-
this.ee.on('internal-error', error => {
|
|
23
|
+
this.ee.on('internal-error', (error, reason) => {
|
|
24
24
|
if (!this.abortHandler) return;
|
|
25
|
-
handle('ierr', [castError(error), now(), true, {}, this.#replayRunning], undefined, this.featureName, this.ee);
|
|
25
|
+
handle('ierr', [castError(error), now(), true, {}, this.#replayRunning, reason], undefined, this.featureName, this.ee);
|
|
26
26
|
});
|
|
27
27
|
this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
28
28
|
this.#replayRunning = isRunning;
|
|
@@ -148,40 +148,6 @@ export class Aggregate extends AggregateBase {
|
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
150
|
unload() {
|
|
151
|
-
|
|
152
|
-
if (this.resourcesSent) return;
|
|
153
|
-
this.resourcesSent = true; // make sure this only gets sent once
|
|
154
|
-
|
|
155
|
-
// Capture SMs around network resources using the performance API to assess
|
|
156
|
-
// work to split this out from the ST nodes
|
|
157
|
-
// differentiate between internal+external and ajax+non-ajax
|
|
158
|
-
const ajaxResources = ['beacon', 'fetch', 'xmlhttprequest'];
|
|
159
|
-
const internalUrls = ['nr-data.net', 'newrelic.com', 'nr-local.net', 'localhost'];
|
|
160
|
-
function isInternal(x) {
|
|
161
|
-
return internalUrls.some(y => x.name.indexOf(y) >= 0);
|
|
162
|
-
}
|
|
163
|
-
function isAjax(x) {
|
|
164
|
-
return ajaxResources.includes(x.initiatorType);
|
|
165
|
-
}
|
|
166
|
-
const allResources = performance?.getEntriesByType('resource') || [];
|
|
167
|
-
allResources.forEach(entry => {
|
|
168
|
-
if (isInternal(entry)) {
|
|
169
|
-
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/Internal');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/Internal');
|
|
170
|
-
} else {
|
|
171
|
-
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/External');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/External');
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Capture SMs for performance markers and measures to assess the usage and possible inclusion of this
|
|
176
|
-
// data in the agent for use in NR
|
|
177
|
-
if (typeof performance !== 'undefined') {
|
|
178
|
-
const markers = performance.getEntriesByType('mark');
|
|
179
|
-
const measures = performance.getEntriesByType('measure');
|
|
180
|
-
if (markers.length) this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
|
|
181
|
-
if (measures.length) this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
|
|
182
|
-
}
|
|
183
|
-
} catch (e) {
|
|
184
|
-
// do nothing
|
|
185
|
-
}
|
|
151
|
+
// do nothing for now, marks and measures and resources stats are now being captured by the ge feature
|
|
186
152
|
}
|
|
187
153
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/common/config/init.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/common/config/init.js"],"names":[],"mappings":"AAwIA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,
|
|
1
|
+
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAkGnB;IAGD,gDAqBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAuB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BA1PY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B"}
|
|
@@ -9,7 +9,24 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export function getSubmitMethod({ isFinalHarvest }?: {
|
|
11
11
|
isFinalHarvest: boolean;
|
|
12
|
-
}): typeof xhr | typeof beacon;
|
|
12
|
+
}): typeof xhr | typeof beacon | typeof xhrFetch;
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param url
|
|
16
|
+
* @param body
|
|
17
|
+
* @param method
|
|
18
|
+
* @param headers
|
|
19
|
+
* @returns {Promise<Response>}
|
|
20
|
+
*/
|
|
21
|
+
export function xhrFetch({ url, body, method, headers }: {
|
|
22
|
+
url: any;
|
|
23
|
+
body?: null | undefined;
|
|
24
|
+
method?: string | undefined;
|
|
25
|
+
headers?: {
|
|
26
|
+
key: string;
|
|
27
|
+
value: string;
|
|
28
|
+
}[] | undefined;
|
|
29
|
+
}): Promise<Response>;
|
|
13
30
|
/**
|
|
14
31
|
* Send via XHR
|
|
15
32
|
* @param {Object} args - The args.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,
|
|
1
|
+
{"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,gDAcA;AAED;;;;;;;GAOG;AACH;;;;;;;;IAFa,OAAO,CAAC,QAAQ,CAAC,CAkB7B;AAED;;;;;;;;;GASG;AACH,0DAPG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;IACQ,IAAI,GAAnB,OAAO,YAAC;IACO,MAAM,GAArB,MAAM,YAAC;IAC+B,OAAO;aAAvC,MAAM;eAAS,MAAM;;CACnC,GAAU,cAAc,CAmB1B;AAED;;;;;;GAMG;AACH,sCAJG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;CACf,GAAU,OAAO,CAanB;6BAjGY,0BAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAuBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,
|
|
1
|
+
{"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,CAiLlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
|
|
@@ -24,7 +24,7 @@ export class Aggregate extends AggregateBase {
|
|
|
24
24
|
ua: any;
|
|
25
25
|
at: any;
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
trackSupportabilityMetrics(): void;
|
|
28
28
|
}
|
|
29
29
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
30
30
|
import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IACjC,2BAsKC;IAnKC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IA8B9F,4CAAuD;IAyHzD,mCAGQ;IASZ;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QA0CjB;IAED,qCAEC;IAED;;;MAEC;IAED,mCASC;CACF;8BA7P6B,4BAA4B;sCAOpB,wCAAwC;iCAX7C,2CAA2C"}
|
|
@@ -5,4 +5,9 @@ export const OBSERVED_EVENTS: string[];
|
|
|
5
5
|
export const OBSERVED_WINDOW_EVENTS: string[];
|
|
6
6
|
export const RAGE_CLICK_THRESHOLD_EVENTS: 4;
|
|
7
7
|
export const RAGE_CLICK_THRESHOLD_MS: 1000;
|
|
8
|
+
export namespace FEATURE_FLAGS {
|
|
9
|
+
let MARKS: string;
|
|
10
|
+
let MEASURES: string;
|
|
11
|
+
let RESOURCES: string;
|
|
12
|
+
}
|
|
8
13
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAUA;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAUA;IACE,2BAAiC;IACjC,2CAkCC;CACF;AAED,8CAAuC;+BA1CR,6BAA6B"}
|
|
@@ -29,9 +29,10 @@ export class Aggregate extends AggregateBase {
|
|
|
29
29
|
* @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
|
|
30
30
|
* @param {object=} customAttributes any custom attributes to be included in the error payload
|
|
31
31
|
* @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
|
|
32
|
+
* @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
|
|
32
33
|
* @returns
|
|
33
34
|
*/
|
|
34
|
-
storeError(err: Error | UncaughtError, time: number, internal?: boolean | undefined, customAttributes?: object | undefined, hasReplay?: boolean | undefined): void;
|
|
35
|
+
storeError(err: Error | UncaughtError, time: number, internal?: boolean | undefined, customAttributes?: object | undefined, hasReplay?: boolean | undefined, swallowReason?: string | undefined): void;
|
|
35
36
|
onInteractionDone(interaction: any, wasSaved: any): void;
|
|
36
37
|
onSoftNavNotification(interactionId: any, wasFinished: any, softNavAttrs: any): void;
|
|
37
38
|
#private;
|