@newrelic/browser-agent 1.301.0-rc.1 → 1.301.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/config/init-types.js +1 -1
- package/dist/cjs/common/config/init.js +7 -1
- package/dist/cjs/common/config/runtime.js +1 -1
- package/dist/cjs/common/constants/agent-constants.js +11 -2
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/drain/drain.js +1 -1
- package/dist/cjs/common/harvest/harvester.js +30 -39
- package/dist/cjs/common/util/mfe.js +45 -0
- package/dist/cjs/features/generic_events/aggregate/index.js +9 -8
- package/dist/cjs/features/generic_events/constants.js +3 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +18 -17
- package/dist/cjs/features/logging/aggregate/index.js +19 -15
- package/dist/cjs/features/logging/shared/utils.js +3 -3
- package/dist/cjs/features/page_view_event/aggregate/index.js +3 -33
- package/dist/cjs/features/session_replay/aggregate/index.js +13 -13
- package/dist/cjs/features/session_replay/shared/recorder.js +3 -2
- package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
- package/dist/cjs/features/soft_navigations/aggregate/index.js +1 -2
- package/dist/cjs/features/utils/aggregate-base.js +45 -47
- package/dist/cjs/loaders/api/addPageAction.js +2 -2
- package/dist/cjs/loaders/api/log.js +2 -2
- package/dist/cjs/loaders/api/noticeError.js +2 -2
- package/dist/cjs/loaders/api/register-api-types.js +5 -5
- package/dist/cjs/loaders/api/register.js +80 -97
- package/dist/esm/common/config/init-types.js +1 -1
- package/dist/esm/common/config/init.js +7 -1
- package/dist/esm/common/config/runtime.js +1 -1
- package/dist/esm/common/constants/agent-constants.js +9 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/drain/drain.js +1 -1
- package/dist/esm/common/harvest/harvester.js +30 -39
- package/dist/esm/common/util/mfe.js +38 -0
- package/dist/esm/features/generic_events/aggregate/index.js +9 -8
- package/dist/esm/features/generic_events/constants.js +3 -1
- package/dist/esm/features/jserrors/aggregate/index.js +18 -17
- package/dist/esm/features/logging/aggregate/index.js +19 -15
- package/dist/esm/features/logging/shared/utils.js +3 -3
- package/dist/esm/features/page_view_event/aggregate/index.js +3 -33
- package/dist/esm/features/session_replay/aggregate/index.js +13 -13
- package/dist/esm/features/session_replay/shared/recorder.js +3 -2
- package/dist/esm/features/session_trace/aggregate/index.js +0 -2
- package/dist/esm/features/soft_navigations/aggregate/index.js +1 -2
- package/dist/esm/features/utils/aggregate-base.js +46 -48
- package/dist/esm/loaders/api/addPageAction.js +2 -2
- package/dist/esm/loaders/api/log.js +2 -2
- package/dist/esm/loaders/api/noticeError.js +2 -2
- package/dist/esm/loaders/api/register-api-types.js +5 -5
- package/dist/esm/loaders/api/register.js +80 -97
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +4 -1
- 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/agent-constants.d.ts +7 -4
- package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
- package/dist/types/common/harvest/harvester.d.ts +2 -2
- package/dist/types/common/harvest/harvester.d.ts.map +1 -1
- package/dist/types/common/util/mfe.d.ts +20 -0
- package/dist/types/common/util/mfe.d.ts.map +1 -0
- package/dist/types/features/generic_events/aggregate/index.d.ts +2 -2
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +1 -0
- package/dist/types/features/jserrors/aggregate/index.d.ts +3 -3
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +3 -3
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/shared/utils.d.ts +2 -2
- package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -4
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +13 -4
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +13 -5
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/loaders/api/addPageAction.d.ts +1 -1
- package/dist/types/loaders/api/addPageAction.d.ts.map +1 -1
- package/dist/types/loaders/api/log.d.ts +1 -1
- package/dist/types/loaders/api/log.d.ts.map +1 -1
- package/dist/types/loaders/api/noticeError.d.ts +1 -1
- package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
- package/dist/types/loaders/api/register-api-types.d.ts +4 -4
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts +8 -1
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/init-types.js +1 -1
- package/src/common/config/init.js +3 -1
- package/src/common/config/runtime.js +1 -1
- package/src/common/constants/agent-constants.js +10 -0
- package/src/common/drain/drain.js +1 -1
- package/src/common/harvest/harvester.js +27 -32
- package/src/common/util/mfe.js +35 -0
- package/src/features/generic_events/aggregate/index.js +9 -8
- package/src/features/generic_events/constants.js +3 -1
- package/src/features/jserrors/aggregate/index.js +17 -17
- package/src/features/logging/aggregate/index.js +20 -13
- package/src/features/logging/shared/utils.js +3 -3
- package/src/features/page_view_event/aggregate/index.js +3 -28
- package/src/features/session_replay/aggregate/index.js +12 -10
- package/src/features/session_replay/shared/recorder.js +3 -2
- package/src/features/session_trace/aggregate/index.js +0 -2
- package/src/features/soft_navigations/aggregate/index.js +1 -2
- package/src/features/utils/aggregate-base.js +47 -42
- package/src/loaders/api/addPageAction.js +2 -2
- package/src/loaders/api/log.js +2 -2
- package/src/loaders/api/noticeError.js +2 -2
- package/src/loaders/api/register-api-types.js +5 -5
- package/src/loaders/api/register.js +62 -89
- package/dist/cjs/common/util/target.js +0 -34
- package/dist/cjs/features/utils/entity-manager.js +0 -46
- package/dist/cjs/features/utils/event-store-manager.js +0 -174
- package/dist/cjs/loaders/api/register-api.js +0 -165
- package/dist/esm/common/util/target.js +0 -27
- package/dist/esm/features/utils/entity-manager.js +0 -39
- package/dist/esm/features/utils/event-store-manager.js +0 -166
- package/dist/esm/loaders/api/register-api.js +0 -159
- package/dist/types/common/util/target.d.ts +0 -18
- package/dist/types/common/util/target.d.ts.map +0 -1
- package/dist/types/features/utils/entity-manager.d.ts +0 -11
- package/dist/types/features/utils/entity-manager.d.ts.map +0 -1
- package/dist/types/features/utils/event-store-manager.d.ts +0 -85
- package/dist/types/features/utils/event-store-manager.d.ts.map +0 -1
- package/dist/types/loaders/api/register-api.d.ts +0 -14
- package/dist/types/loaders/api/register-api.d.ts.map +0 -1
- package/src/common/util/target.js +0 -27
- package/src/features/utils/entity-manager.js +0 -45
- package/src/features/utils/event-store-manager.js +0 -165
- package/src/loaders/api/register-api.js +0 -152
|
@@ -10,10 +10,10 @@ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS, LOGGING_MODE }
|
|
|
10
10
|
import { Log } from '../shared/log'
|
|
11
11
|
import { isValidLogLevel } from '../shared/utils'
|
|
12
12
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
13
|
-
import { isContainerAgentTarget } from '../../../common/util/target'
|
|
14
13
|
import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../../../common/session/constants'
|
|
15
14
|
import { ABORT_REASONS } from '../../session_replay/constants'
|
|
16
15
|
import { canEnableSessionTracking } from '../../utils/feature-gates'
|
|
16
|
+
import { getVersion2Attributes } from '../../../common/util/mfe'
|
|
17
17
|
|
|
18
18
|
export class Aggregate extends AggregateBase {
|
|
19
19
|
static featureName = FEATURE_NAME
|
|
@@ -21,6 +21,8 @@ export class Aggregate extends AggregateBase {
|
|
|
21
21
|
super(agentRef, FEATURE_NAME)
|
|
22
22
|
this.isSessionTrackingEnabled = canEnableSessionTracking(agentRef.init) && agentRef.runtime.session
|
|
23
23
|
|
|
24
|
+
/** set up agg-level behaviors specific to this feature */
|
|
25
|
+
this.harvestOpts.raw = true
|
|
24
26
|
super.customAttributesAreSeparate = true
|
|
25
27
|
|
|
26
28
|
// 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.
|
|
@@ -34,7 +36,6 @@ export class Aggregate extends AggregateBase {
|
|
|
34
36
|
else this.loggingMode = data.loggingMode
|
|
35
37
|
})
|
|
36
38
|
|
|
37
|
-
this.harvestOpts.raw = true
|
|
38
39
|
this.waitForFlags(['log']).then(([loggingMode]) => {
|
|
39
40
|
const session = this.agentRef.runtime.session ?? {}
|
|
40
41
|
if (this.loggingMode === LOGGING_MODE.OFF || (session.isNew && loggingMode === LOGGING_MODE.OFF)) {
|
|
@@ -63,11 +64,17 @@ export class Aggregate extends AggregateBase {
|
|
|
63
64
|
})
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO,
|
|
67
|
-
if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName)
|
|
67
|
+
handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO, target) {
|
|
68
68
|
if (this.blocked || !this.loggingMode) return
|
|
69
69
|
|
|
70
70
|
if (!attributes || typeof attributes !== 'object') attributes = {}
|
|
71
|
+
|
|
72
|
+
attributes = {
|
|
73
|
+
...attributes,
|
|
74
|
+
/** Specific attributes only supplied if harvesting to endpoint version 2 */
|
|
75
|
+
...(getVersion2Attributes(target, this))
|
|
76
|
+
}
|
|
77
|
+
|
|
71
78
|
if (typeof level === 'string') level = level.toUpperCase()
|
|
72
79
|
if (!isValidLogLevel(level)) return warn(30, level)
|
|
73
80
|
if (this.loggingMode < (LOGGING_MODE[level] || Infinity)) {
|
|
@@ -100,25 +107,26 @@ export class Aggregate extends AggregateBase {
|
|
|
100
107
|
level
|
|
101
108
|
)
|
|
102
109
|
|
|
103
|
-
this.events.add(log
|
|
110
|
+
this.events.add(log)
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
serializer (eventBuffer
|
|
107
|
-
const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
|
|
113
|
+
serializer (eventBuffer) {
|
|
108
114
|
const sessionEntity = this.agentRef.runtime.session
|
|
109
115
|
return [{
|
|
110
116
|
common: {
|
|
111
117
|
/** Attributes in the `common` section are added to `all` logs generated in the payload */
|
|
112
118
|
attributes: {
|
|
113
119
|
...this.agentRef.info.jsAttributes, // user-provided custom attributes
|
|
114
|
-
|
|
120
|
+
...(this.harvestEndpointVersion === 1 && {
|
|
121
|
+
'entity.guid': this.agentRef.runtime.appMetadata.agents[0].entityGuid,
|
|
122
|
+
appId: this.agentRef.info.applicationID
|
|
123
|
+
}),
|
|
115
124
|
...(sessionEntity && {
|
|
116
125
|
session: sessionEntity.state.value || '0', // The session ID that we generate and keep across page loads
|
|
117
|
-
hasReplay: sessionEntity.state.sessionReplayMode === 1
|
|
126
|
+
hasReplay: sessionEntity.state.sessionReplayMode === 1, // True if a session replay recording is running
|
|
118
127
|
hasTrace: sessionEntity.state.sessionTraceMode === 1 // True if a session trace recording is running
|
|
119
128
|
}),
|
|
120
129
|
ptid: this.agentRef.runtime.ptid, // page trace id
|
|
121
|
-
appId: target.applicationID || this.agentRef.info.applicationID, // Application ID from info object,
|
|
122
130
|
standalone: Boolean(this.agentRef.info.sa), // copy paste (true) vs APM (false)
|
|
123
131
|
agentVersion: this.agentRef.runtime.version, // browser agent version
|
|
124
132
|
// The following 3 attributes are evaluated and dropped at ingest processing time and do not get stored on NRDB:
|
|
@@ -135,9 +143,8 @@ export class Aggregate extends AggregateBase {
|
|
|
135
143
|
}]
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
queryStringsBuilder (
|
|
139
|
-
|
|
140
|
-
return { browser_monitoring_key: target.licenseKey }
|
|
146
|
+
queryStringsBuilder () {
|
|
147
|
+
return { browser_monitoring_key: this.agentRef.info.licenseKey }
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
/** Abort the feature, once aborted it will not resume */
|
|
@@ -13,11 +13,11 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
|
|
|
13
13
|
* @param {string} message - the log message string
|
|
14
14
|
* @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
|
|
15
15
|
* @param {enum} level - the log level enum
|
|
16
|
-
* @param {object=}
|
|
16
|
+
* @param {object=} target - the optional target provided by an api call
|
|
17
17
|
*/
|
|
18
|
-
export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO,
|
|
18
|
+
export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO, target, timestamp = now()) {
|
|
19
19
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
|
|
20
|
-
handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level,
|
|
20
|
+
handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, target], undefined, FEATURE_NAMES.logging, ee)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -17,8 +17,6 @@ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
|
|
|
17
17
|
import { now } from '../../../common/timing/now'
|
|
18
18
|
import { TimeKeeper } from '../../../common/timing/time-keeper'
|
|
19
19
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
20
|
-
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
21
|
-
import { isContainerAgentTarget } from '../../../common/util/target'
|
|
22
20
|
|
|
23
21
|
export class Aggregate extends AggregateBase {
|
|
24
22
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
@@ -29,10 +27,6 @@ export class Aggregate extends AggregateBase {
|
|
|
29
27
|
this.firstByteToWindowLoad = 0 // our "frontend" duration
|
|
30
28
|
this.firstByteToDomContent = 0 // our "dom processing" duration
|
|
31
29
|
|
|
32
|
-
registerHandler('send-rum', (customAttibutes, target) => {
|
|
33
|
-
this.sendRum(customAttibutes, target)
|
|
34
|
-
}, this.featureName, this.ee)
|
|
35
|
-
|
|
36
30
|
if (!isValid(agentRef.info)) {
|
|
37
31
|
this.ee.abort()
|
|
38
32
|
return warn(43)
|
|
@@ -58,7 +52,7 @@ export class Aggregate extends AggregateBase {
|
|
|
58
52
|
*
|
|
59
53
|
* @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
|
|
60
54
|
* @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
|
|
61
|
-
* @param {*} target The target to harvest to
|
|
55
|
+
* @param {*} target The target to harvest to
|
|
62
56
|
*/
|
|
63
57
|
sendRum (customAttributes = this.agentRef.info.jsAttributes, target = { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }) {
|
|
64
58
|
const info = this.agentRef.info
|
|
@@ -122,7 +116,7 @@ export class Aggregate extends AggregateBase {
|
|
|
122
116
|
|
|
123
117
|
this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
124
118
|
directSend: {
|
|
125
|
-
|
|
119
|
+
target,
|
|
126
120
|
payload: { qs: queryParameters, body }
|
|
127
121
|
},
|
|
128
122
|
needResponse: true,
|
|
@@ -130,20 +124,16 @@ export class Aggregate extends AggregateBase {
|
|
|
130
124
|
})
|
|
131
125
|
}
|
|
132
126
|
|
|
133
|
-
postHarvestCleanup ({ status, responseText, xhr
|
|
127
|
+
postHarvestCleanup ({ status, responseText, xhr }) {
|
|
134
128
|
const rumEndTime = now()
|
|
135
129
|
let app, flags
|
|
136
130
|
try {
|
|
137
131
|
({ app, ...flags } = JSON.parse(responseText))
|
|
138
|
-
this.processEntities(app.agents, targetApp)
|
|
139
132
|
} catch (error) {
|
|
140
133
|
// wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
|
|
141
134
|
warn(53, error)
|
|
142
135
|
}
|
|
143
136
|
|
|
144
|
-
/** Only run agent-wide side-effects if the harvest was for the main agent */
|
|
145
|
-
if (!isContainerAgentTarget(targetApp, this.agentRef)) return
|
|
146
|
-
|
|
147
137
|
if (status >= 400 || status === 0) {
|
|
148
138
|
warn(18, status)
|
|
149
139
|
// Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
|
|
@@ -176,19 +166,4 @@ export class Aggregate extends AggregateBase {
|
|
|
176
166
|
this.agentRef.runtime.harvester.startTimer()
|
|
177
167
|
activateFeatures(flags, this.agentRef)
|
|
178
168
|
}
|
|
179
|
-
|
|
180
|
-
processEntities (entities, targetApp) {
|
|
181
|
-
if (!entities || !targetApp) return
|
|
182
|
-
entities.forEach(agent => {
|
|
183
|
-
const entityManager = this.agentRef.runtime.entityManager
|
|
184
|
-
const entityGuid = agent.entityGuid
|
|
185
|
-
const entity = entityManager.get(entityGuid)
|
|
186
|
-
if (entity) return // already processed
|
|
187
|
-
|
|
188
|
-
if (isContainerAgentTarget(targetApp, this.agentRef)) {
|
|
189
|
-
entityManager.setDefaultEntity({ ...targetApp, entityGuid })
|
|
190
|
-
}
|
|
191
|
-
entityManager.set(agent.entityGuid, { ...targetApp, entityGuid })
|
|
192
|
-
})
|
|
193
|
-
}
|
|
194
169
|
}
|
|
@@ -37,6 +37,8 @@ export class Aggregate extends AggregateBase {
|
|
|
37
37
|
this.gzipper = undefined
|
|
38
38
|
/** populated with the u8 string lib async */
|
|
39
39
|
this.u8 = undefined
|
|
40
|
+
/** flips to false if the compressor libraries cannot import */
|
|
41
|
+
this.shouldCompress = true
|
|
40
42
|
|
|
41
43
|
/** set by BCS response */
|
|
42
44
|
this.entitled = false
|
|
@@ -200,14 +202,15 @@ export class Aggregate extends AggregateBase {
|
|
|
200
202
|
this.gzipper = gzipSync
|
|
201
203
|
this.u8 = strToU8
|
|
202
204
|
} catch (err) {
|
|
203
|
-
|
|
205
|
+
this.shouldCompress = false
|
|
206
|
+
// compressor failed to load, but we can still try to record without compression as a last ditch effort
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
makeHarvestPayload (
|
|
208
|
-
|
|
209
|
-
if (this.
|
|
210
|
-
if (!this.recorder || !this.timeKeeper?.ready || !this.recorder.hasSeenSnapshot) return
|
|
210
|
+
makeHarvestPayload () {
|
|
211
|
+
if (this.mode !== MODE.FULL || this.blocked) return // harvests should only be made in FULL mode, and not if the feature is blocked
|
|
212
|
+
if (this.shouldCompress && !this.gzipper) return // if compression is enabled, but the libraries have not loaded, wait for them to load
|
|
213
|
+
if (!this.recorder || !this.timeKeeper?.ready || !(this.recorder.hasSeenSnapshot && this.recorder.hasSeenMeta)) return // if the recorder or the timekeeper is not ready, or the recorder has not yet seen a snapshot, do not harvest
|
|
211
214
|
|
|
212
215
|
const recorderEvents = this.recorder.getEvents()
|
|
213
216
|
// get the event type and use that to trigger another harvest if needed
|
|
@@ -216,7 +219,7 @@ export class Aggregate extends AggregateBase {
|
|
|
216
219
|
const payload = this.getHarvestContents(recorderEvents)
|
|
217
220
|
if (!payload.body.length) {
|
|
218
221
|
this.recorder.clearBuffer()
|
|
219
|
-
return
|
|
222
|
+
return
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
this.reportSupportabilityMetric('SessionReplay/Harvest/Attempts')
|
|
@@ -232,19 +235,18 @@ export class Aggregate extends AggregateBase {
|
|
|
232
235
|
|
|
233
236
|
if (len > MAX_PAYLOAD_SIZE) {
|
|
234
237
|
this.abort(ABORT_REASONS.TOO_BIG, len)
|
|
235
|
-
return
|
|
238
|
+
return
|
|
236
239
|
}
|
|
240
|
+
|
|
237
241
|
// TODO -- Gracefully handle the buffer for retries.
|
|
238
242
|
if (!this.agentRef.runtime.session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({ sessionReplaySentFirstChunk: true })
|
|
239
243
|
this.recorder.clearBuffer()
|
|
240
|
-
if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
241
|
-
payloadOutput.payload = payload
|
|
242
244
|
|
|
243
245
|
if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
|
|
244
246
|
warn(59, JSON.stringify(this.agentRef.runtime.session.state))
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
return
|
|
249
|
+
return payload
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
/**
|
|
@@ -47,6 +47,7 @@ export class Recorder {
|
|
|
47
47
|
this.backloggedEvents = new RecorderEvents(this.shouldFix)
|
|
48
48
|
/** Only set to true once a snapshot node has been processed. Used to block harvests from sending before we know we have a snapshot */
|
|
49
49
|
this.hasSeenSnapshot = false
|
|
50
|
+
this.hasSeenMeta = false
|
|
50
51
|
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
51
52
|
this.lastMeta = false
|
|
52
53
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
@@ -191,9 +192,9 @@ export class Recorder {
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
// meta event
|
|
194
|
-
this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
|
|
195
|
+
this.hasSeenMeta ||= this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
|
|
195
196
|
// snapshot event
|
|
196
|
-
this.
|
|
197
|
+
this.hasSeenSnapshot ||= this.events.hasSnapshot ||= event.type === RRWEB_EVENT_TYPES.FullSnapshot
|
|
197
198
|
|
|
198
199
|
//* dont let the EventBuffer class double evaluate the event data size, it's a performance burden and we have special reasons to do it outside the event buffer */
|
|
199
200
|
this.events.add(event, eventBytes)
|
|
@@ -97,8 +97,6 @@ export class Aggregate extends AggregateBase {
|
|
|
97
97
|
}
|
|
98
98
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
99
99
|
this.drain()
|
|
100
|
-
/** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
|
|
101
|
-
this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
preHarvestChecks () {
|
|
@@ -46,7 +46,6 @@ export class Aggregate extends AggregateBase {
|
|
|
46
46
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
47
47
|
if (spaOn) {
|
|
48
48
|
this.drain()
|
|
49
|
-
setTimeout(() => agentRef.runtime.harvester.triggerHarvestFor(this), 0) // send the IPL ixn on next tick, giving some time for any ajax to finish; we may want to just remove this?
|
|
50
49
|
} else {
|
|
51
50
|
this.blocked = true // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
|
|
52
51
|
this.deregisterDrain()
|
|
@@ -148,7 +147,7 @@ export class Aggregate extends AggregateBase {
|
|
|
148
147
|
*/
|
|
149
148
|
if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress
|
|
150
149
|
let saveIxn
|
|
151
|
-
const interactionsBuffer = this.interactionsToHarvest.get()
|
|
150
|
+
const interactionsBuffer = this.interactionsToHarvest.get()
|
|
152
151
|
if (!interactionsBuffer) return undefined // no interactions have been staged yet, so nothing to search through)
|
|
153
152
|
for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) { // reverse search for the latest completed interaction for efficiency
|
|
154
153
|
const finishedInteraction = interactionsBuffer[idx]
|
|
@@ -10,15 +10,12 @@ import { drain } from '../../common/drain/drain'
|
|
|
10
10
|
import { activatedFeatures } from '../../common/util/feature-flags'
|
|
11
11
|
import { Obfuscator } from '../../common/util/obfuscate'
|
|
12
12
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
13
|
-
import { EventStoreManager } from './event-store-manager'
|
|
14
13
|
import { Harvester } from '../../common/harvest/harvester'
|
|
15
|
-
import { warn } from '../../common/util/console'
|
|
16
|
-
import { EntityManager } from './entity-manager'
|
|
17
14
|
import { EventBuffer } from './event-buffer'
|
|
18
15
|
import { handle } from '../../common/event-emitter/handle'
|
|
19
16
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../metrics/constants'
|
|
20
17
|
import { EventAggregator } from '../../common/aggregate/event-aggregator'
|
|
21
|
-
import { IDEAL_PAYLOAD_SIZE } from '../../common/constants/agent-constants'
|
|
18
|
+
import { MAX_PAYLOAD_SIZE, IDEAL_PAYLOAD_SIZE, SUPPORTS_REGISTERED_ENTITIES } from '../../common/constants/agent-constants'
|
|
22
19
|
|
|
23
20
|
export class AggregateBase extends FeatureBase {
|
|
24
21
|
/**
|
|
@@ -41,16 +38,9 @@ export class AggregateBase extends FeatureBase {
|
|
|
41
38
|
|
|
42
39
|
this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/** wait for the entity guid from the rum response and use to it to further configure things to set the default entity to share an indexed entity with entityGuid */
|
|
48
|
-
this.ee.on('entity-added', entity => {
|
|
49
|
-
// not all event managers have this fn, like ST and SR
|
|
50
|
-
// this allows the lookup to work for the default and an entityGuid without creating two separate buffers
|
|
51
|
-
this.events?.setEventStore?.(entity.entityGuid)
|
|
52
|
-
})
|
|
53
|
-
}
|
|
41
|
+
this.#setupEventStore()
|
|
42
|
+
|
|
43
|
+
this.waitForDrain()
|
|
54
44
|
}
|
|
55
45
|
|
|
56
46
|
/**
|
|
@@ -58,7 +48,7 @@ export class AggregateBase extends FeatureBase {
|
|
|
58
48
|
* @param {string} entityGuid
|
|
59
49
|
* @returns {void}
|
|
60
50
|
*/
|
|
61
|
-
#setupEventStore (
|
|
51
|
+
#setupEventStore () {
|
|
62
52
|
if (this.events) return
|
|
63
53
|
switch (this.featureName) {
|
|
64
54
|
// SessionReplay has its own storage mechanisms.
|
|
@@ -67,17 +57,40 @@ export class AggregateBase extends FeatureBase {
|
|
|
67
57
|
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
68
58
|
case FEATURE_NAMES.jserrors:
|
|
69
59
|
case FEATURE_NAMES.metrics:
|
|
70
|
-
this.events = this.agentRef.sharedAggregator ??= new
|
|
60
|
+
this.events = this.agentRef.sharedAggregator ??= new EventAggregator()
|
|
71
61
|
break
|
|
72
|
-
/** All other features get EventBuffer
|
|
62
|
+
/** All other features get EventBuffer by default. Note: PVE is included here, but event buffer will always be empty so future harvests will still not happen by interval or EOL.
|
|
73
63
|
This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
|
|
74
64
|
Its easier to just keep an empty event buffer in place. */
|
|
75
65
|
default:
|
|
76
|
-
this.events = new
|
|
66
|
+
this.events = new EventBuffer(MAX_PAYLOAD_SIZE, this)
|
|
77
67
|
break
|
|
78
68
|
}
|
|
79
69
|
}
|
|
80
70
|
|
|
71
|
+
/** @type {Boolean} indicates if the feature supports registered entities and the harvest requirements therein. Also read by getter "harvestEndpointVersion". Controlled by feature flag in pre-release phase. */
|
|
72
|
+
get supportsRegisteredEntities () {
|
|
73
|
+
return this.featureName in SUPPORTS_REGISTERED_ENTITIES && (SUPPORTS_REGISTERED_ENTITIES[this.featureName] || this.agentRef.init.feature_flags.includes('register.' + this.featureName))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* the endpoint version the feature uses during harvests
|
|
78
|
+
* @type {number}
|
|
79
|
+
* @returns {boolean}
|
|
80
|
+
*/
|
|
81
|
+
get harvestEndpointVersion () {
|
|
82
|
+
return (this.supportsRegisteredEntities && !!this.agentRef.runtime.registeredEntities.length) ? 2 : 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
waitForDrain () {
|
|
86
|
+
/** emitted when the feature successfully drains */
|
|
87
|
+
this.ee.on('drain-' + this.featureName, () => {
|
|
88
|
+
/** make an immediate harvest for all the features to help with harvestability for pre-load dervied data on short lived pages */
|
|
89
|
+
if (!this.drained) setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this), 1)
|
|
90
|
+
this.drained = true
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
/**
|
|
82
95
|
* Evaluates whether a harvest should be made early by estimating the size of the current payload. Currently, this only happens if the event storage is EventBuffer, since that triggers this method directly.
|
|
83
96
|
* If conditions are met, a new harvest will be triggered immediately.
|
|
@@ -125,7 +138,6 @@ export class AggregateBase extends FeatureBase {
|
|
|
125
138
|
*/
|
|
126
139
|
drain () {
|
|
127
140
|
drain(this.agentIdentifier, this.featureName)
|
|
128
|
-
this.drained = true
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
preHarvestChecks (opts) {
|
|
@@ -136,44 +148,39 @@ export class AggregateBase extends FeatureBase {
|
|
|
136
148
|
* Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
|
|
137
149
|
* @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
|
|
138
150
|
* @param {object|undefined} opts - opts passed from the harvester to help form the payload
|
|
139
|
-
* @param {string} opts.
|
|
151
|
+
* @param {string} opts.target - the target app metadata
|
|
140
152
|
* @returns {Array} Final payload tagged with their targeting browser app. The value of `payload` can be undefined if there are no pending events for an app. This should be a minimum length of 1.
|
|
141
153
|
*/
|
|
142
154
|
makeHarvestPayload (shouldRetryOnFail = false, opts = {}) {
|
|
143
|
-
if (!this.events || this.events.isEmpty(this.harvestOpts
|
|
155
|
+
if (!this.events || this.events.isEmpty(this.harvestOpts)) return
|
|
144
156
|
// Other conditions and things to do when preparing harvest that is required.
|
|
145
157
|
if (this.preHarvestChecks && !this.preHarvestChecks(opts)) return
|
|
146
158
|
|
|
147
|
-
if (shouldRetryOnFail) this.events.save(this.harvestOpts
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
150
|
-
this.events.clear(this.harvestOpts
|
|
151
|
-
|
|
152
|
-
return returnedDataArr.map(({ targetApp, data }) => {
|
|
153
|
-
// A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
|
|
154
|
-
const body = this.serializer ? this.serializer(data, targetApp?.entityGuid) : data
|
|
155
|
-
const payload = {
|
|
156
|
-
body
|
|
157
|
-
}
|
|
158
|
-
// Constructs the payload `qs` for relevant features on harvest.
|
|
159
|
-
if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data, targetApp?.entityGuid)
|
|
159
|
+
if (shouldRetryOnFail) this.events.save(this.harvestOpts)
|
|
160
|
+
const data = this.events.get(this.harvestOpts)
|
|
161
|
+
if (!data) return
|
|
162
|
+
this.events.clear(this.harvestOpts)
|
|
160
163
|
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
// A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
|
|
165
|
+
const body = this.serializer ? this.serializer(data) : data
|
|
166
|
+
const payload = {
|
|
167
|
+
body
|
|
168
|
+
}
|
|
169
|
+
// Constructs the payload `qs` for relevant features on harvest.
|
|
170
|
+
if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data)
|
|
171
|
+
return payload
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
/**
|
|
166
175
|
* Cleanup task after a harvest.
|
|
167
176
|
* @param {object} result - the cbResult object from the harvester's send method
|
|
168
|
-
* @param {object=} result.targetApp - the target app object that was used to point the harvest to the correct app
|
|
169
|
-
* @param {string=} result.targetApp.entityGuid - the entity guid of the target app
|
|
170
177
|
* @param {boolean=} result.sent - whether the harvest was sent successfully
|
|
171
178
|
* @param {boolean=} result.retry - whether the harvest should be retried
|
|
172
179
|
*/
|
|
173
180
|
postHarvestCleanup (result = {}) {
|
|
174
181
|
this.isRetrying = result.sent && result.retry
|
|
175
|
-
if (this.isRetrying) this.events.reloadSave(this.harvestOpts
|
|
176
|
-
this.events.clearSave(this.harvestOpts
|
|
182
|
+
if (this.isRetrying) this.events.reloadSave(this.harvestOpts)
|
|
183
|
+
this.events.clearSave(this.harvestOpts)
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
/**
|
|
@@ -212,8 +219,6 @@ export class AggregateBase extends FeatureBase {
|
|
|
212
219
|
if (!agentRef.runtime.obfuscator) agentRef.runtime.obfuscator = new Obfuscator(agentRef)
|
|
213
220
|
this.obfuscator = agentRef.runtime.obfuscator
|
|
214
221
|
|
|
215
|
-
if (!agentRef.runtime.entityManager) agentRef.runtime.entityManager = new EntityManager(this.agentRef)
|
|
216
|
-
|
|
217
222
|
if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
|
|
218
223
|
}
|
|
219
224
|
|
|
@@ -12,6 +12,6 @@ export function setupAddPageActionAPI (agent) {
|
|
|
12
12
|
setupAPI(ADD_PAGE_ACTION, (name, attributes) => addPageAction(name, attributes, agent), agent)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function addPageAction (name, attributes, agentRef,
|
|
16
|
-
handle(prefix + ADD_PAGE_ACTION, [timestamp, name, attributes,
|
|
15
|
+
export function addPageAction (name, attributes, agentRef, target, timestamp = now()) {
|
|
16
|
+
handle(prefix + ADD_PAGE_ACTION, [timestamp, name, attributes, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
|
|
17
17
|
}
|
package/src/loaders/api/log.js
CHANGED
|
@@ -13,6 +13,6 @@ export function setupLogAPI (agent) {
|
|
|
13
13
|
setupAPI(LOG, (message, options) => log(message, options, agent), agent)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function log (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}, agentRef,
|
|
17
|
-
bufferLog(agentRef.ee, message, customAttributes, level,
|
|
16
|
+
export function log (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}, agentRef, target, timestamp = now()) {
|
|
17
|
+
bufferLog(agentRef.ee, message, customAttributes, level, target, timestamp)
|
|
18
18
|
}
|
|
@@ -12,8 +12,8 @@ export function setupNoticeErrorAPI (agent) {
|
|
|
12
12
|
setupAPI(NOTICE_ERROR, (err, customAttributes) => noticeError(err, customAttributes, agent), agent)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function noticeError (err, customAttributes, agentRef,
|
|
15
|
+
export function noticeError (err, customAttributes, agentRef, target, timestamp = now()) {
|
|
16
16
|
if (typeof err === 'string') err = new Error(err)
|
|
17
|
-
handle('err', [err, timestamp, false, customAttributes, agentRef.runtime.isRecording, undefined,
|
|
17
|
+
handle('err', [err, timestamp, false, customAttributes, agentRef.runtime.isRecording, undefined, target], undefined, FEATURE_NAMES.jserrors, agentRef.ee)
|
|
18
18
|
handle('uaErr', [], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
|
|
19
19
|
}
|
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
/**
|
|
18
18
|
* @typedef {Object} RegisterAPIConstructor
|
|
19
19
|
* @property {Object} opts - The options for the registered entity.
|
|
20
|
-
* @property {string} opts.
|
|
21
|
-
* @property {string} opts.
|
|
20
|
+
* @property {string} opts.id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
21
|
+
* @property {string} opts.name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* @typedef {Object} RegisterAPIMetadata
|
|
26
26
|
* @property {Object} customAttributes - The custom attributes for the registered entity.
|
|
27
27
|
* @property {Object} target - The options for the registered entity.
|
|
28
|
-
* @property {string} target.licenseKey - The license key for the registered entity.
|
|
29
|
-
* @property {string} target.
|
|
30
|
-
* @property {string} target.
|
|
28
|
+
* @property {string} target.licenseKey - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
29
|
+
* @property {string} target.id - The ID for the registered entity.
|
|
30
|
+
* @property {string} target.name - The name returned for the registered entity.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
export default {}
|