@newrelic/browser-agent 1.316.0 → 1.317.0-rc.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 +15 -0
- package/dist/cjs/common/config/configurable.js +1 -1
- package/dist/cjs/common/config/init-types.js +1 -0
- package/dist/cjs/common/config/init.js +6 -4
- package/dist/cjs/common/constants/env.cdn.js +2 -2
- package/dist/cjs/common/constants/env.npm.js +2 -2
- package/dist/cjs/common/constants/events.js +26 -0
- package/dist/cjs/common/harvest/harvester.js +9 -217
- package/dist/cjs/common/harvest/send.js +232 -0
- package/dist/cjs/common/harvest/types.js +9 -5
- package/dist/cjs/common/payloads/payloads.js +127 -0
- package/dist/cjs/common/serialize/bel-serializer.js +4 -3
- package/dist/cjs/common/session/session-entity.js +8 -8
- package/dist/cjs/common/util/data-size.js +2 -2
- package/dist/cjs/common/util/obfuscate.js +147 -3
- package/dist/cjs/common/v2/mfe-vitals.js +286 -0
- package/dist/cjs/common/wrap/wrap-websocket.js +2 -1
- package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
- package/dist/cjs/features/ajax/aggregate/gql.js +40 -1
- package/dist/cjs/features/ajax/aggregate/index.js +51 -13
- package/dist/cjs/features/ajax/constants.js +6 -1
- package/dist/cjs/features/ajax/instrument/index.js +105 -17
- package/dist/cjs/features/generic_events/aggregate/index.js +15 -10
- package/dist/cjs/features/generic_events/constants.js +3 -2
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +2 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +12 -3
- package/dist/cjs/features/logging/aggregate/index.js +12 -8
- package/dist/cjs/features/metrics/aggregate/index.js +5 -1
- package/dist/cjs/features/page_view_event/aggregate/index.js +72 -33
- package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +39 -29
- package/dist/cjs/features/session_trace/aggregate/index.js +35 -29
- package/dist/cjs/features/session_trace/constants.js +17 -3
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +36 -5
- package/dist/cjs/features/soft_navigations/aggregate/index.js +7 -1
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +6 -4
- package/dist/cjs/features/utils/aggregate-base.js +4 -3
- package/dist/cjs/loaders/api/register.js +26 -4
- package/dist/esm/common/config/configurable.js +1 -1
- package/dist/esm/common/config/init-types.js +1 -0
- package/dist/esm/common/config/init.js +3 -1
- package/dist/esm/common/constants/env.cdn.js +2 -2
- package/dist/esm/common/constants/env.npm.js +2 -2
- package/dist/esm/common/constants/events.js +20 -0
- package/dist/esm/common/harvest/harvester.js +9 -217
- package/dist/esm/common/harvest/send.js +226 -0
- package/dist/esm/common/harvest/types.js +9 -5
- package/dist/esm/common/payloads/payloads.js +118 -0
- package/dist/esm/common/serialize/bel-serializer.js +4 -3
- package/dist/esm/common/session/session-entity.js +9 -9
- package/dist/esm/common/util/data-size.js +2 -2
- package/dist/esm/common/util/obfuscate.js +147 -3
- package/dist/esm/common/v2/mfe-vitals.js +281 -0
- package/dist/esm/common/wrap/wrap-websocket.js +2 -1
- package/dist/esm/common/wrap/wrap-xhr.js +1 -1
- package/dist/esm/features/ajax/aggregate/gql.js +39 -1
- package/dist/esm/features/ajax/aggregate/index.js +52 -14
- package/dist/esm/features/ajax/constants.js +5 -0
- package/dist/esm/features/ajax/instrument/index.js +106 -18
- package/dist/esm/features/generic_events/aggregate/index.js +15 -10
- package/dist/esm/features/generic_events/constants.js +3 -2
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +2 -2
- package/dist/esm/features/jserrors/aggregate/index.js +12 -3
- package/dist/esm/features/logging/aggregate/index.js +12 -8
- package/dist/esm/features/metrics/aggregate/index.js +5 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +71 -32
- package/dist/esm/features/page_view_timing/aggregate/index.js +6 -1
- package/dist/esm/features/session_replay/aggregate/index.js +39 -29
- package/dist/esm/features/session_trace/aggregate/index.js +36 -30
- package/dist/esm/features/session_trace/constants.js +16 -2
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +36 -5
- package/dist/esm/features/soft_navigations/aggregate/index.js +7 -1
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +6 -4
- package/dist/esm/features/utils/aggregate-base.js +4 -3
- package/dist/esm/loaders/api/register.js +26 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +4 -0
- package/dist/types/common/config/init-types.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/constants/events.d.ts +16 -0
- package/dist/types/common/constants/events.d.ts.map +1 -0
- package/dist/types/common/harvest/harvester.d.ts +2 -16
- package/dist/types/common/harvest/harvester.d.ts.map +1 -1
- package/dist/types/common/harvest/send.d.ts +9 -0
- package/dist/types/common/harvest/send.d.ts.map +1 -0
- package/dist/types/common/harvest/types.d.ts +39 -10
- package/dist/types/common/harvest/types.d.ts.map +1 -1
- package/dist/types/common/payloads/payloads.d.ts +42 -0
- package/dist/types/common/payloads/payloads.d.ts.map +1 -0
- package/dist/types/common/serialize/bel-serializer.d.ts +1 -1
- package/dist/types/common/serialize/bel-serializer.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/util/obfuscate.d.ts +23 -2
- package/dist/types/common/util/obfuscate.d.ts.map +1 -1
- package/dist/types/common/v2/mfe-vitals.d.ts +14 -0
- package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -0
- package/dist/types/common/wrap/wrap-websocket.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/gql.d.ts +8 -0
- package/dist/types/features/ajax/aggregate/gql.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts +2 -0
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/ajax/constants.d.ts +5 -0
- package/dist/types/features/ajax/constants.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +3 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +8 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +3 -1
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +2 -0
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -0
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -0
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +4 -2
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +3 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/constants.d.ts +17 -0
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +6 -1
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +3 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +0 -1
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/common/config/configurable.js +1 -1
- package/src/common/config/init-types.js +1 -0
- package/src/common/config/init.js +2 -1
- package/src/common/constants/events.js +20 -0
- package/src/common/harvest/harvester.js +9 -201
- package/src/common/harvest/send.js +209 -0
- package/src/common/harvest/types.js +9 -5
- package/src/common/payloads/payloads.js +135 -0
- package/src/common/serialize/bel-serializer.js +4 -3
- package/src/common/session/session-entity.js +8 -9
- package/src/common/util/data-size.js +2 -2
- package/src/common/util/obfuscate.js +154 -3
- package/src/common/v2/mfe-vitals.js +260 -0
- package/src/common/wrap/wrap-websocket.js +2 -1
- package/src/common/wrap/wrap-xhr.js +1 -1
- package/src/features/ajax/aggregate/gql.js +42 -1
- package/src/features/ajax/aggregate/index.js +43 -13
- package/src/features/ajax/constants.js +5 -1
- package/src/features/ajax/instrument/index.js +114 -18
- package/src/features/generic_events/aggregate/index.js +14 -9
- package/src/features/generic_events/constants.js +3 -2
- package/src/features/jserrors/aggregate/format-stack-trace.js +3 -3
- package/src/features/jserrors/aggregate/index.js +12 -3
- package/src/features/logging/aggregate/index.js +12 -11
- package/src/features/metrics/aggregate/index.js +5 -1
- package/src/features/page_view_event/aggregate/index.js +58 -29
- package/src/features/page_view_timing/aggregate/index.js +6 -1
- package/src/features/session_replay/aggregate/index.js +37 -28
- package/src/features/session_trace/aggregate/index.js +37 -30
- package/src/features/session_trace/constants.js +7 -1
- package/src/features/soft_navigations/aggregate/ajax-node.js +26 -5
- package/src/features/soft_navigations/aggregate/index.js +7 -1
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
- package/src/features/soft_navigations/aggregate/interaction.js +6 -4
- package/src/features/utils/aggregate-base.js +3 -3
- package/src/loaders/api/register.js +24 -4
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
6
|
-
import { FEATURE_NAME } from '../constants'
|
|
6
|
+
import { ABORT_REASONS, FEATURE_NAME } from '../constants'
|
|
7
7
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
8
8
|
import { TraceStorage } from './trace/storage'
|
|
9
9
|
import { obj as encodeObj } from '../../../common/url/encode'
|
|
10
10
|
import { globalScope } from '../../../common/constants/runtime'
|
|
11
11
|
import { MODE, SESSION_EVENTS } from '../../../common/session/constants'
|
|
12
|
-
import {
|
|
12
|
+
import { Obfuscator } from '../../../common/util/obfuscate'
|
|
13
13
|
import { cleanURL } from '../../../common/url/clean-url'
|
|
14
14
|
import { warn } from '../../../common/util/console'
|
|
15
|
+
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
15
16
|
|
|
16
17
|
/** Reserved room for query param attrs */
|
|
17
18
|
const QUERY_PARAM_PADDING = 5000
|
|
@@ -22,6 +23,9 @@ export class Aggregate extends AggregateBase {
|
|
|
22
23
|
super(agentRef, FEATURE_NAME)
|
|
23
24
|
this.harvestOpts.raw = true
|
|
24
25
|
|
|
26
|
+
// Create obfuscator for session trace nodes
|
|
27
|
+
this.obfuscator = new Obfuscator(agentRef, EVENT_TYPES.ST)
|
|
28
|
+
|
|
25
29
|
/** Tied to the entitlement flag response from BCS. Will short circuit operations of the agg if false */
|
|
26
30
|
this.entitled = undefined
|
|
27
31
|
/** A flag used to decide if the 30 node threshold should be ignored on the first harvest to ensure sending on the first payload */
|
|
@@ -39,8 +43,10 @@ export class Aggregate extends AggregateBase {
|
|
|
39
43
|
/** Sets up event listeners, and initializes this module to run in the correct "mode". Can be triggered from a few places, but makes an effort to only set up listeners once */
|
|
40
44
|
initialize (stMode, stEntitled, ignoreSession) {
|
|
41
45
|
this.entitled ??= stEntitled
|
|
42
|
-
if (!this.entitled)
|
|
43
|
-
|
|
46
|
+
if (!this.entitled) {
|
|
47
|
+
this.abort(ABORT_REASONS.ENTITLEMENTS)
|
|
48
|
+
return this.deregisterDrain()
|
|
49
|
+
}
|
|
44
50
|
this.timeKeeper ??= this.agentRef.runtime.timeKeeper
|
|
45
51
|
|
|
46
52
|
if (!this.initialized) {
|
|
@@ -51,7 +57,7 @@ export class Aggregate extends AggregateBase {
|
|
|
51
57
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
52
58
|
this.ee.on(SESSION_EVENTS.RESET, () => {
|
|
53
59
|
if (this.blocked) return
|
|
54
|
-
this.abort(
|
|
60
|
+
this.abort(ABORT_REASONS.RESET)
|
|
55
61
|
})
|
|
56
62
|
// The SessionEntity can have updates (locally or across tabs for SR mode changes), (across tabs for ST mode changes).
|
|
57
63
|
// Those updates should be sync'd here to ensure this page also honors the mode after initialization
|
|
@@ -60,7 +66,7 @@ export class Aggregate extends AggregateBase {
|
|
|
60
66
|
// this will only have an effect if ST is NOT already in full mode
|
|
61
67
|
if (this.mode !== MODE.FULL && (sessionState.sessionReplayMode === MODE.FULL || sessionState.sessionTraceMode === MODE.FULL)) this.switchToFull()
|
|
62
68
|
// if another page's session entity has expired, or another page has transitioned to off and this one hasn't... we can just abort straight away here
|
|
63
|
-
if (this.sessionId !== sessionState.value || (eventType === 'cross-tab' && sessionState.sessionTraceMode === MODE.OFF)) this.abort(
|
|
69
|
+
if (this.sessionId !== sessionState.value || (eventType === 'cross-tab' && sessionState.sessionTraceMode === MODE.OFF)) this.abort(ABORT_REASONS.CROSS_TAB)
|
|
64
70
|
})
|
|
65
71
|
|
|
66
72
|
if (typeof PerformanceNavigationTiming !== 'undefined' && globalScope.performance?.getEntriesByType('navigation')?.length > 0) {
|
|
@@ -72,30 +78,31 @@ export class Aggregate extends AggregateBase {
|
|
|
72
78
|
|
|
73
79
|
/** ST/SR sampling flow in BCS - https://drive.google.com/file/d/19hwt2oft-8Hh4RrjpLqEXfpP_9wYBLcq/view?usp=sharing */
|
|
74
80
|
/** ST will run in the mode provided by BCS if the session IS NEW. If not... it will use the state of the session entity to determine what mode to run in */
|
|
75
|
-
if (
|
|
81
|
+
if (this.agentRef.runtime.session.state.sessionTraceMode !== null && !ignoreSession) this.mode = this.agentRef.runtime.session.state.sessionTraceMode
|
|
76
82
|
else this.mode = stMode
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (this.mode === MODE.ERROR) this.switchToFull()
|
|
95
|
-
}, this.featureName, this.ee)
|
|
84
|
+
if (this.mode !== MODE.OFF) {
|
|
85
|
+
/** The handlers set up by the Inst file */
|
|
86
|
+
registerHandler('bst', (...args) => this.traceStorage.storeEvent(...args), this.featureName, this.ee)
|
|
87
|
+
registerHandler('bstResource', (...args) => this.traceStorage.storeResources(...args), this.featureName, this.ee)
|
|
88
|
+
registerHandler('bstHist', (...args) => this.traceStorage.storeHist(...args), this.featureName, this.ee)
|
|
89
|
+
registerHandler('bstXhrAgg', (...args) => this.traceStorage.storeXhrAgg(...args), this.featureName, this.ee)
|
|
90
|
+
registerHandler('bstApi', (...args) => this.traceStorage.storeNode(...args), this.featureName, this.ee)
|
|
91
|
+
registerHandler('trace-jserror', (...args) => this.traceStorage.storeErrorAgg(...args), this.featureName, this.ee)
|
|
92
|
+
registerHandler('pvtAdded', (...args) => this.traceStorage.processPVT(...args), this.featureName, this.ee)
|
|
93
|
+
|
|
94
|
+
if (this.mode === MODE.ERROR) {
|
|
95
|
+
/** A separate handler for noticing errors, and switching to "full" mode if running in "error" mode */
|
|
96
|
+
registerHandler('trace-jserror', () => {
|
|
97
|
+
if (this.mode === MODE.ERROR) this.switchToFull()
|
|
98
|
+
}, this.featureName, this.ee)
|
|
99
|
+
}
|
|
96
100
|
}
|
|
97
101
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
98
|
-
|
|
102
|
+
|
|
103
|
+
/** If the mode is off, we do not want to hold up draining for other features, so we deregister the feature for now.
|
|
104
|
+
* If it drains later (due to a mode change), data and handlers will instantly drain instead of waiting for the registry. */
|
|
105
|
+
this.mode === MODE.OFF ? this.deregisterDrain() : this.drain()
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
preHarvestChecks () {
|
|
@@ -104,7 +111,7 @@ export class Aggregate extends AggregateBase {
|
|
|
104
111
|
if (!this.agentRef.runtime.session) return // session entity is required for trace to run and continue running
|
|
105
112
|
if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
|
|
106
113
|
// If something unexpected happened and we somehow still got to harvesting after a session identifier changed, we should force-exit instead of harvesting:
|
|
107
|
-
this.abort(
|
|
114
|
+
this.abort(ABORT_REASONS.SESSION_CHANGED)
|
|
108
115
|
return
|
|
109
116
|
}
|
|
110
117
|
|
|
@@ -114,7 +121,7 @@ export class Aggregate extends AggregateBase {
|
|
|
114
121
|
serializer (stns) {
|
|
115
122
|
if (!stns.length) return // there are no processed nodes
|
|
116
123
|
this.everHarvested = true
|
|
117
|
-
return
|
|
124
|
+
return this.obfuscator.traverseAndObfuscateEvents(stns)
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
queryStringsBuilder (stns) {
|
|
@@ -181,8 +188,8 @@ export class Aggregate extends AggregateBase {
|
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
/** Stop running for the remainder of the page lifecycle */
|
|
184
|
-
abort (
|
|
185
|
-
warn(60,
|
|
191
|
+
abort (reason = {}) {
|
|
192
|
+
warn(60, reason.message)
|
|
186
193
|
this.blocked = true
|
|
187
194
|
this.mode = MODE.OFF
|
|
188
195
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
@@ -14,3 +14,9 @@ export const FN_END = 'fn' + END
|
|
|
14
14
|
export const PUSH_STATE = 'pushState'
|
|
15
15
|
export const MAX_NODES_PER_HARVEST = 1000
|
|
16
16
|
export const ERROR_MODE_SECONDS_WINDOW = 30 * 1000 // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
|
|
17
|
+
export const ABORT_REASONS = {
|
|
18
|
+
CROSS_TAB: { message: 'Session Entity was set to OFF on another tab' },
|
|
19
|
+
ENTITLEMENTS: { message: 'Session Trace is not allowed and will not be started' },
|
|
20
|
+
RESET: { message: 'Session was reset' },
|
|
21
|
+
SESSION_CHANGED: { message: 'Session identifier changed unexpectedly' }
|
|
22
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { addCustomAttributes, getAddStringContext, nullable, numeric } from '../../../common/serialize/bel-serializer'
|
|
6
|
+
import { createStringAdders } from '../../../common/payloads/payloads'
|
|
6
7
|
import { AJAX_ID } from '../../ajax/constants'
|
|
7
8
|
import { NODE_TYPE } from '../constants'
|
|
8
9
|
import { BelNode } from './bel-node'
|
|
@@ -25,6 +26,13 @@ export class AjaxNode extends BelNode {
|
|
|
25
26
|
this.targetAttributes = ajaxEvent.targetAttributes
|
|
26
27
|
this[AJAX_ID] = ajaxEvent[AJAX_ID] // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
27
28
|
|
|
29
|
+
// optional payload metadata attributes
|
|
30
|
+
this.requestBody = ajaxEvent.requestBody
|
|
31
|
+
this.requestHeaders = ajaxEvent.requestHeaders
|
|
32
|
+
this.requestQuery = ajaxEvent.requestQuery
|
|
33
|
+
this.responseBody = ajaxEvent.responseBody
|
|
34
|
+
this.responseHeaders = ajaxEvent.responseHeaders
|
|
35
|
+
|
|
28
36
|
this.start = ajaxEvent.startTime
|
|
29
37
|
this.end = ajaxEvent.endTime
|
|
30
38
|
if (ajaxContext?.latestLongtaskEnd) {
|
|
@@ -33,8 +41,9 @@ export class AjaxNode extends BelNode {
|
|
|
33
41
|
} else this.callbackEnd = this.end // if no long task was observed, callbackEnd is the same as end
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
serialize (parentStartTimestamp, agentRef) {
|
|
37
|
-
const addString = getAddStringContext
|
|
44
|
+
serialize (parentStartTimestamp, agentRef, ajaxObfuscator) {
|
|
45
|
+
const { addString, addStringWithTruncation } = createStringAdders(getAddStringContext, ajaxObfuscator)
|
|
46
|
+
|
|
38
47
|
const nodeList = []
|
|
39
48
|
|
|
40
49
|
// IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
|
|
@@ -55,11 +64,23 @@ export class AjaxNode extends BelNode {
|
|
|
55
64
|
addString(this.nodeId),
|
|
56
65
|
nullable(this.spanId, addString, true) + nullable(this.traceId, addString, true) + nullable(this.spanTimestamp, numeric)
|
|
57
66
|
]
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
// Regular attributes: obfuscate only
|
|
68
|
+
const regularAttrs = addCustomAttributes({
|
|
69
|
+
[AJAX_ID]: this[AJAX_ID],
|
|
60
70
|
...(this.targetAttributes || {}),
|
|
61
|
-
|
|
71
|
+
...(this.gql || {})
|
|
62
72
|
}, addString)
|
|
73
|
+
|
|
74
|
+
// Payload attributes: obfuscate then truncate
|
|
75
|
+
const payloadAttrs = addCustomAttributes({
|
|
76
|
+
...(this.requestBody ? { requestBody: this.requestBody } : {}),
|
|
77
|
+
...(this.requestHeaders ? { requestHeaders: this.requestHeaders } : {}),
|
|
78
|
+
...(this.requestQuery ? { requestQuery: this.requestQuery } : {}),
|
|
79
|
+
...(this.responseBody ? { responseBody: this.responseBody } : {}),
|
|
80
|
+
...(this.responseHeaders ? { responseHeaders: this.responseHeaders } : {})
|
|
81
|
+
}, addStringWithTruncation)
|
|
82
|
+
|
|
83
|
+
let allAttachedNodes = [...regularAttrs, ...payloadAttrs]
|
|
63
84
|
this.children.forEach(node => allAttachedNodes.push(node.serialize())) // no children is expected under ajax nodes at this time
|
|
64
85
|
|
|
65
86
|
fields[1] = numeric(allAttachedNodes.length)
|
|
@@ -8,10 +8,12 @@ import { webdriverDetected } from '../../../common/util/webdriver-detection'
|
|
|
8
8
|
import { loadTime } from '../../../common/vitals/load-time'
|
|
9
9
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
10
10
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
11
|
+
import { Obfuscator } from '../../../common/util/obfuscate'
|
|
11
12
|
import { API_TRIGGER_NAME, FEATURE_NAME, INTERACTION_STATUS, INTERACTION_TRIGGERS, IPL_TRIGGER_NAME, NO_LONG_TASK_WINDOW, POPSTATE_MERGE_WINDOW, POPSTATE_TRIGGER } from '../constants'
|
|
12
13
|
import { AjaxNode } from './ajax-node'
|
|
13
14
|
import { InitialPageLoadInteraction } from './initial-page-load-interaction'
|
|
14
15
|
import { Interaction } from './interaction'
|
|
16
|
+
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
15
17
|
|
|
16
18
|
export class Aggregate extends AggregateBase {
|
|
17
19
|
static featureName = FEATURE_NAME
|
|
@@ -20,6 +22,10 @@ export class Aggregate extends AggregateBase {
|
|
|
20
22
|
|
|
21
23
|
super.customAttributesAreSeparate = true
|
|
22
24
|
|
|
25
|
+
// Create obfuscators for browser interactions and nested AJAX requests
|
|
26
|
+
this.interactionObfuscator = new Obfuscator(agentRef, EVENT_TYPES.BI)
|
|
27
|
+
this.ajaxObfuscator = new Obfuscator(agentRef, EVENT_TYPES.AJAX)
|
|
28
|
+
|
|
23
29
|
this.interactionsToHarvest = this.events
|
|
24
30
|
this.domObserver = domObserver
|
|
25
31
|
|
|
@@ -86,7 +92,7 @@ export class Aggregate extends AggregateBase {
|
|
|
86
92
|
let firstIxnStartTime
|
|
87
93
|
const serializedIxnList = []
|
|
88
94
|
for (const interaction of eventBuffer) {
|
|
89
|
-
serializedIxnList.push(interaction.serialize(firstIxnStartTime, this.agentRef))
|
|
95
|
+
serializedIxnList.push(interaction.serialize(firstIxnStartTime, this.agentRef, this.interactionObfuscator, this.ajaxObfuscator))
|
|
90
96
|
if (firstIxnStartTime === undefined) firstIxnStartTime = Math.floor(interaction.start) // careful not to match or overwrite on 0 value!
|
|
91
97
|
}
|
|
92
98
|
return `bel.7;${serializedIxnList.join(';')}`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { globalScope, initialLocation } from '../../../common/constants/runtime'
|
|
@@ -133,11 +133,13 @@ export class Interaction extends BelNode {
|
|
|
133
133
|
* Serializes (BEL) the interaction data for transmission.
|
|
134
134
|
* @param {Number} firstStartTimeOfPayload timestamp
|
|
135
135
|
* @param {Agent} agentRef Pass in the agent reference directly so that the event itself doesnt need to store the pointers and ruin the evaluation of the event size by including unused object references.
|
|
136
|
+
* @param {Obfuscator} interactionObfuscator Obfuscator for BrowserInteraction event type
|
|
137
|
+
* @param {Obfuscator} ajaxObfuscator Obfuscator for AjaxRequest event type (passed to child AjaxNodes)
|
|
136
138
|
* @returns {String} A string that is the serialized representation of this interaction.
|
|
137
139
|
*/
|
|
138
|
-
serialize (firstStartTimeOfPayload, agentRef) {
|
|
140
|
+
serialize (firstStartTimeOfPayload, agentRef, interactionObfuscator, ajaxObfuscator) {
|
|
139
141
|
const isFirstIxnOfPayload = firstStartTimeOfPayload === undefined
|
|
140
|
-
const addString = getAddStringContext(
|
|
142
|
+
const addString = getAddStringContext(interactionObfuscator)
|
|
141
143
|
const nodeList = []
|
|
142
144
|
let ixnType
|
|
143
145
|
if (this.trigger === IPL_TRIGGER_NAME) ixnType = INTERACTION_TYPE.INITIAL_PAGE_LOAD
|
|
@@ -170,7 +172,7 @@ export class Interaction extends BelNode {
|
|
|
170
172
|
/* Querypack encoder+decoder quirkiness:
|
|
171
173
|
- If first ixn node of payload is being processed, its children's start time must be offset by this node's start. (firstStartTime should be undefined.)
|
|
172
174
|
- Else for subsequent ixns in the same payload, we go back to using that first ixn node's start to offset their children's start. */
|
|
173
|
-
this.children.forEach(node => allAttachedNodes.push(node.serialize(isFirstIxnOfPayload ? this.start : firstStartTimeOfPayload, agentRef))) // recursively add the serialized string of every child of this (ixn) bel node
|
|
175
|
+
this.children.forEach(node => allAttachedNodes.push(node.serialize(isFirstIxnOfPayload ? this.start : firstStartTimeOfPayload, agentRef, ajaxObfuscator))) // recursively add the serialized string of every child of this (ixn) bel node
|
|
174
176
|
|
|
175
177
|
fields[1] = numeric(allAttachedNodes.length)
|
|
176
178
|
nodeList.push(fields)
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { FeatureBase } from './feature-base'
|
|
6
6
|
import { drain } from '../../common/drain/drain'
|
|
7
|
-
import { Obfuscator } from '../../common/util/obfuscate'
|
|
8
7
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
9
8
|
import { Harvester } from '../../common/harvest/harvester'
|
|
10
9
|
import { EventBuffer } from './event-buffer'
|
|
@@ -12,6 +11,7 @@ import { handle } from '../../common/event-emitter/handle'
|
|
|
12
11
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../metrics/constants'
|
|
13
12
|
import { EventAggregator } from '../../common/aggregate/event-aggregator'
|
|
14
13
|
import { MAX_PAYLOAD_SIZE, IDEAL_PAYLOAD_SIZE, SUPPORTS_REGISTERED_ENTITIES } from '../../common/constants/agent-constants'
|
|
14
|
+
import { warn } from '../../common/util/console'
|
|
15
15
|
|
|
16
16
|
export class AggregateBase extends FeatureBase {
|
|
17
17
|
/**
|
|
@@ -115,6 +115,7 @@ export class AggregateBase extends FeatureBase {
|
|
|
115
115
|
}
|
|
116
116
|
function buildOutput (ref) {
|
|
117
117
|
return flagNames.map(flag => {
|
|
118
|
+
if (ref[flag] === undefined || ref[flag] === null) warn(71, flag)
|
|
118
119
|
if (!ref[flag]) return 0
|
|
119
120
|
return ref[flag]
|
|
120
121
|
})
|
|
@@ -182,8 +183,7 @@ export class AggregateBase extends FeatureBase {
|
|
|
182
183
|
* This method should run after checkConfiguration, which may reset the agent's info/runtime object that is used here.
|
|
183
184
|
*/
|
|
184
185
|
doOnceForAllAggregate (agentRef) {
|
|
185
|
-
|
|
186
|
-
this.obfuscator = agentRef.runtime.obfuscator
|
|
186
|
+
// Note: obfuscator is now created per-feature for their specific event types
|
|
187
187
|
|
|
188
188
|
if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
|
|
189
189
|
}
|
|
@@ -18,6 +18,7 @@ import { measure } from './measure'
|
|
|
18
18
|
import { recordCustomEvent } from './recordCustomEvent'
|
|
19
19
|
import { subscribeToPageUnload } from '../../common/window/page-visibility'
|
|
20
20
|
import { findScriptTimings } from '../../common/v2/script-tracker'
|
|
21
|
+
import { trackMFEVitals } from '../../common/v2/mfe-vitals'
|
|
21
22
|
import { generateRandomHexString } from '../../common/ids/unique-id'
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -83,6 +84,9 @@ function register (agentRef, target) {
|
|
|
83
84
|
|
|
84
85
|
const timings = findScriptTimings()
|
|
85
86
|
|
|
87
|
+
// Track MFE vitals for this entity
|
|
88
|
+
const vitals = trackMFEVitals(target.id)
|
|
89
|
+
|
|
86
90
|
const attrs = {}
|
|
87
91
|
|
|
88
92
|
// Only define attributes getter if it doesn't already exist
|
|
@@ -149,7 +153,8 @@ function register (agentRef, target) {
|
|
|
149
153
|
metadata: {
|
|
150
154
|
get customAttributes () { return attrs },
|
|
151
155
|
target,
|
|
152
|
-
timings
|
|
156
|
+
timings,
|
|
157
|
+
vitals
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -177,9 +182,18 @@ function register (agentRef, target) {
|
|
|
177
182
|
// only ever report the timings the first time this is called
|
|
178
183
|
if (timings.reportedAt) return
|
|
179
184
|
timings.reportedAt = now()
|
|
185
|
+
|
|
186
|
+
// Disconnect observers and capture current values, store in timings obj for visibility
|
|
187
|
+
vitals.disconnect()
|
|
188
|
+
timings.fcp = vitals.fcp
|
|
189
|
+
timings.lcp = vitals.lcp
|
|
190
|
+
timings.cls = vitals.cls
|
|
191
|
+
timings.inp = vitals.inp
|
|
192
|
+
|
|
180
193
|
const timeToFetch = timings.fetchEnd - timings.fetchStart // fetchStart to fetchEnd
|
|
181
194
|
const timeToExecute = timings.scriptEnd - timings.scriptStart // scriptStart to scriptEnd
|
|
182
|
-
|
|
195
|
+
|
|
196
|
+
const eventData = {
|
|
183
197
|
assetUrl: timings.asset, // the url of the script that was registered, or undefined if it could not be determined (inline or no match)
|
|
184
198
|
assetType: timings.type, // the type of asset that was associated with the timings, one of 'script', 'link' (if preloaded and found in the resource timing buffer), 'preload' (if preloaded but not found in the resource timing buffer), or "unknown" if it could not be determined
|
|
185
199
|
timeAlive: timings.reportedAt - timings.registeredAt, // registeredAt to reportedAt
|
|
@@ -187,8 +201,14 @@ function register (agentRef, target) {
|
|
|
187
201
|
timeToExecute, // scriptStart to scriptEnd
|
|
188
202
|
timeToFetch, // fetchStart to fetchEnd
|
|
189
203
|
timeToLoad: timeToFetch + timeToExecute, // fetch time and script time together
|
|
190
|
-
timeToRegister: timings.registeredAt // timestamp when register() was called
|
|
191
|
-
|
|
204
|
+
timeToRegister: timings.registeredAt, // timestamp when register() was called
|
|
205
|
+
'nr.vitals.fcp': vitals.fcp || null, // FCP vital object with value and metadata
|
|
206
|
+
'nr.vitals.lcp': vitals.lcp || null, // LCP vital object with value and metadata
|
|
207
|
+
'nr.vitals.cls': vitals.cls || null, // CLS vital object with value and metadata
|
|
208
|
+
'nr.vitals.inp': vitals.inp || null // INP vital object with value and metadata
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
api.recordCustomEvent('MicroFrontEndTiming', eventData)
|
|
192
212
|
}
|
|
193
213
|
|
|
194
214
|
/**
|