@newrelic/browser-agent 1.290.1 → 1.291.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/CHANGELOG.md +22 -0
- 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/harvester.js +0 -7
- package/dist/cjs/common/session/session-entity.js +5 -5
- package/dist/cjs/common/util/console.js +12 -0
- package/dist/cjs/features/generic_events/aggregate/index.js +18 -2
- package/dist/cjs/features/generic_events/instrument/index.js +2 -0
- package/dist/cjs/features/session_replay/aggregate/index.js +3 -0
- package/dist/cjs/features/session_replay/shared/recorder.js +0 -4
- package/dist/cjs/features/session_trace/aggregate/index.js +7 -3
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +36 -23
- package/dist/cjs/loaders/api/addToTrace.js +8 -0
- package/dist/cjs/loaders/api/constants.js +3 -2
- package/dist/cjs/loaders/api/measure.js +60 -0
- package/dist/cjs/loaders/api-base.js +11 -0
- 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/harvester.js +0 -7
- package/dist/esm/common/session/session-entity.js +5 -5
- package/dist/esm/common/util/console.js +13 -0
- package/dist/esm/features/generic_events/aggregate/index.js +18 -2
- package/dist/esm/features/generic_events/instrument/index.js +2 -0
- package/dist/esm/features/session_replay/aggregate/index.js +3 -0
- package/dist/esm/features/session_replay/shared/recorder.js +0 -4
- package/dist/esm/features/session_trace/aggregate/index.js +7 -3
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +36 -23
- package/dist/esm/loaders/api/addToTrace.js +8 -0
- package/dist/esm/loaders/api/constants.js +2 -1
- package/dist/esm/loaders/api/measure.js +53 -0
- package/dist/esm/loaders/api-base.js +12 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/harvest/harvester.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts +0 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/util/console.d.ts +0 -4
- package/dist/types/common/util/console.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/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +1 -3
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
- package/dist/types/loaders/api/addToTrace.d.ts.map +1 -1
- package/dist/types/loaders/api/constants.d.ts +1 -0
- package/dist/types/loaders/api/constants.d.ts.map +1 -1
- package/dist/types/loaders/api/measure.d.ts +2 -0
- package/dist/types/loaders/api/measure.d.ts.map +1 -0
- package/dist/types/loaders/api-base.d.ts +13 -0
- package/dist/types/loaders/api-base.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/harvest/harvester.js +0 -5
- package/src/common/session/session-entity.js +5 -6
- package/src/common/util/console.js +13 -0
- package/src/features/generic_events/aggregate/index.js +17 -2
- package/src/features/generic_events/instrument/index.js +2 -0
- package/src/features/session_replay/aggregate/index.js +4 -0
- package/src/features/session_replay/shared/recorder.js +0 -4
- package/src/features/session_trace/aggregate/index.js +7 -3
- package/src/features/session_trace/aggregate/trace/storage.js +37 -23
- package/src/loaders/api/addToTrace.js +6 -0
- package/src/loaders/api/constants.js +1 -0
- package/src/loaders/api/measure.js +53 -0
- package/src/loaders/api-base.js +12 -1
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { dispatchGlobalEvent } from '../dispatch/global-event'
|
|
7
|
+
|
|
6
8
|
/* eslint no-console: ["error", { allow: ["debug"] }] */
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -14,4 +16,15 @@
|
|
|
14
16
|
export function warn (code, secondary) {
|
|
15
17
|
if (typeof console.debug !== 'function') return
|
|
16
18
|
console.debug(`New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#${code}`, secondary)
|
|
19
|
+
dispatchGlobalEvent({
|
|
20
|
+
agentIdentifier: null,
|
|
21
|
+
drained: null,
|
|
22
|
+
type: 'data',
|
|
23
|
+
name: 'warn',
|
|
24
|
+
feature: 'warn',
|
|
25
|
+
data: {
|
|
26
|
+
code,
|
|
27
|
+
secondary
|
|
28
|
+
}
|
|
29
|
+
})
|
|
17
30
|
}
|
|
@@ -143,7 +143,7 @@ export class Aggregate extends AggregateBase {
|
|
|
143
143
|
...detailObj,
|
|
144
144
|
eventType: 'BrowserPerformance',
|
|
145
145
|
timestamp: this.toEpoch(entry.startTime),
|
|
146
|
-
entryName:
|
|
146
|
+
entryName: entry.name,
|
|
147
147
|
entryDuration: entry.duration,
|
|
148
148
|
entryType: type
|
|
149
149
|
})
|
|
@@ -208,7 +208,7 @@ export class Aggregate extends AggregateBase {
|
|
|
208
208
|
...entryObject,
|
|
209
209
|
eventType: 'BrowserPerformance',
|
|
210
210
|
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entryObject.startTime)),
|
|
211
|
-
entryName: name,
|
|
211
|
+
entryName: cleanURL(name),
|
|
212
212
|
entryDuration: duration,
|
|
213
213
|
firstParty
|
|
214
214
|
}
|
|
@@ -220,6 +220,21 @@ export class Aggregate extends AggregateBase {
|
|
|
220
220
|
}, this.featureName, this.ee)
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
registerHandler('api-measure', (args, n) => {
|
|
224
|
+
const { start, duration, customAttributes } = args
|
|
225
|
+
|
|
226
|
+
const event = {
|
|
227
|
+
...customAttributes,
|
|
228
|
+
eventType: 'BrowserPerformance',
|
|
229
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(start)),
|
|
230
|
+
entryName: n,
|
|
231
|
+
entryDuration: duration,
|
|
232
|
+
entryType: 'measure'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.addEvent(event)
|
|
236
|
+
}, this.featureName, this.ee)
|
|
237
|
+
|
|
223
238
|
agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
224
239
|
this.drain()
|
|
225
240
|
})
|
|
@@ -11,6 +11,7 @@ import { setupAddPageActionAPI } from '../../../loaders/api/addPageAction'
|
|
|
11
11
|
import { setupFinishedAPI } from '../../../loaders/api/finished'
|
|
12
12
|
import { setupRecordCustomEventAPI } from '../../../loaders/api/recordCustomEvent'
|
|
13
13
|
import { setupRegisterAPI } from '../../../loaders/api/register'
|
|
14
|
+
import { setupMeasureAPI } from '../../../loaders/api/measure'
|
|
14
15
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
15
16
|
import { FEATURE_NAME, OBSERVED_EVENTS, OBSERVED_WINDOW_EVENTS } from '../constants'
|
|
16
17
|
|
|
@@ -32,6 +33,7 @@ export class Instrument extends InstrumentBase {
|
|
|
32
33
|
setupRecordCustomEventAPI(agentRef)
|
|
33
34
|
setupFinishedAPI(agentRef)
|
|
34
35
|
setupRegisterAPI(agentRef)
|
|
36
|
+
setupMeasureAPI(agentRef)
|
|
35
37
|
|
|
36
38
|
if (isBrowserScope) {
|
|
37
39
|
if (agentRef.init.user_actions.enabled) {
|
|
@@ -262,6 +262,10 @@ export class Aggregate extends AggregateBase {
|
|
|
262
262
|
if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
263
263
|
payloadOutput.payload = payload
|
|
264
264
|
|
|
265
|
+
if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
|
|
266
|
+
warn(59, JSON.stringify(this.agentRef.runtime.session.state))
|
|
267
|
+
}
|
|
268
|
+
|
|
265
269
|
return [payloadOutput]
|
|
266
270
|
}
|
|
267
271
|
|
|
@@ -155,10 +155,6 @@ export class Recorder {
|
|
|
155
155
|
/** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
|
|
156
156
|
store (event, isCheckout) {
|
|
157
157
|
if (!event) return
|
|
158
|
-
if (this.parent.agentRef.runtime.session?.isAfterSessionExpiry(event.timestamp)) {
|
|
159
|
-
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, FEATURE_NAMES.metrics, this.ee)
|
|
160
|
-
return
|
|
161
|
-
}
|
|
162
158
|
|
|
163
159
|
if (!(this.parent instanceof AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1]
|
|
164
160
|
else this.currentBufferTarget = this.#events
|
|
@@ -11,6 +11,7 @@ import { globalScope } from '../../../common/constants/runtime'
|
|
|
11
11
|
import { MODE, SESSION_EVENTS } from '../../../common/session/constants'
|
|
12
12
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
13
13
|
import { cleanURL } from '../../../common/url/clean-url'
|
|
14
|
+
import { warn } from '../../../common/util/console'
|
|
14
15
|
|
|
15
16
|
const ERROR_MODE_SECONDS_WINDOW = 30 * 1000 // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
|
|
16
17
|
/** Reserved room for query param attrs */
|
|
@@ -85,7 +86,7 @@ export class Aggregate extends AggregateBase {
|
|
|
85
86
|
registerHandler('bstResource', (...args) => this.events.storeResources(...args), this.featureName, this.ee)
|
|
86
87
|
registerHandler('bstHist', (...args) => this.events.storeHist(...args), this.featureName, this.ee)
|
|
87
88
|
registerHandler('bstXhrAgg', (...args) => this.events.storeXhrAgg(...args), this.featureName, this.ee)
|
|
88
|
-
registerHandler('bstApi', (...args) => this.events.
|
|
89
|
+
registerHandler('bstApi', (...args) => this.events.storeNode(...args), this.featureName, this.ee)
|
|
89
90
|
registerHandler('trace-jserror', (...args) => this.events.storeErrorAgg(...args), this.featureName, this.ee)
|
|
90
91
|
registerHandler('pvtAdded', (...args) => this.events.processPVT(...args), this.featureName, this.ee)
|
|
91
92
|
|
|
@@ -97,10 +98,12 @@ export class Aggregate extends AggregateBase {
|
|
|
97
98
|
}
|
|
98
99
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
99
100
|
this.drain()
|
|
101
|
+
/** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
|
|
102
|
+
this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
preHarvestChecks () {
|
|
103
|
-
if (this.mode !== MODE.FULL) return // only allow harvest if running in full mode
|
|
106
|
+
if (this.blocked || this.mode !== MODE.FULL) return // only allow harvest if running in full mode
|
|
104
107
|
if (!this.timeKeeper?.ready) return // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
|
|
105
108
|
if (!this.agentRef.runtime.session) return // session entity is required for trace to run and continue running
|
|
106
109
|
if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
|
|
@@ -178,7 +181,8 @@ export class Aggregate extends AggregateBase {
|
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
/** Stop running for the remainder of the page lifecycle */
|
|
181
|
-
abort () {
|
|
184
|
+
abort (code) {
|
|
185
|
+
warn(60, code)
|
|
182
186
|
this.blocked = true
|
|
183
187
|
this.mode = MODE.OFF
|
|
184
188
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
@@ -41,23 +41,18 @@ export class TraceStorage {
|
|
|
41
41
|
this.parent = parent
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Central function called by all the other store__ & addToTrace API to append a trace node. */
|
|
49
|
-
storeSTN (stn) {
|
|
50
|
-
if (this.parent.blocked) return
|
|
44
|
+
#canStoreNewNode () {
|
|
45
|
+
if (this.parent.blocked) return false
|
|
51
46
|
if (this.nodeCount >= MAX_NODES_PER_HARVEST) { // limit the amount of pending data awaiting next harvest
|
|
52
|
-
if (this.parent.mode !== MODE.ERROR) return
|
|
47
|
+
if (this.parent.mode !== MODE.ERROR) return false
|
|
53
48
|
const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW) // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
|
|
54
|
-
if (openedSpace === 0) return
|
|
55
|
-
}
|
|
56
|
-
if (this.isAfterSessionExpiry(stn.s)) {
|
|
57
|
-
this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen')
|
|
58
|
-
return
|
|
49
|
+
if (openedSpace === 0) return false
|
|
59
50
|
}
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
60
53
|
|
|
54
|
+
/** Central internal function called by all the other store__ & addToTrace API to append a trace node. They MUST all have checked #canStoreNewNode before calling this func!! */
|
|
55
|
+
#storeSTN (stn) {
|
|
61
56
|
if (this.trace[stn.n]) this.trace[stn.n].push(stn)
|
|
62
57
|
else this.trace[stn.n] = [stn]
|
|
63
58
|
|
|
@@ -143,6 +138,11 @@ export class TraceStorage {
|
|
|
143
138
|
}
|
|
144
139
|
}
|
|
145
140
|
|
|
141
|
+
storeNode (node) {
|
|
142
|
+
if (!this.#canStoreNewNode()) return
|
|
143
|
+
this.#storeSTN(node)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
146
|
processPVT (name, value, attrs) {
|
|
147
147
|
this.storeTiming({ [name]: value })
|
|
148
148
|
}
|
|
@@ -168,13 +168,16 @@ export class TraceStorage {
|
|
|
168
168
|
Math.floor(this.parent.timeKeeper.correctAbsoluteTimestamp(val))
|
|
169
169
|
)
|
|
170
170
|
}
|
|
171
|
-
this
|
|
171
|
+
if (!this.#canStoreNewNode()) return // at any point when no new nodes can be stored, there's no point in processing the rest of the timing entries
|
|
172
|
+
this.#storeSTN(new TraceNode(key, val, val, 'document', 'timing'))
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
// Tracks the events and their listener's duration on objects wrapped by wrap-events.
|
|
176
177
|
storeEvent (currentEvent, target, start, end) {
|
|
177
178
|
if (this.shouldIgnoreEvent(currentEvent, target)) return
|
|
179
|
+
if (!this.#canStoreNewNode()) return // need to check if adding node will succeed BEFORE storing event ref below (*cli Jun'25 - addressing memory leak in aborted ST issue #NR-420780)
|
|
180
|
+
|
|
178
181
|
if (this.prevStoredEvents.has(currentEvent)) return // prevent multiple listeners of an event from creating duplicate trace nodes per occurrence. Cleared every harvest. near-zero chance for re-duplication after clearing per harvest since the timestamps of the event are considered for uniqueness.
|
|
179
182
|
this.prevStoredEvents.add(currentEvent)
|
|
180
183
|
|
|
@@ -186,7 +189,7 @@ export class TraceStorage {
|
|
|
186
189
|
} catch (e) {
|
|
187
190
|
evt.o = eventOrigin(null, target, this.parent.ee)
|
|
188
191
|
}
|
|
189
|
-
this
|
|
192
|
+
this.#storeSTN(evt)
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
shouldIgnoreEvent (event, target) {
|
|
@@ -224,7 +227,8 @@ export class TraceStorage {
|
|
|
224
227
|
|
|
225
228
|
// Tracks when the window history API specified by wrap-history is used.
|
|
226
229
|
storeHist (path, old, time) {
|
|
227
|
-
this
|
|
230
|
+
if (!this.#canStoreNewNode()) return
|
|
231
|
+
this.#storeSTN(new TraceNode('history.pushState', time, time, path, old))
|
|
228
232
|
}
|
|
229
233
|
|
|
230
234
|
#laststart = 0
|
|
@@ -232,14 +236,17 @@ export class TraceStorage {
|
|
|
232
236
|
storeResources (resources) {
|
|
233
237
|
if (!resources || resources.length === 0) return
|
|
234
238
|
|
|
235
|
-
resources.
|
|
236
|
-
|
|
239
|
+
for (let i = 0; i < resources.length; i++) {
|
|
240
|
+
const currentResource = resources[i]
|
|
241
|
+
if ((currentResource.fetchStart | 0) <= this.#laststart) continue // don't recollect already-seen resources
|
|
242
|
+
if (!this.#canStoreNewNode()) break // stop processing if we can't store any more resource nodes anyways
|
|
237
243
|
|
|
238
244
|
const { initiatorType, fetchStart, responseEnd, entryType } = currentResource
|
|
239
245
|
const { protocol, hostname, port, pathname } = parseUrl(currentResource.name)
|
|
240
246
|
const res = new TraceNode(initiatorType, fetchStart | 0, responseEnd | 0, `${protocol}://${hostname}:${port}${pathname}`, entryType)
|
|
241
|
-
|
|
242
|
-
|
|
247
|
+
|
|
248
|
+
this.#storeSTN(res)
|
|
249
|
+
}
|
|
243
250
|
|
|
244
251
|
this.#laststart = resources[resources.length - 1].fetchStart | 0
|
|
245
252
|
}
|
|
@@ -247,13 +254,15 @@ export class TraceStorage {
|
|
|
247
254
|
// JavascriptError (FEATURE) events pipes into ST here.
|
|
248
255
|
storeErrorAgg (type, name, params, metrics) {
|
|
249
256
|
if (type !== 'err') return // internal errors are purposefully ignored
|
|
250
|
-
this
|
|
257
|
+
if (!this.#canStoreNewNode()) return
|
|
258
|
+
this.#storeSTN(new TraceNode('error', metrics.time, metrics.time, params.message, params.stackHash))
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
// Ajax (FEATURE) events--XML & fetches--pipes into ST here.
|
|
254
262
|
storeXhrAgg (type, name, params, metrics) {
|
|
255
263
|
if (type !== 'xhr') return
|
|
256
|
-
this
|
|
264
|
+
if (!this.#canStoreNewNode()) return
|
|
265
|
+
this.#storeSTN(new TraceNode('Ajax', metrics.time, metrics.time + metrics.duration, `${params.status} ${params.method}: ${params.host}${params.pathname}`, 'ajax'))
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
/* Below are the interface expected & required of whatever storage is used across all features on an individual basis. This allows a common `.events` property on Trace shared with AggregateBase.
|
|
@@ -279,7 +288,12 @@ export class TraceStorage {
|
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
reloadSave () {
|
|
282
|
-
Object.values(this.#backupTrace)
|
|
291
|
+
for (const stnsArray of Object.values(this.#backupTrace)) {
|
|
292
|
+
for (const stn of stnsArray) {
|
|
293
|
+
if (!this.#canStoreNewNode()) return // stop attempting to re-store nodes
|
|
294
|
+
this.#storeSTN(stn)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
283
297
|
}
|
|
284
298
|
|
|
285
299
|
clearSave () {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { originTime } from '../../common/constants/runtime'
|
|
6
6
|
import { handle } from '../../common/event-emitter/handle'
|
|
7
|
+
import { warn } from '../../common/util/console'
|
|
7
8
|
import { FEATURE_NAMES } from '../features/features'
|
|
8
9
|
import { ADD_TO_TRACE } from './constants'
|
|
9
10
|
import { setupAPI } from './sharedHandlers'
|
|
@@ -20,6 +21,11 @@ export function setupAddToTraceAPI (agent) {
|
|
|
20
21
|
t: 'api'
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
if (report.s < 0 || report.e < 0 || report.e < report.s) {
|
|
25
|
+
warn(61, { start: report.s, end: report.e })
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
handle('bstApi', [report], undefined, FEATURE_NAMES.sessionTrace, agent.ee)
|
|
24
30
|
}, agent)
|
|
25
31
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { handle } from '../../common/event-emitter/handle'
|
|
6
|
+
import { now } from '../../common/timing/now'
|
|
7
|
+
import { warn } from '../../common/util/console'
|
|
8
|
+
import { FEATURE_NAMES } from '../features/features'
|
|
9
|
+
import { prefix, MEASURE } from './constants'
|
|
10
|
+
import { setupAPI } from './sharedHandlers'
|
|
11
|
+
|
|
12
|
+
export function setupMeasureAPI (agent) {
|
|
13
|
+
setupAPI(MEASURE, function (name, options) {
|
|
14
|
+
const n = now()
|
|
15
|
+
const { start, end, customAttributes } = options || {}
|
|
16
|
+
const returnObj = { customAttributes: customAttributes || {} }
|
|
17
|
+
|
|
18
|
+
if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
|
|
19
|
+
warn(57)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* getValueFromTiming - Helper function to extract a numeric value from a supplied option.
|
|
25
|
+
* @param {Number|PerformanceMark} [timing] The timing value
|
|
26
|
+
* @param {Number} [d] The default value to return if timing is invalid
|
|
27
|
+
* @returns {Number} The timing value or the default value
|
|
28
|
+
*/
|
|
29
|
+
const getValueFromTiming = (timing, d) => {
|
|
30
|
+
if (timing == null) return d
|
|
31
|
+
if (typeof timing === 'number') return timing
|
|
32
|
+
if (timing instanceof PerformanceMark) return timing.startTime
|
|
33
|
+
return Number.NaN
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
returnObj.start = getValueFromTiming(start, 0)
|
|
37
|
+
returnObj.end = getValueFromTiming(end, n)
|
|
38
|
+
if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
|
|
39
|
+
warn(57)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
returnObj.duration = returnObj.end - returnObj.start
|
|
44
|
+
if (returnObj.duration < 0) {
|
|
45
|
+
warn(58)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
handle(prefix + MEASURE, [returnObj, name], undefined, FEATURE_NAMES.genericEvents, agent.ee)
|
|
50
|
+
|
|
51
|
+
return returnObj
|
|
52
|
+
}, agent)
|
|
53
|
+
}
|
package/src/loaders/api-base.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { warn } from '../common/util/console'
|
|
6
|
-
import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER } from './api/constants'
|
|
6
|
+
import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
|
|
@@ -215,4 +215,15 @@ export class ApiBase {
|
|
|
215
215
|
wrapLogger (parent, functionName, options) {
|
|
216
216
|
return this.#callMethod(WRAP_LOGGER, parent, functionName, options)
|
|
217
217
|
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Measures a task that is recorded as a BrowserPerformance event.
|
|
221
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
|
|
222
|
+
* @param {string} name The name of the task
|
|
223
|
+
* @param {object?} options An object used to control the way the measure API operates
|
|
224
|
+
* @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
|
|
225
|
+
*/
|
|
226
|
+
measure (name, options) {
|
|
227
|
+
return this.#callMethod(MEASURE, name, options)
|
|
228
|
+
}
|
|
218
229
|
}
|