@newrelic/browser-agent 1.270.3 → 1.272.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 +14 -0
- package/dist/cjs/common/config/init.js +8 -4
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/features/generic_events/aggregate/index.js +39 -0
- package/dist/cjs/features/generic_events/instrument/index.js +1 -1
- package/dist/cjs/features/jserrors/shared/cast-error.js +9 -3
- package/dist/esm/common/config/init.js +8 -4
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/features/generic_events/aggregate/index.js +39 -0
- package/dist/esm/features/generic_events/instrument/index.js +1 -1
- package/dist/esm/features/jserrors/shared/cast-error.js +9 -3
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/shared/cast-error.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/common/config/init.js +6 -2
- package/src/features/generic_events/aggregate/index.js +35 -0
- package/src/features/generic_events/instrument/index.js +2 -0
- package/src/features/jserrors/shared/cast-error.js +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.272.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.271.0...v1.272.0) (2024-11-07)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Marks and measures ([#1189](https://github.com/newrelic/newrelic-browser-agent/issues/1189)) ([1a58409](https://github.com/newrelic/newrelic-browser-agent/commit/1a58409cda8b7b264e58fe284041540941dccd1c))
|
|
12
|
+
|
|
13
|
+
## [1.271.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.270.3...v1.271.0) (2024-11-01)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* Ignore unhandled promise rejections that lack a valid reason ([#1233](https://github.com/newrelic/newrelic-browser-agent/issues/1233)) ([25a1fff](https://github.com/newrelic/newrelic-browser-agent/commit/25a1fffb91fa5936766a7bc89d735b3018aa62a2))
|
|
19
|
+
|
|
6
20
|
## [1.270.3](https://github.com/newrelic/newrelic-browser-agent/compare/v1.270.2...v1.270.3) (2024-10-31)
|
|
7
21
|
|
|
8
22
|
|
|
@@ -80,9 +80,6 @@ const model = () => {
|
|
|
80
80
|
page_action: {
|
|
81
81
|
enabled: true
|
|
82
82
|
},
|
|
83
|
-
user_actions: {
|
|
84
|
-
enabled: true
|
|
85
|
-
},
|
|
86
83
|
page_view_event: {
|
|
87
84
|
enabled: true,
|
|
88
85
|
autoStart: true
|
|
@@ -92,6 +89,10 @@ const model = () => {
|
|
|
92
89
|
harvestTimeSeconds: 30,
|
|
93
90
|
autoStart: true
|
|
94
91
|
},
|
|
92
|
+
performance: {
|
|
93
|
+
capture_marks: false,
|
|
94
|
+
capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
|
|
95
|
+
},
|
|
95
96
|
privacy: {
|
|
96
97
|
cookies_enabled: true
|
|
97
98
|
},
|
|
@@ -175,7 +176,10 @@ const model = () => {
|
|
|
175
176
|
harvestTimeSeconds: 10,
|
|
176
177
|
autoStart: true
|
|
177
178
|
},
|
|
178
|
-
ssl: undefined
|
|
179
|
+
ssl: undefined,
|
|
180
|
+
user_actions: {
|
|
181
|
+
enabled: true
|
|
182
|
+
}
|
|
179
183
|
};
|
|
180
184
|
};
|
|
181
185
|
const _cache = {};
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.272.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.272.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -107,6 +107,45 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
107
107
|
if (options.isFinalHarvest) this.addUserAction(this.userActionAggregator.aggregationEvent);
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* is it worth complicating the agent and skipping the POs for single repeating queries? maybe,
|
|
113
|
+
* but right now it was less desirable simply because it is a nice benefit of populating the event buffer
|
|
114
|
+
* immediately as events happen for payload evaluation purposes and that becomes a little more chaotic
|
|
115
|
+
* with an arbitrary query method. note: eventTypes: [...types] does not support the 'buffered' flag so we have
|
|
116
|
+
* to create up to two PO's here.
|
|
117
|
+
*/
|
|
118
|
+
const performanceTypesToCapture = [...(agentRef.init.performance.capture_marks ? ['mark'] : []), ...(agentRef.init.performance.capture_measures ? ['measure'] : [])];
|
|
119
|
+
if (performanceTypesToCapture.length) {
|
|
120
|
+
try {
|
|
121
|
+
performanceTypesToCapture.forEach(type => {
|
|
122
|
+
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
|
123
|
+
const observer = new PerformanceObserver(list => {
|
|
124
|
+
list.getEntries().forEach(entry => {
|
|
125
|
+
try {
|
|
126
|
+
this.addEvent({
|
|
127
|
+
eventType: 'BrowserPerformance',
|
|
128
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
|
|
129
|
+
entryName: entry.name,
|
|
130
|
+
entryDuration: entry.duration,
|
|
131
|
+
entryType: type,
|
|
132
|
+
...(entry.detail && {
|
|
133
|
+
entryDetail: entry.detail
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
observer.observe({
|
|
140
|
+
buffered: true,
|
|
141
|
+
type
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
} catch (err) {
|
|
146
|
+
// Something failed in our set up, likely the browser does not support PO's... do nothing
|
|
147
|
+
}
|
|
148
|
+
}
|
|
110
149
|
this.harvestScheduler = new _harvestScheduler.HarvestScheduler('ins', {
|
|
111
150
|
onFinished: (...args) => this.onHarvestFinished(...args)
|
|
112
151
|
}, this);
|
|
@@ -17,7 +17,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
17
17
|
static featureName = _constants.FEATURE_NAME;
|
|
18
18
|
constructor(agentRef, auto = true) {
|
|
19
19
|
super(agentRef, _constants.FEATURE_NAME, auto);
|
|
20
|
-
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.user_actions.enabled
|
|
20
|
+
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled
|
|
21
21
|
// other future generic event source configs to go here, like M&Ms, PageResouce, etc.
|
|
22
22
|
];
|
|
23
23
|
if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
|
|
@@ -32,8 +32,15 @@ function castError(error) {
|
|
|
32
32
|
* @returns {Error} An Error object with the message as the casted reason
|
|
33
33
|
*/
|
|
34
34
|
function castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const prefix = 'Unhandled Promise Rejection';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
|
|
39
|
+
* We drop promise rejections that could not form a valid error stack or message deriving from the .reason attribute
|
|
40
|
+
* -- such as a manually invoked rejection without an argument -- since they lack reproduction value and create confusion.
|
|
41
|
+
* */
|
|
42
|
+
if (!promiseRejectionEvent?.reason) return;
|
|
43
|
+
if (canTrustError(promiseRejectionEvent.reason)) {
|
|
37
44
|
try {
|
|
38
45
|
promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
|
|
39
46
|
return castError(promiseRejectionEvent.reason);
|
|
@@ -41,7 +48,6 @@ function castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
|
41
48
|
return castError(promiseRejectionEvent.reason);
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
|
-
if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix);
|
|
45
51
|
const error = castError(promiseRejectionEvent.reason);
|
|
46
52
|
error.message = prefix + ': ' + error?.message;
|
|
47
53
|
return error;
|
|
@@ -72,9 +72,6 @@ const model = () => {
|
|
|
72
72
|
page_action: {
|
|
73
73
|
enabled: true
|
|
74
74
|
},
|
|
75
|
-
user_actions: {
|
|
76
|
-
enabled: true
|
|
77
|
-
},
|
|
78
75
|
page_view_event: {
|
|
79
76
|
enabled: true,
|
|
80
77
|
autoStart: true
|
|
@@ -84,6 +81,10 @@ const model = () => {
|
|
|
84
81
|
harvestTimeSeconds: 30,
|
|
85
82
|
autoStart: true
|
|
86
83
|
},
|
|
84
|
+
performance: {
|
|
85
|
+
capture_marks: false,
|
|
86
|
+
capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
|
|
87
|
+
},
|
|
87
88
|
privacy: {
|
|
88
89
|
cookies_enabled: true
|
|
89
90
|
},
|
|
@@ -167,7 +168,10 @@ const model = () => {
|
|
|
167
168
|
harvestTimeSeconds: 10,
|
|
168
169
|
autoStart: true
|
|
169
170
|
},
|
|
170
|
-
ssl: undefined
|
|
171
|
+
ssl: undefined,
|
|
172
|
+
user_actions: {
|
|
173
|
+
enabled: true
|
|
174
|
+
}
|
|
171
175
|
};
|
|
172
176
|
};
|
|
173
177
|
const _cache = {};
|
|
@@ -100,6 +100,45 @@ export class Aggregate extends AggregateBase {
|
|
|
100
100
|
if (options.isFinalHarvest) this.addUserAction(this.userActionAggregator.aggregationEvent);
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* is it worth complicating the agent and skipping the POs for single repeating queries? maybe,
|
|
106
|
+
* but right now it was less desirable simply because it is a nice benefit of populating the event buffer
|
|
107
|
+
* immediately as events happen for payload evaluation purposes and that becomes a little more chaotic
|
|
108
|
+
* with an arbitrary query method. note: eventTypes: [...types] does not support the 'buffered' flag so we have
|
|
109
|
+
* to create up to two PO's here.
|
|
110
|
+
*/
|
|
111
|
+
const performanceTypesToCapture = [...(agentRef.init.performance.capture_marks ? ['mark'] : []), ...(agentRef.init.performance.capture_measures ? ['measure'] : [])];
|
|
112
|
+
if (performanceTypesToCapture.length) {
|
|
113
|
+
try {
|
|
114
|
+
performanceTypesToCapture.forEach(type => {
|
|
115
|
+
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
|
116
|
+
const observer = new PerformanceObserver(list => {
|
|
117
|
+
list.getEntries().forEach(entry => {
|
|
118
|
+
try {
|
|
119
|
+
this.addEvent({
|
|
120
|
+
eventType: 'BrowserPerformance',
|
|
121
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
|
|
122
|
+
entryName: entry.name,
|
|
123
|
+
entryDuration: entry.duration,
|
|
124
|
+
entryType: type,
|
|
125
|
+
...(entry.detail && {
|
|
126
|
+
entryDetail: entry.detail
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
} catch (err) {}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
observer.observe({
|
|
133
|
+
buffered: true,
|
|
134
|
+
type
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
} catch (err) {
|
|
139
|
+
// Something failed in our set up, likely the browser does not support PO's... do nothing
|
|
140
|
+
}
|
|
141
|
+
}
|
|
103
142
|
this.harvestScheduler = new HarvestScheduler('ins', {
|
|
104
143
|
onFinished: (...args) => this.onHarvestFinished(...args)
|
|
105
144
|
}, this);
|
|
@@ -11,7 +11,7 @@ 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
|
-
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.user_actions.enabled
|
|
14
|
+
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled
|
|
15
15
|
// other future generic event source configs to go here, like M&Ms, PageResouce, etc.
|
|
16
16
|
];
|
|
17
17
|
if (isBrowserScope && agentRef.init.user_actions.enabled) {
|
|
@@ -25,8 +25,15 @@ export function castError(error) {
|
|
|
25
25
|
* @returns {Error} An Error object with the message as the casted reason
|
|
26
26
|
*/
|
|
27
27
|
export function castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const prefix = 'Unhandled Promise Rejection';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
|
|
32
|
+
* We drop promise rejections that could not form a valid error stack or message deriving from the .reason attribute
|
|
33
|
+
* -- such as a manually invoked rejection without an argument -- since they lack reproduction value and create confusion.
|
|
34
|
+
* */
|
|
35
|
+
if (!promiseRejectionEvent?.reason) return;
|
|
36
|
+
if (canTrustError(promiseRejectionEvent.reason)) {
|
|
30
37
|
try {
|
|
31
38
|
promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
|
|
32
39
|
return castError(promiseRejectionEvent.reason);
|
|
@@ -34,7 +41,6 @@ export function castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
|
34
41
|
return castError(promiseRejectionEvent.reason);
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
|
-
if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix);
|
|
38
44
|
const error = castError(promiseRejectionEvent.reason);
|
|
39
45
|
error.message = prefix + ': ' + error?.message;
|
|
40
46
|
return error;
|
|
@@ -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":"AAsHA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
|
|
@@ -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,2BAyHC;IAtHC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IAClG,oBAA+B;IA6B3B,4CAAuD;IAEvD,mDAyBC;IAiDH,mCAAuH;IAY3H;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QAmCjB;IAED;;;;;;kBAkBC;IAED,qCAGC;IAED,yBAMC;CACF;8BAvN6B,4BAA4B;4BAK9B,0BAA0B;sCAGhB,wCAAwC;iCAZ7C,2CAA2C"}
|
|
@@ -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,2CAuBC;CACF;AAED,8CAAuC;+BA/BR,6BAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cast-error.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/shared/cast-error.js"],"names":[],"mappings":"AAEA;;;;;KAKK;AACL,iCAHa,GAAG,GACD,KAAK,GAAC,aAAa,CAmBjC;AAED;;;;KAIK;AACL,uEAFe,KAAK,
|
|
1
|
+
{"version":3,"file":"cast-error.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/shared/cast-error.js"],"names":[],"mappings":"AAEA;;;;;KAKK;AACL,iCAHa,GAAG,GACD,KAAK,GAAC,aAAa,CAmBjC;AAED;;;;KAIK;AACL,uEAFe,KAAK,CAwBnB;AAED;;;;KAIK;AACL,2CAHa,UAAU,GACR,KAAK,GAAC,aAAa,CAUjC;8BArE6B,kBAAkB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.272.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "New Relic Browser Agent",
|
|
@@ -201,7 +201,8 @@
|
|
|
201
201
|
"devDependencies": {
|
|
202
202
|
"@babel/cli": "^7.23.4",
|
|
203
203
|
"@babel/core": "^7.23.7",
|
|
204
|
-
"@babel/eslint-parser": "^7.
|
|
204
|
+
"@babel/eslint-parser": "^7.25.9",
|
|
205
|
+
"@babel/plugin-syntax-import-attributes": "^7.26.0",
|
|
205
206
|
"@babel/plugin-transform-template-literals": "^7.24.7",
|
|
206
207
|
"@babel/preset-env": "^7.23.8",
|
|
207
208
|
"@babel/register": "^7.23.7",
|
|
@@ -48,9 +48,12 @@ const model = () => {
|
|
|
48
48
|
metrics: { enabled: true, autoStart: true },
|
|
49
49
|
obfuscate: undefined,
|
|
50
50
|
page_action: { enabled: true },
|
|
51
|
-
user_actions: { enabled: true },
|
|
52
51
|
page_view_event: { enabled: true, autoStart: true },
|
|
53
52
|
page_view_timing: { enabled: true, harvestTimeSeconds: 30, autoStart: true },
|
|
53
|
+
performance: {
|
|
54
|
+
capture_marks: false,
|
|
55
|
+
capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
|
|
56
|
+
},
|
|
54
57
|
privacy: { cookies_enabled: true }, // *cli - per discussion, default should be true
|
|
55
58
|
proxy: {
|
|
56
59
|
assets: undefined, // if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
|
|
@@ -105,7 +108,8 @@ const model = () => {
|
|
|
105
108
|
session_trace: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
|
|
106
109
|
soft_navigations: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
|
|
107
110
|
spa: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
|
|
108
|
-
ssl: undefined
|
|
111
|
+
ssl: undefined,
|
|
112
|
+
user_actions: { enabled: true }
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
@@ -97,6 +97,41 @@ export class Aggregate extends AggregateBase {
|
|
|
97
97
|
})
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* is it worth complicating the agent and skipping the POs for single repeating queries? maybe,
|
|
102
|
+
* but right now it was less desirable simply because it is a nice benefit of populating the event buffer
|
|
103
|
+
* immediately as events happen for payload evaluation purposes and that becomes a little more chaotic
|
|
104
|
+
* with an arbitrary query method. note: eventTypes: [...types] does not support the 'buffered' flag so we have
|
|
105
|
+
* to create up to two PO's here.
|
|
106
|
+
*/
|
|
107
|
+
const performanceTypesToCapture = [...(agentRef.init.performance.capture_marks ? ['mark'] : []), ...(agentRef.init.performance.capture_measures ? ['measure'] : [])]
|
|
108
|
+
if (performanceTypesToCapture.length) {
|
|
109
|
+
try {
|
|
110
|
+
performanceTypesToCapture.forEach(type => {
|
|
111
|
+
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
|
112
|
+
const observer = new PerformanceObserver((list) => {
|
|
113
|
+
list.getEntries().forEach(entry => {
|
|
114
|
+
try {
|
|
115
|
+
this.addEvent({
|
|
116
|
+
eventType: 'BrowserPerformance',
|
|
117
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
|
|
118
|
+
entryName: entry.name,
|
|
119
|
+
entryDuration: entry.duration,
|
|
120
|
+
entryType: type,
|
|
121
|
+
...(entry.detail && { entryDetail: entry.detail })
|
|
122
|
+
})
|
|
123
|
+
} catch (err) {
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
observer.observe({ buffered: true, type })
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
} catch (err) {
|
|
131
|
+
// Something failed in our set up, likely the browser does not support PO's... do nothing
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
100
135
|
this.harvestScheduler = new HarvestScheduler('ins', { onFinished: (...args) => this.onHarvestFinished(...args) }, this)
|
|
101
136
|
this.harvestScheduler.harvest.on('ins', (...args) => {
|
|
102
137
|
preHarvestMethods.forEach(fn => fn(...args))
|
|
@@ -14,6 +14,8 @@ export class Instrument extends InstrumentBase {
|
|
|
14
14
|
super(agentRef, FEATURE_NAME, auto)
|
|
15
15
|
const genericEventSourceConfigs = [
|
|
16
16
|
agentRef.init.page_action.enabled,
|
|
17
|
+
agentRef.init.performance.capture_marks,
|
|
18
|
+
agentRef.init.performance.capture_measures,
|
|
17
19
|
agentRef.init.user_actions.enabled
|
|
18
20
|
// other future generic event source configs to go here, like M&Ms, PageResouce, etc.
|
|
19
21
|
]
|
|
@@ -31,9 +31,16 @@ export function castError (error) {
|
|
|
31
31
|
* @returns {Error} An Error object with the message as the casted reason
|
|
32
32
|
*/
|
|
33
33
|
export function castPromiseRejectionEvent (promiseRejectionEvent) {
|
|
34
|
-
|
|
34
|
+
const prefix = 'Unhandled Promise Rejection'
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
|
|
38
|
+
* We drop promise rejections that could not form a valid error stack or message deriving from the .reason attribute
|
|
39
|
+
* -- such as a manually invoked rejection without an argument -- since they lack reproduction value and create confusion.
|
|
40
|
+
* */
|
|
41
|
+
if (!promiseRejectionEvent?.reason) return
|
|
42
|
+
|
|
43
|
+
if (canTrustError(promiseRejectionEvent.reason)) {
|
|
37
44
|
try {
|
|
38
45
|
promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message
|
|
39
46
|
return castError(promiseRejectionEvent.reason)
|
|
@@ -42,8 +49,6 @@ export function castPromiseRejectionEvent (promiseRejectionEvent) {
|
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix)
|
|
46
|
-
|
|
47
52
|
const error = castError(promiseRejectionEvent.reason)
|
|
48
53
|
error.message = prefix + ': ' + error?.message
|
|
49
54
|
return error
|