@newrelic/browser-agent 1.258.2 → 1.259.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 +7 -0
- package/dist/cjs/cdn/polyfills.js +3 -1
- 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/harvest-scheduler.js +4 -2
- package/dist/cjs/features/ajax/constants.js +3 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +2 -1
- package/dist/cjs/features/metrics/aggregate/index.js +0 -8
- package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +31 -36
- package/dist/cjs/features/session_replay/constants.js +5 -2
- package/dist/cjs/features/session_replay/instrument/index.js +53 -13
- package/dist/cjs/features/session_replay/shared/utils.js +3 -5
- package/dist/cjs/features/session_trace/aggregate/index.js +181 -527
- package/dist/cjs/features/session_trace/aggregate/trace/node.js +19 -0
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +289 -0
- package/dist/cjs/features/session_trace/constants.js +3 -2
- package/dist/cjs/features/session_trace/instrument/index.js +7 -3
- package/dist/cjs/features/utils/aggregate-base.js +1 -0
- package/dist/cjs/features/utils/feature-gates.js +17 -0
- package/dist/cjs/features/utils/instrument-base.js +2 -1
- package/dist/cjs/loaders/agent-base.js +4 -0
- package/dist/cjs/loaders/api/api-methods.js +1 -1
- package/dist/cjs/loaders/api/api.js +2 -2
- package/dist/cjs/loaders/configure/configure.js +1 -0
- package/dist/esm/cdn/polyfills.js +3 -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/harvest-scheduler.js +4 -2
- package/dist/esm/features/ajax/constants.js +2 -1
- package/dist/esm/features/jserrors/aggregate/index.js +2 -1
- package/dist/esm/features/metrics/aggregate/index.js +0 -8
- package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
- package/dist/esm/features/session_replay/aggregate/index.js +32 -37
- package/dist/esm/features/session_replay/constants.js +4 -1
- package/dist/esm/features/session_replay/instrument/index.js +54 -14
- package/dist/esm/features/session_replay/shared/utils.js +4 -6
- package/dist/esm/features/session_trace/aggregate/index.js +182 -527
- package/dist/esm/features/session_trace/aggregate/trace/node.js +12 -0
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +282 -0
- package/dist/esm/features/session_trace/constants.js +2 -1
- package/dist/esm/features/session_trace/instrument/index.js +7 -3
- package/dist/esm/features/utils/aggregate-base.js +1 -0
- package/dist/esm/features/utils/feature-gates.js +11 -0
- package/dist/esm/features/utils/instrument-base.js +3 -2
- package/dist/esm/loaders/agent-base.js +4 -0
- package/dist/esm/loaders/api/api-methods.js +1 -1
- package/dist/esm/loaders/api/api.js +2 -2
- package/dist/esm/loaders/configure/configure.js +1 -0
- package/dist/types/common/harvest/harvest-scheduler.d.ts +1 -0
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
- package/dist/types/features/ajax/constants.d.ts +1 -0
- package/dist/types/features/ajax/constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +3 -0
- package/dist/types/features/session_replay/instrument/index.d.ts +0 -1
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +39 -52
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/node.d.ts +12 -0
- package/dist/types/features/session_trace/aggregate/trace/node.d.ts.map +1 -0
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +43 -0
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -0
- package/dist/types/features/session_trace/constants.d.ts +1 -0
- package/dist/types/features/session_trace/constants.d.ts.map +1 -1
- package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +1 -0
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/feature-gates.d.ts +2 -0
- package/dist/types/features/utils/feature-gates.d.ts.map +1 -0
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +1 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cdn/polyfills.js +2 -0
- package/src/common/drain/drain.js +1 -1
- package/src/common/harvest/harvest-scheduler.js +4 -2
- package/src/features/ajax/constants.js +2 -0
- package/src/features/jserrors/aggregate/index.js +2 -1
- package/src/features/metrics/aggregate/index.js +0 -8
- package/src/features/page_view_event/aggregate/index.js +1 -1
- package/src/features/session_replay/aggregate/index.js +30 -39
- package/src/features/session_replay/constants.js +4 -0
- package/src/features/session_replay/instrument/index.js +48 -8
- package/src/features/session_replay/shared/__mocks__/utils.js +0 -1
- package/src/features/session_replay/shared/utils.js +4 -7
- package/src/features/session_trace/aggregate/index.js +157 -493
- package/src/features/session_trace/aggregate/trace/node.js +12 -0
- package/src/features/session_trace/aggregate/trace/storage.js +287 -0
- package/src/features/session_trace/constants.js +1 -0
- package/src/features/session_trace/instrument/index.js +7 -2
- package/src/features/utils/__mocks__/feature-gates.js +1 -0
- package/src/features/utils/aggregate-base.js +1 -0
- package/src/features/utils/feature-gates.js +11 -0
- package/src/features/utils/instrument-base.js +3 -2
- package/src/loaders/agent-base.js +4 -0
- package/src/loaders/api/api-methods.js +1 -1
- package/src/loaders/api/api.js +2 -2
- package/src/loaders/configure/configure.js +1 -0
- package/dist/cjs/features/session_replay/shared/replay-mode.js +0 -28
- package/dist/cjs/features/utils/handler-cache.js +0 -70
- package/dist/esm/features/session_replay/shared/replay-mode.js +0 -23
- package/dist/esm/features/utils/handler-cache.js +0 -63
- package/dist/types/features/session_replay/shared/replay-mode.d.ts +0 -9
- package/dist/types/features/session_replay/shared/replay-mode.d.ts.map +0 -1
- package/dist/types/features/utils/handler-cache.d.ts +0 -23
- package/dist/types/features/utils/handler-cache.d.ts.map +0 -1
- package/src/features/session_replay/shared/replay-mode.js +0 -23
- package/src/features/utils/handler-cache.js +0 -65
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** The purpose of this class is to manage, normalize, and retrieve ST nodes as needed without polluting the main ST modules */
|
|
2
|
+
export class TraceStorage {
|
|
3
|
+
constructor(parent: any);
|
|
4
|
+
nodeCount: number;
|
|
5
|
+
trace: {};
|
|
6
|
+
earliestTimeStamp: number;
|
|
7
|
+
latestTimeStamp: number;
|
|
8
|
+
tempStorage: any[];
|
|
9
|
+
prevStoredEvents: Set<any>;
|
|
10
|
+
parent: any;
|
|
11
|
+
/** Central function called by all the other store__ & addToTrace API to append a trace node. */
|
|
12
|
+
storeSTN(stn: any): void;
|
|
13
|
+
/**
|
|
14
|
+
* Trim the collection of nodes awaiting harvest such that those seen outside a certain span of time are discarded.
|
|
15
|
+
* @param {number} lookbackDuration Past length of time until now for which we care about nodes, in milliseconds
|
|
16
|
+
* @returns {number} However many nodes were discarded after trimming.
|
|
17
|
+
*/
|
|
18
|
+
trimSTNs(lookbackDuration: number): number;
|
|
19
|
+
/** Used by session trace's harvester to create the payload body. */
|
|
20
|
+
takeSTNs(): {
|
|
21
|
+
stns?: undefined;
|
|
22
|
+
earliestTimeStamp?: undefined;
|
|
23
|
+
latestTimeStamp?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
stns: any[];
|
|
26
|
+
earliestTimeStamp: number;
|
|
27
|
+
latestTimeStamp: number;
|
|
28
|
+
};
|
|
29
|
+
smearEvtsByOrigin(name: any): (byOrigin: any, evtNode: any) => any;
|
|
30
|
+
processPVT(name: any, value: any, attrs: any): void;
|
|
31
|
+
storeTiming(timingEntry: any): void;
|
|
32
|
+
storeEvent(currentEvent: any, target: any, start: any, end: any): void;
|
|
33
|
+
shouldIgnoreEvent(event: any, target: any): boolean;
|
|
34
|
+
evtName(type: any): any;
|
|
35
|
+
evtOrigin(t: any, target: any): string;
|
|
36
|
+
storeHist(path: any, old: any, time: any): void;
|
|
37
|
+
storeResources(resources: any): void;
|
|
38
|
+
storeErrorAgg(type: any, name: any, params: any, metrics: any): void;
|
|
39
|
+
storeXhrAgg(type: any, name: any, params: any, metrics: any): void;
|
|
40
|
+
restoreNode(name: any, listOfSTNodes: any): void;
|
|
41
|
+
#private;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AAyBA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,mBAAgB;IAChB,2BAA4B;IAG1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAiBC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;;;;;MAsBC;IAED,mEA6BC;IAED,oDAOC;IAED,oCAkBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAIC;IAED,iDAKC;;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/session_trace/constants.js"],"names":[],"mappings":"AAEA,kCAAsD;AACtD,yCAAyC;AACzC,kCAAkC;AAClC,6BAA6B;AAC7B,yBAAyB;AACzB,8BAAoC;AACpC,4BAAgC;AAChC,qCAAqC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/session_trace/constants.js"],"names":[],"mappings":"AAEA,kCAAsD;AACtD,yCAAyC;AACzC,kCAAkC;AAClC,6BAA6B;AAC7B,yBAAyB;AACzB,8BAAoC;AACpC,4BAAgC;AAChC,qCAAqC;AACrC,yCAAyC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/instrument/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/instrument/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,mEA0CC;IAhCC,6BAA4C;CAiC/C;+BAxD8B,6BAA6B"}
|
|
@@ -7,6 +7,7 @@ export class AggregateBase extends FeatureBase {
|
|
|
7
7
|
*/
|
|
8
8
|
waitForFlags(flagNames?: string[]): Promise<any>;
|
|
9
9
|
drain(): void;
|
|
10
|
+
drained: boolean | undefined;
|
|
10
11
|
/**
|
|
11
12
|
* Checks for additional `jsAttributes` items to support backward compatibility with implementations of the agent where
|
|
12
13
|
* loader configurations may appear after the loader code is executed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAOA;IACE,4BAGC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAmBlB;IAED,
|
|
1
|
+
{"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAOA;IACE,4BAGC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAmBlB;IAED,cAGC;IADC,6BAAmB;IAGrB;;;OAGG;IACH,2BAqBC;CACF;4BAnE2B,gBAAgB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-gates.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/feature-gates.js"],"names":[],"mappings":"AAQO,kDAHI,MAAM,GACJ,OAAO,CAInB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAiBA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,cACN,OAAO,mCAAmC,EAAE,UAAU,eACtD,MAAM,8BAqChB;IA9BC,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,kCAAoC;IAiBtC;;;;;OAKG;IACH,mEAgDC;;CAYF;4BA5H2B,gBAAgB"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export class AgentBase {
|
|
5
5
|
constructor(agentIdentifier?: string);
|
|
6
6
|
agentIdentifier: string;
|
|
7
|
+
ee: any;
|
|
7
8
|
/**
|
|
8
9
|
* Reports a browser PageAction event along with a name and optional attributes.
|
|
9
10
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addpageaction/}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"AAOA;;GAEG;AAEH;IAGE,sCAKC;IAPD,wBAAe;IAMb,QAAiC;IAanC;;;;;OAKG;IACH,oBAHW,MAAM,wCAKhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,kCAKhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,IAAI,sCAK5B;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,8CAKtB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,OAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,OAMrB;IAED;;;;OAIG;IACH,kCAFmB,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,OAI9D;IAED;;;;OAIG;IACH,8CAEC;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,OAIhB;IAED;;;;OAIG;IACH,yDAEC;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;;OAKG;IACH,mBAEC;IAED;;;;;;;;;;OAUG;IACH,6BARW;QAAC,MAAM,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,OAUrF;IAED;;;;;OAKG;IACH,0BAHW,MAAM,OAKhB;IAED;;;;;MAKE;IACF,eAHa,mBAAmB,CAK/B;;CACF;kCA/KY,OAAO,yBAAyB,EAAE,mBAAmB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AASA;;GAEG;AACH,
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AASA;;GAEG;AACH,oGAgDC"}
|
package/package.json
CHANGED
package/src/cdn/polyfills.js
CHANGED
|
@@ -10,6 +10,7 @@ import 'core-js/stable/array/flat'
|
|
|
10
10
|
import 'core-js/stable/array/flat-map'
|
|
11
11
|
import 'core-js/stable/array/from'
|
|
12
12
|
import 'core-js/stable/array/some'
|
|
13
|
+
import 'core-js/stable/array/find-index'
|
|
13
14
|
import 'core-js/stable/object/assign'
|
|
14
15
|
import 'core-js/stable/object/entries'
|
|
15
16
|
import 'core-js/stable/object/values'
|
|
@@ -23,3 +24,4 @@ import 'core-js/stable/url'
|
|
|
23
24
|
import 'core-js/stable/url-search-params'
|
|
24
25
|
import 'core-js/stable/string/starts-with'
|
|
25
26
|
import 'core-js/stable/number/is-nan'
|
|
27
|
+
import 'core-js/stable/string/includes'
|
|
@@ -71,7 +71,7 @@ export function drain (agentIdentifier = '', featureName = 'feature', force = fa
|
|
|
71
71
|
function checkCanDrainAll (agentIdentifier) {
|
|
72
72
|
// Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
|
|
73
73
|
// that the last feature to call drain triggers drain for all features.
|
|
74
|
-
const items =
|
|
74
|
+
const items = Array.from(registry[agentIdentifier])
|
|
75
75
|
if (items.every(([key, values]) => values.staged)) {
|
|
76
76
|
items.sort((a, b) => a[1].priority - b[1].priority)
|
|
77
77
|
items.forEach(([group]) => {
|
|
@@ -31,7 +31,7 @@ export class HarvestScheduler extends SharedContext {
|
|
|
31
31
|
this.started = false
|
|
32
32
|
this.timeoutHandle = null
|
|
33
33
|
this.aborted = false // this controls the per-interval and final harvests for the scheduler (currently per feature specific!)
|
|
34
|
-
|
|
34
|
+
this.harvesting = false
|
|
35
35
|
this.harvest = new Harvest(this.sharedContext)
|
|
36
36
|
|
|
37
37
|
// unload if EOL mechanism fires
|
|
@@ -82,12 +82,14 @@ export class HarvestScheduler extends SharedContext {
|
|
|
82
82
|
|
|
83
83
|
runHarvest (opts) {
|
|
84
84
|
if (this.aborted) return
|
|
85
|
+
this.harvesting = true
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
88
|
* This is executed immediately after harvest sends the data via XHR, or if there's nothing to send. Note that this excludes on unloading / sendBeacon.
|
|
88
89
|
* @param {Object} result
|
|
89
90
|
*/
|
|
90
91
|
const cbRanAfterSend = (result) => {
|
|
92
|
+
this.harvesting = false
|
|
91
93
|
if (opts?.forceNoRetry) result.retry = false // discard unsent data rather than re-queuing for next harvest attempt
|
|
92
94
|
this.onHarvestFinished(opts, result)
|
|
93
95
|
}
|
|
@@ -102,7 +104,7 @@ export class HarvestScheduler extends SharedContext {
|
|
|
102
104
|
if (!submitMethod) return false
|
|
103
105
|
|
|
104
106
|
const retry = !opts?.unload && submitMethod === submitData.xhr
|
|
105
|
-
payload = this.opts.getPayload({ retry, opts })
|
|
107
|
+
payload = this.opts.getPayload({ retry, ...opts })
|
|
106
108
|
|
|
107
109
|
if (!payload) {
|
|
108
110
|
if (this.started) {
|
|
@@ -201,7 +201,8 @@ export class Aggregate extends AggregateBase {
|
|
|
201
201
|
var type = internal ? 'ierr' : 'err'
|
|
202
202
|
var newMetrics = { time }
|
|
203
203
|
|
|
204
|
-
//
|
|
204
|
+
// sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
|
|
205
|
+
// and spa annotates the error with interaction info
|
|
205
206
|
const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes]
|
|
206
207
|
handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
|
|
207
208
|
// still send EE events for other features such as above, but stop this one from aggregating internal data
|
|
@@ -112,8 +112,6 @@ export class Aggregate extends AggregateBase {
|
|
|
112
112
|
if (this.resourcesSent) return
|
|
113
113
|
this.resourcesSent = true // make sure this only gets sent once
|
|
114
114
|
|
|
115
|
-
const agentRuntime = getRuntime(this.agentIdentifier)
|
|
116
|
-
|
|
117
115
|
// Capture SMs around network resources using the performance API to assess
|
|
118
116
|
// work to split this out from the ST nodes
|
|
119
117
|
// differentiate between internal+external and ajax+non-ajax
|
|
@@ -132,12 +130,6 @@ export class Aggregate extends AggregateBase {
|
|
|
132
130
|
}
|
|
133
131
|
})
|
|
134
132
|
|
|
135
|
-
// Capture SMs for session trace if active (`ptid` is set when returned by replay ingest).
|
|
136
|
-
// Retain these SMs while we are working through the session_replay feature
|
|
137
|
-
if (agentRuntime.ptid) {
|
|
138
|
-
this.storeSupportabilityMetrics('PageSession/Feature/SessionTrace/DurationMs', Math.round(performance.now()))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
133
|
// Capture SMs for performance markers and measures to assess the usage and possible inclusion of this
|
|
142
134
|
// data in the agent for use in NR
|
|
143
135
|
if (typeof performance !== 'undefined') {
|
|
@@ -126,7 +126,7 @@ export class Aggregate extends AggregateBase {
|
|
|
126
126
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee)
|
|
127
127
|
drain(this.agentIdentifier, FEATURE_NAMES.metrics, true)
|
|
128
128
|
this.ee.abort()
|
|
129
|
-
warn('Could not calculate New Relic server time. Agent shutting down.')
|
|
129
|
+
warn('Could not calculate New Relic server time. Agent shutting down.', error)
|
|
130
130
|
return
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
14
14
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
15
|
-
import { ABORT_REASONS, FEATURE_NAME, MAX_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, SR_EVENT_EMITTER_TYPES } from '../constants'
|
|
15
|
+
import { ABORT_REASONS, FEATURE_NAME, MAX_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, SR_EVENT_EMITTER_TYPES, TRIGGERS } from '../constants'
|
|
16
16
|
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config'
|
|
17
17
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
18
18
|
import { sharedChannel } from '../../../common/constants/shared-channel'
|
|
@@ -97,16 +97,6 @@ export class Aggregate extends AggregateBase {
|
|
|
97
97
|
raw: true
|
|
98
98
|
}, this)
|
|
99
99
|
|
|
100
|
-
registerHandler(SR_EVENT_EMITTER_TYPES.RECORD, () => {
|
|
101
|
-
// if it has aborted or BCS returned bad entitlements, do not allow
|
|
102
|
-
if (this.blocked || !this.entitled) return
|
|
103
|
-
// if it isnt already (fully) initialized... initialize it
|
|
104
|
-
if (!this.recorder) this.initializeRecording(false, true, true)
|
|
105
|
-
// its been initialized and imported the recorder but its not recording (mode === off || error)
|
|
106
|
-
else if (this.mode !== MODE.FULL) this.switchToFull()
|
|
107
|
-
// if it gets all the way to here, that means a full session is already recording... do nothing
|
|
108
|
-
}, this.featureName, this.ee)
|
|
109
|
-
|
|
110
100
|
registerHandler(SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
111
101
|
this.forceStop(this.mode !== MODE.ERROR)
|
|
112
102
|
}, this.featureName, this.ee)
|
|
@@ -117,8 +107,8 @@ export class Aggregate extends AggregateBase {
|
|
|
117
107
|
|
|
118
108
|
const { error_sampling_rate, sampling_rate, autoStart, block_selector, mask_text_selector, mask_all_inputs, inline_stylesheet, inline_images, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
|
|
119
109
|
|
|
120
|
-
this.waitForFlags(['sr']).then(([
|
|
121
|
-
this.entitled =
|
|
110
|
+
this.waitForFlags(['srs', 'sr']).then(([srMode, entitled]) => {
|
|
111
|
+
this.entitled = !!entitled
|
|
122
112
|
if (!this.entitled) {
|
|
123
113
|
deregisterDrain(this.agentIdentifier, this.featureName)
|
|
124
114
|
if (this.recorder?.recording) {
|
|
@@ -128,14 +118,14 @@ export class Aggregate extends AggregateBase {
|
|
|
128
118
|
return
|
|
129
119
|
}
|
|
130
120
|
this.drain()
|
|
131
|
-
this.initializeRecording(
|
|
132
|
-
(Math.random() * 100) < error_sampling_rate,
|
|
133
|
-
(Math.random() * 100) < sampling_rate
|
|
134
|
-
)
|
|
121
|
+
this.initializeRecording(srMode)
|
|
135
122
|
}).then(() => {
|
|
136
|
-
if (this.mode === MODE.OFF)
|
|
137
|
-
|
|
138
|
-
|
|
123
|
+
if (this.mode === MODE.OFF) {
|
|
124
|
+
this.recorder?.stopRecording() // stop any conservative preload recording launched by instrument
|
|
125
|
+
while (this.recorder?.getEvents().events.length) this.recorder?.clearBuffer?.()
|
|
126
|
+
}
|
|
127
|
+
sharedChannel.onReplayReady(this.mode)
|
|
128
|
+
}) // notify watchers that replay started with the mode
|
|
139
129
|
|
|
140
130
|
/** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
|
|
141
131
|
if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
|
|
@@ -179,7 +169,7 @@ export class Aggregate extends AggregateBase {
|
|
|
179
169
|
* @param {boolean} ignoreSession - whether to force the method to ignore the session state and use just the sample flags
|
|
180
170
|
* @returns {void}
|
|
181
171
|
*/
|
|
182
|
-
async initializeRecording (
|
|
172
|
+
async initializeRecording (srMode, ignoreSession) {
|
|
183
173
|
this.initialized = true
|
|
184
174
|
if (!this.entitled) return
|
|
185
175
|
|
|
@@ -191,23 +181,16 @@ export class Aggregate extends AggregateBase {
|
|
|
191
181
|
// session replays can continue if already in progress
|
|
192
182
|
const { session, timeKeeper } = getRuntime(this.agentIdentifier)
|
|
193
183
|
this.timeKeeper = timeKeeper
|
|
194
|
-
if (
|
|
184
|
+
if (this.recorder?.parent.trigger === TRIGGERS.API && this.recorder?.recording) {
|
|
185
|
+
this.mode = MODE.FULL
|
|
186
|
+
} else if (!session.isNew && !ignoreSession) { // inherit the mode of the existing session
|
|
195
187
|
this.mode = session.state.sessionReplayMode
|
|
196
188
|
} else {
|
|
197
189
|
// The session is new... determine the mode the new session should start in
|
|
198
|
-
|
|
199
|
-
else if (errorSample) this.mode = MODE.ERROR
|
|
200
|
-
// If neither are selected, then don't record (early return)
|
|
201
|
-
else {
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (this.recorder?.getEvents().type === 'preloaded') {
|
|
207
|
-
this.prepUtils().then(() => {
|
|
208
|
-
this.scheduler.runHarvest()
|
|
209
|
-
})
|
|
190
|
+
this.mode = srMode
|
|
210
191
|
}
|
|
192
|
+
// If off, then don't record (early return)
|
|
193
|
+
if (this.mode === MODE.OFF) return
|
|
211
194
|
|
|
212
195
|
if (!this.recorder) {
|
|
213
196
|
try {
|
|
@@ -225,12 +208,20 @@ export class Aggregate extends AggregateBase {
|
|
|
225
208
|
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
226
209
|
if (this.mode === MODE.ERROR && this.errorNoticed) this.mode = MODE.FULL
|
|
227
210
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
211
|
+
if (this.mode === MODE.FULL) {
|
|
212
|
+
// If theres preloaded events and we are in full mode, just harvest immediately to clear up space and for consistency
|
|
213
|
+
if (this.recorder?.getEvents().type === 'preloaded') {
|
|
214
|
+
this.prepUtils().then(() => {
|
|
215
|
+
this.scheduler.runHarvest()
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
219
|
+
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
220
|
+
// If an error happened in ERROR mode before we've gotten to this stage, it will have already set the mode to FULL
|
|
221
|
+
if (!this.scheduler.started) {
|
|
232
222
|
// We only report (harvest) in FULL mode
|
|
233
|
-
|
|
223
|
+
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
224
|
+
}
|
|
234
225
|
}
|
|
235
226
|
|
|
236
227
|
await this.prepUtils()
|
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
import { handle } from '../../../common/event-emitter/handle'
|
|
13
13
|
import { DEFAULT_KEY, MODE, PREFIX } from '../../../common/session/constants'
|
|
14
14
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { hasReplayPrerequisite, isPreloadAllowed } from '../shared/utils'
|
|
16
|
+
import { FEATURE_NAME, SR_EVENT_EMITTER_TYPES, TRIGGERS } from '../constants'
|
|
17
17
|
|
|
18
18
|
export class Instrument extends InstrumentBase {
|
|
19
19
|
static featureName = FEATURE_NAME
|
|
20
|
+
|
|
21
|
+
#mode
|
|
20
22
|
constructor (agentIdentifier, aggregator, auto = true) {
|
|
21
23
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto)
|
|
22
24
|
let session
|
|
@@ -25,8 +27,13 @@ export class Instrument extends InstrumentBase {
|
|
|
25
27
|
session = JSON.parse(localStorage.getItem(`${PREFIX}_${DEFAULT_KEY}`))
|
|
26
28
|
} catch (err) { }
|
|
27
29
|
|
|
30
|
+
if (hasReplayPrerequisite(agentIdentifier)) {
|
|
31
|
+
this.ee.on('recordReplay', () => this.#apiStartOrRestartReplay())
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
if (this.#canPreloadRecorder(session)) {
|
|
29
|
-
this.#
|
|
35
|
+
this.#mode = session?.sessionReplayMode
|
|
36
|
+
this.#preloadStartRecording()
|
|
30
37
|
} else {
|
|
31
38
|
this.importAggregator()
|
|
32
39
|
}
|
|
@@ -58,11 +65,44 @@ export class Instrument extends InstrumentBase {
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
#alreadyStarted = false
|
|
69
|
+
/**
|
|
70
|
+
* This func is use for early pre-load recording prior to replay feature (agg) being loaded onto the page. It should only setup once, including if already called and in-progress.
|
|
71
|
+
*/
|
|
72
|
+
async #preloadStartRecording (trigger) {
|
|
73
|
+
if (this.#alreadyStarted) return
|
|
74
|
+
this.#alreadyStarted = true
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const { Recorder } = (await import(/* webpackChunkName: "recorder" */'../shared/recorder'))
|
|
78
|
+
|
|
79
|
+
// If startReplay() has been used by this point, we must record in full mode regardless of session preload:
|
|
80
|
+
// Note: recorder starts here with w/e the mode is at this time, but this may be changed later (see #apiStartOrRestartReplay else-case)
|
|
81
|
+
this.recorder ??= new Recorder({ mode: this.#mode, agentIdentifier: this.agentIdentifier, trigger, ee: this.ee })
|
|
82
|
+
this.recorder.startRecording()
|
|
83
|
+
this.abortHandler = this.recorder.stopRecording
|
|
84
|
+
} catch (e) {}
|
|
66
85
|
this.importAggregator({ recorder: this.recorder, errorNoticed: this.errorNoticed })
|
|
67
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Called whenever startReplay API is used. That could occur any time, pre or post load.
|
|
90
|
+
*/
|
|
91
|
+
#apiStartOrRestartReplay () {
|
|
92
|
+
if (this.featAggregate) { // post-load; there's possibly already an ongoing recording
|
|
93
|
+
if (this.featAggregate.mode !== MODE.FULL) this.featAggregate.initializeRecording(MODE.FULL, true)
|
|
94
|
+
} else { // pre-load
|
|
95
|
+
this.#mode = MODE.FULL
|
|
96
|
+
this.#preloadStartRecording(TRIGGERS.API)
|
|
97
|
+
// There's a race here wherein either:
|
|
98
|
+
// a. Recorder has not been initialized, and we've set the enforced mode, so we're good, or;
|
|
99
|
+
// b. Record has been initialized, possibly with the "wrong" mode, so we have to correct that + restart.
|
|
100
|
+
if (this.recorder && this.recorder.parent.mode !== MODE.FULL) {
|
|
101
|
+
this.recorder.parent.mode = MODE.FULL
|
|
102
|
+
this.recorder.stopRecording()
|
|
103
|
+
this.recorder.startRecording()
|
|
104
|
+
this.abortHandler = this.recorder.stopRecording
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
68
108
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { getConfigurationValue, originals } from '../../../common/config/config'
|
|
2
|
-
import {
|
|
2
|
+
import { canEnableSessionTracking } from '../../utils/feature-gates'
|
|
3
|
+
import { originTime } from '../../../common/constants/runtime'
|
|
3
4
|
|
|
4
|
-
export function
|
|
5
|
-
return isBrowserScope && getConfigurationValue(agentId, 'privacy.cookies_enabled') === true
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function hasReplayPrerequisite (agentId) {
|
|
5
|
+
export function hasReplayPrerequisite (agentId) {
|
|
9
6
|
return !!originals.MO && // Session Replay cannot work without Mutation Observer
|
|
10
|
-
|
|
7
|
+
canEnableSessionTracking(agentId) && // requires session tracking to be running (hence "session" replay...)
|
|
11
8
|
getConfigurationValue(agentId, 'session_trace.enabled') === true // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
12
9
|
}
|
|
13
10
|
|