@newrelic/browser-agent 1.311.0-rc.4 → 1.311.0-rc.6
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/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/util/stringify.js +6 -21
- package/dist/cjs/common/util/v2.js +36 -8
- package/dist/cjs/common/wrap/wrap-fetch.js +0 -2
- package/dist/cjs/common/wrap/wrap-function.js +16 -8
- package/dist/cjs/common/wrap/wrap-logger.js +6 -4
- package/dist/cjs/common/wrap/wrap-xhr.js +0 -2
- package/dist/cjs/features/ajax/aggregate/index.js +1 -1
- package/dist/cjs/features/logging/aggregate/index.js +12 -7
- package/dist/cjs/features/logging/instrument/index.js +5 -3
- package/dist/cjs/features/logging/shared/utils.js +3 -3
- package/dist/cjs/loaders/api/register.js +3 -1
- package/dist/cjs/loaders/api/wrapLogger.js +1 -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/util/stringify.js +6 -21
- package/dist/esm/common/util/v2.js +36 -8
- package/dist/esm/common/wrap/wrap-fetch.js +0 -2
- package/dist/esm/common/wrap/wrap-function.js +16 -8
- package/dist/esm/common/wrap/wrap-logger.js +6 -4
- package/dist/esm/common/wrap/wrap-xhr.js +0 -2
- package/dist/esm/features/ajax/aggregate/index.js +1 -1
- package/dist/esm/features/logging/aggregate/index.js +13 -8
- package/dist/esm/features/logging/instrument/index.js +5 -3
- package/dist/esm/features/logging/shared/utils.js +3 -3
- package/dist/esm/loaders/api/register.js +3 -1
- package/dist/esm/loaders/api/wrapLogger.js +1 -1
- package/dist/types/common/util/stringify.d.ts.map +1 -1
- package/dist/types/common/util/v2.d.ts +18 -4
- package/dist/types/common/util/v2.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-logger.d.ts +1 -1
- package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/instrument/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/package.json +1 -1
- package/src/common/util/stringify.js +6 -23
- package/src/common/util/v2.js +34 -8
- package/src/common/wrap/wrap-fetch.js +0 -2
- package/src/common/wrap/wrap-function.js +15 -8
- package/src/common/wrap/wrap-logger.js +6 -4
- package/src/common/wrap/wrap-xhr.js +0 -2
- package/src/features/ajax/aggregate/index.js +1 -1
- package/src/features/logging/aggregate/index.js +13 -14
- package/src/features/logging/instrument/index.js +5 -3
- package/src/features/logging/shared/utils.js +3 -3
- package/src/loaders/api/register.js +1 -1
- package/src/loaders/api/wrapLogger.js +1 -1
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.311.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.311.0-rc.6";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.311.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.311.0-rc.6";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -12,32 +12,17 @@ var _contextualEe = require("../event-emitter/contextual-ee");
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Returns a function for use as a replacer parameter in JSON.stringify() to handle circular references.
|
|
15
|
-
* Uses an array to track the current ancestor chain, allowing the same object to appear
|
|
16
|
-
* multiple times in the structure as long as it's not a circular reference.
|
|
17
15
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value MDN - Cyclical object value}
|
|
18
|
-
* @returns {Function} A function that filters out
|
|
16
|
+
* @returns {Function} A function that filters out values it has seen before.
|
|
19
17
|
*/
|
|
20
18
|
const getCircularReplacer = () => {
|
|
21
|
-
const
|
|
22
|
-
return
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
const thisPos = stack.indexOf(this);
|
|
26
|
-
if (~thisPos) {
|
|
27
|
-
// We're still in the stack, trim it
|
|
28
|
-
stack.splice(thisPos + 1);
|
|
29
|
-
} else {
|
|
30
|
-
// We're not in the stack, add ourselves
|
|
31
|
-
stack.push(this);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Check if value is in the current ancestor chain
|
|
35
|
-
if (~stack.indexOf(value)) {
|
|
19
|
+
const seen = new WeakSet();
|
|
20
|
+
return (key, value) => {
|
|
21
|
+
if (typeof value === 'object' && value !== null) {
|
|
22
|
+
if (seen.has(value)) {
|
|
36
23
|
return;
|
|
37
24
|
}
|
|
38
|
-
|
|
39
|
-
// First call, initialize with root
|
|
40
|
-
stack.push(value);
|
|
25
|
+
seen.add(value);
|
|
41
26
|
}
|
|
42
27
|
return value;
|
|
43
28
|
};
|
|
@@ -28,7 +28,7 @@ const V2_TYPES = exports.V2_TYPES = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Returns the registered target associated with a given ID. Returns
|
|
31
|
+
* Returns the registered target associated with a given ID. Returns an empty array if no target is found.
|
|
32
32
|
* @param {string|number} id
|
|
33
33
|
* @param {*} agentRef the agent reference
|
|
34
34
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
@@ -40,7 +40,7 @@ function getRegisteredTargetsFromId(id, agentRef) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if
|
|
43
|
+
* Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if no target is found.
|
|
44
44
|
* @param {string} filename
|
|
45
45
|
* @param {*} agentRef
|
|
46
46
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
@@ -60,7 +60,7 @@ function getRegisteredTargetsFromFilename(filename, agentRef) {
|
|
|
60
60
|
* @returns {Object} returns an empty object if args are not supplied or the aggregate instance is not supporting version 2
|
|
61
61
|
*/
|
|
62
62
|
function getVersion2Attributes(target, aggregateInstance) {
|
|
63
|
-
if (aggregateInstance
|
|
63
|
+
if (!supportsV2(aggregateInstance)) return {};
|
|
64
64
|
const containerAgentEntityGuid = aggregateInstance.agentRef.runtime.appMetadata.agents[0].entityGuid;
|
|
65
65
|
/** if there's no target, but we are in v2 mode, this means the data belongs to the container agent */
|
|
66
66
|
if (!target) {
|
|
@@ -74,24 +74,40 @@ function getVersion2Attributes(target, aggregateInstance) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* Returns the attributes used for duplicating data in version 2 of the harvest endpoint.
|
|
77
|
+
* Returns the attributes used for duplicating data in version 2 of the harvest endpoint.
|
|
78
|
+
* If not valid for duplication, returns an empty object.
|
|
79
|
+
* @note BEST PRACTICE - Caller should call shouldDuplicate() before utilizing this method to determine if duplication attributes should be added to the event.
|
|
78
80
|
* @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
|
|
79
81
|
* @param {*} aggregateInstance the aggregate instance calling the method
|
|
80
82
|
* @returns {Object}
|
|
81
83
|
*/
|
|
82
84
|
function getVersion2DuplicationAttributes(target, aggregateInstance) {
|
|
83
|
-
if (
|
|
85
|
+
if (!shouldDuplicate(target, aggregateInstance)) return {};
|
|
84
86
|
return {
|
|
85
87
|
'child.id': target.id,
|
|
86
88
|
'child.type': target.type,
|
|
87
89
|
...getVersion2Attributes(undefined, aggregateInstance)
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Determines if an event should be duplicated for a given target and aggregate instance. This is used to determine if duplication attributes should be added to an event and if the event should be sent to the soft nav feature for evaluation.
|
|
95
|
+
* @note This method is intended to be used in conjunction with getVersion2DuplicationAttributes and should be called before it to determine if duplication attributes should be added to an event.
|
|
96
|
+
* @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
|
|
97
|
+
* @param {*} aggregateInstance The aggregate instance calling the method. This is needed to check if duplication is enabled and if the harvest endpoint version supports it.
|
|
98
|
+
* @returns {boolean} returns true if the event should be duplicated for the target, false otherwise
|
|
99
|
+
*/
|
|
100
|
+
function shouldDuplicate(target, aggregateInstance) {
|
|
101
|
+
return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.duplicate_registered_data;
|
|
92
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Finds the registered targets from the stack trace for a given agent reference.
|
|
106
|
+
* @param {*} agentRef The agent reference to use for finding targets.
|
|
107
|
+
* @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
|
|
108
|
+
*/
|
|
93
109
|
function findTargetsFromStackTrace(agentRef) {
|
|
94
|
-
if (!agentRef?.init.api.allow_registered_children) return [];
|
|
110
|
+
if (!agentRef?.init.api.allow_registered_children) return [undefined];
|
|
95
111
|
const targets = [];
|
|
96
112
|
try {
|
|
97
113
|
var urls = (0, _scriptTracker.extractUrlsFromStack)((0, _scriptTracker.getDeepStackTrace)());
|
|
@@ -102,5 +118,17 @@ function findTargetsFromStackTrace(agentRef) {
|
|
|
102
118
|
} catch (err) {
|
|
103
119
|
// Silent catch to prevent errors from propagating
|
|
104
120
|
}
|
|
121
|
+
if (!targets.length) targets.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
|
|
105
122
|
return targets;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Determines if the aggregate instance supports version 2 of the harvest endpoint. Nearly all the V2 logic "depends" on
|
|
127
|
+
* the harvest endpoint version, so this is the main gatekeeper method for whether or not V2 logic should be executed across the
|
|
128
|
+
* various functions in this module.
|
|
129
|
+
* @param {*} aggregateInstance The aggregate instance to check.
|
|
130
|
+
* @returns {boolean} Returns true if the aggregate instance supports version 2, false otherwise.
|
|
131
|
+
*/
|
|
132
|
+
function supportsV2(aggregateInstance) {
|
|
133
|
+
return aggregateInstance?.harvestEndpointVersion === 2;
|
|
106
134
|
}
|
|
@@ -80,8 +80,6 @@ function wrapFetch(sharedEE, agentRef) {
|
|
|
80
80
|
var args = [...arguments];
|
|
81
81
|
const ctx = {};
|
|
82
82
|
const targets = (0, _v.findTargetsFromStackTrace)(agentRef);
|
|
83
|
-
// undefined target reports to container
|
|
84
|
-
if (!targets.length) targets.push(undefined);
|
|
85
83
|
|
|
86
84
|
// we are wrapping args in an array so we can preserve the reference
|
|
87
85
|
ee.emit(prefix + 'before-start', [args], ctx);
|
|
@@ -8,6 +8,7 @@ exports.createWrapperWithEmitter = createWrapperWithEmitter;
|
|
|
8
8
|
exports.flag = exports.default = void 0;
|
|
9
9
|
var _contextualEe = require("../event-emitter/contextual-ee");
|
|
10
10
|
var _bundleId = require("../ids/bundle-id");
|
|
11
|
+
var _v = require("../util/v2");
|
|
11
12
|
/**
|
|
12
13
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -40,7 +41,7 @@ var _default = exports.default = createWrapperWithEmitter;
|
|
|
40
41
|
* @param {boolean} always - If `true`, emit events even if already emitting an event.
|
|
41
42
|
* @returns {function} The wrapped function.
|
|
42
43
|
*/
|
|
43
|
-
function createWrapperWithEmitter(emitter, always) {
|
|
44
|
+
function createWrapperWithEmitter(emitter, always, agentRef) {
|
|
44
45
|
emitter || (emitter = _contextualEe.ee);
|
|
45
46
|
wrapFn.inPlace = inPlace;
|
|
46
47
|
|
|
@@ -59,9 +60,10 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
59
60
|
* @param {function|object} getContext - The function or object that will serve as the 'this' context for handlers of events emitted by this wrapper.
|
|
60
61
|
* @param {string} methodName - The name of the method being wrapped.
|
|
61
62
|
* @param {boolean} bubble - If true, emitted events should also bubble up to the old emitter upon which the `emitter` in the current scope was based (if it defines one).
|
|
63
|
+
* @param {boolean} [evaluateStack] - If true, the wrapper will attempt to evaluate the stack of the executed wrapped function to find targets of the execution (ex. the MFE source of a console.log).
|
|
62
64
|
* @returns {function} The wrapped function.
|
|
63
65
|
*/
|
|
64
|
-
function wrapFn(fn, prefix, getContext, methodName, bubble) {
|
|
66
|
+
function wrapFn(fn, prefix, getContext, methodName, bubble, evaluateStack) {
|
|
65
67
|
// Unless fn is both wrappable and unwrapped, return it unchanged.
|
|
66
68
|
if (notWrappable(fn)) return fn;
|
|
67
69
|
if (!prefix) prefix = '';
|
|
@@ -80,9 +82,15 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
80
82
|
var ctx;
|
|
81
83
|
var result;
|
|
82
84
|
let thrownError;
|
|
85
|
+
let targets;
|
|
83
86
|
try {
|
|
84
87
|
originalThis = this;
|
|
85
88
|
args = [...arguments];
|
|
89
|
+
|
|
90
|
+
// certain wrappers can inform the function wrapper to evaluate the stack of the executed wrapped function to find targets of the execution
|
|
91
|
+
// (e.g. wrap-logger can inform this method to find try to find the MFE source of a console.log)
|
|
92
|
+
targets = evaluateStack ? (0, _v.findTargetsFromStackTrace)(agentRef) : [undefined]; // undefined target always maps to the container agent
|
|
93
|
+
|
|
86
94
|
if (typeof getContext === 'function') {
|
|
87
95
|
ctx = getContext(args, originalThis);
|
|
88
96
|
} else {
|
|
@@ -93,7 +101,7 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
// Warning: start events may mutate args!
|
|
96
|
-
safeEmit(prefix + 'start', [args, originalThis, methodName], ctx, bubble);
|
|
104
|
+
safeEmit(prefix + 'start', [args, originalThis, methodName, targets], ctx, bubble);
|
|
97
105
|
const fnStartTime = performance.now();
|
|
98
106
|
let fnEndTime;
|
|
99
107
|
try {
|
|
@@ -102,7 +110,7 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
102
110
|
return result;
|
|
103
111
|
} catch (err) {
|
|
104
112
|
fnEndTime = performance.now();
|
|
105
|
-
safeEmit(prefix + 'err', [args, originalThis, err], ctx, bubble);
|
|
113
|
+
safeEmit(prefix + 'err', [args, originalThis, err, targets], ctx, bubble);
|
|
106
114
|
// rethrow error so we don't effect execution by observing.
|
|
107
115
|
thrownError = err;
|
|
108
116
|
throw thrownError;
|
|
@@ -119,10 +127,10 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
119
127
|
};
|
|
120
128
|
// standalone long task message
|
|
121
129
|
if (task.isLongTask) {
|
|
122
|
-
safeEmit('long-task', [task, originalThis], ctx, bubble);
|
|
130
|
+
safeEmit('long-task', [task, originalThis, targets], ctx, bubble);
|
|
123
131
|
}
|
|
124
132
|
// -end message also includes the task execution info
|
|
125
|
-
safeEmit(prefix + 'end', [args, originalThis, result], ctx, bubble);
|
|
133
|
+
safeEmit(prefix + 'end', [args, originalThis, result, targets], ctx, bubble);
|
|
126
134
|
}
|
|
127
135
|
}
|
|
128
136
|
}
|
|
@@ -138,7 +146,7 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
138
146
|
* @param {boolean} [bubble=false] If `true`, emitted events should also bubble up to the old emitter upon which
|
|
139
147
|
* the `emitter` in the current scope was based (if it defines one).
|
|
140
148
|
*/
|
|
141
|
-
function inPlace(obj, methods, prefix, getContext, bubble) {
|
|
149
|
+
function inPlace(obj, methods, prefix, getContext, bubble, evaluateStack) {
|
|
142
150
|
if (!prefix) prefix = '';
|
|
143
151
|
|
|
144
152
|
// If prefix starts with '-' set this boolean to add the method name to the prefix before passing each one to wrap.
|
|
@@ -149,7 +157,7 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
149
157
|
|
|
150
158
|
// Unless fn is both wrappable and unwrapped, bail so we don't add extra properties with undefined values.
|
|
151
159
|
if (notWrappable(fn)) continue;
|
|
152
|
-
obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble);
|
|
160
|
+
obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble, evaluateStack);
|
|
153
161
|
}
|
|
154
162
|
}
|
|
155
163
|
|
|
@@ -30,10 +30,10 @@ const contextMap = new Map();
|
|
|
30
30
|
* @returns {Object} Scoped event emitter with a debug ID of `logger`.
|
|
31
31
|
*/
|
|
32
32
|
// eslint-disable-next-line
|
|
33
|
-
function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true) {
|
|
33
|
+
function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true, agentRef) {
|
|
34
34
|
if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return (0, _console.warn)(29);
|
|
35
35
|
const ee = scopedEE(sharedEE);
|
|
36
|
-
const wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee);
|
|
36
|
+
const wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, undefined, agentRef);
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* This section contains the context that will be shared across all invoked calls of the wrapped function,
|
|
@@ -46,8 +46,10 @@ function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true) {
|
|
|
46
46
|
const contextLookupKey = parent[loggerFn]?.[_wrapFunction.flag] || parent[loggerFn];
|
|
47
47
|
contextMap.set(contextLookupKey, ctx);
|
|
48
48
|
|
|
49
|
-
/** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-`
|
|
50
|
-
|
|
49
|
+
/** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-`
|
|
50
|
+
* inform the inplace wrapper to evaluate the stack for targets of the log execution,
|
|
51
|
+
* so that logs can be attributed to a matching MFE source if being used. */
|
|
52
|
+
wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey), undefined, true);
|
|
51
53
|
return ee;
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -56,8 +56,6 @@ function wrapXhr(sharedEE, agentRef) {
|
|
|
56
56
|
const xhr = new OrigXHR(opts);
|
|
57
57
|
const context = ee.context(xhr);
|
|
58
58
|
context.targets = (0, _v.findTargetsFromStackTrace)(agentRef);
|
|
59
|
-
// undefined target reports to container
|
|
60
|
-
if (!context.targets.length) context.targets.push(undefined);
|
|
61
59
|
try {
|
|
62
60
|
ee.emit('new-xhr', [xhr], context);
|
|
63
61
|
xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), (0, _eventListenerOpts.eventListenerOpts)(false));
|
|
@@ -108,7 +108,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
108
108
|
...event,
|
|
109
109
|
targetAttributes: (0, _v.getVersion2Attributes)(target, this)
|
|
110
110
|
});
|
|
111
|
-
if ((0, _v.shouldDuplicate)(target, this
|
|
111
|
+
if ((0, _v.shouldDuplicate)(target, this)) this.reportContainerEvent({
|
|
112
112
|
...event,
|
|
113
113
|
targetAttributes: (0, _v.getVersion2DuplicationAttributes)(target, this)
|
|
114
114
|
}, ctx);
|
|
@@ -78,11 +78,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
78
78
|
const modeForThisLog = autoCaptured ? this.loggingMode.auto : this.loggingMode.api;
|
|
79
79
|
if (!modeForThisLog) return;
|
|
80
80
|
if (!attributes || typeof attributes !== 'object') attributes = {};
|
|
81
|
-
attributes = {
|
|
82
|
-
...attributes,
|
|
83
|
-
/** Specific attributes only supplied if harvesting to endpoint version 2 */
|
|
84
|
-
...(0, _v.getVersion2Attributes)(target, this)
|
|
85
|
-
};
|
|
86
81
|
if (typeof level === 'string') level = level.toUpperCase();
|
|
87
82
|
if (!(0, _utils.isValidLogLevel)(level)) return (0, _console.warn)(30, level);
|
|
88
83
|
if (modeForThisLog < (_constants.LOGGING_MODE[level] || Infinity)) {
|
|
@@ -105,8 +100,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
105
100
|
return;
|
|
106
101
|
}
|
|
107
102
|
if (typeof message !== 'string' || !message) return (0, _console.warn)(32);
|
|
108
|
-
const
|
|
109
|
-
|
|
103
|
+
const addEvent = attributes => {
|
|
104
|
+
const log = new _log.Log(Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp)), message, attributes, level);
|
|
105
|
+
if (this.events.add(log)) this.reportSupportabilityMetric(LOGGING_EVENT + (autoCaptured ? 'Auto' : 'API') + '/Added');
|
|
106
|
+
};
|
|
107
|
+
addEvent({
|
|
108
|
+
...attributes,
|
|
109
|
+
...(0, _v.getVersion2Attributes)(target, this)
|
|
110
|
+
});
|
|
111
|
+
if ((0, _v.shouldDuplicate)(target, this)) addEvent({
|
|
112
|
+
...attributes,
|
|
113
|
+
...(0, _v.getVersion2DuplicationAttributes)(target, this)
|
|
114
|
+
});
|
|
110
115
|
}
|
|
111
116
|
serializer(eventBuffer) {
|
|
112
117
|
const sessionEntity = this.agentRef.runtime.session;
|
|
@@ -32,17 +32,19 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
32
32
|
(0, _monkeyPatched.isNative)(_runtime.globalScope.console[method]);
|
|
33
33
|
(0, _wrapLogger.wrapLogger)(instanceEE, _runtime.globalScope.console, method, {
|
|
34
34
|
level: method === 'log' ? 'info' : method
|
|
35
|
-
});
|
|
35
|
+
}, undefined, agentRef);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
/** emitted by wrap-logger function */
|
|
39
|
-
this.ee.on('wrap-logger-end', function handleLog([message]) {
|
|
39
|
+
this.ee.on('wrap-logger-end', function handleLog([message], _, __, targets = []) {
|
|
40
40
|
const {
|
|
41
41
|
level,
|
|
42
42
|
customAttributes,
|
|
43
43
|
autoCaptured
|
|
44
44
|
} = this;
|
|
45
|
-
|
|
45
|
+
targets.forEach(target => {
|
|
46
|
+
(0, _utils.bufferLog)(instanceEE, message, customAttributes, level, autoCaptured, target);
|
|
47
|
+
});
|
|
46
48
|
});
|
|
47
49
|
this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "logging-aggregate" */'../aggregate'))));
|
|
48
50
|
}
|
|
@@ -21,11 +21,11 @@ var _constants2 = require("../constants");
|
|
|
21
21
|
* @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
|
|
22
22
|
* @param {enum} level - the log level enum
|
|
23
23
|
* @param {boolean} [autoCaptured=true] - True if log was captured from auto wrapping. False if it was captured from the API manual usage.
|
|
24
|
-
* @param {object=}
|
|
24
|
+
* @param {object=} targets - the optional targets found
|
|
25
25
|
*/
|
|
26
|
-
function bufferLog(ee, message, customAttributes = {}, level = _constants2.LOG_LEVELS.INFO, autoCaptured = true,
|
|
26
|
+
function bufferLog(ee, message, customAttributes = {}, level = _constants2.LOG_LEVELS.INFO, autoCaptured = true, targets, timestamp = (0, _now.now)()) {
|
|
27
27
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/logging/".concat(level.toLowerCase(), "/called")], undefined, _features.FEATURE_NAMES.metrics, ee);
|
|
28
|
-
(0, _handle.handle)(_constants2.LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, autoCaptured,
|
|
28
|
+
(0, _handle.handle)(_constants2.LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, autoCaptured, targets], undefined, _features.FEATURE_NAMES.logging, ee);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -160,7 +160,9 @@ function register(agentRef, target, parent) {
|
|
|
160
160
|
setUserId: value => setLocalValue('enduser.id', value),
|
|
161
161
|
/** metadata */
|
|
162
162
|
metadata: {
|
|
163
|
-
customAttributes
|
|
163
|
+
get customAttributes() {
|
|
164
|
+
return attrs;
|
|
165
|
+
},
|
|
164
166
|
target,
|
|
165
167
|
timings
|
|
166
168
|
}
|
|
@@ -7,32 +7,17 @@ import { ee } from '../event-emitter/contextual-ee';
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns a function for use as a replacer parameter in JSON.stringify() to handle circular references.
|
|
10
|
-
* Uses an array to track the current ancestor chain, allowing the same object to appear
|
|
11
|
-
* multiple times in the structure as long as it's not a circular reference.
|
|
12
10
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value MDN - Cyclical object value}
|
|
13
|
-
* @returns {Function} A function that filters out
|
|
11
|
+
* @returns {Function} A function that filters out values it has seen before.
|
|
14
12
|
*/
|
|
15
13
|
const getCircularReplacer = () => {
|
|
16
|
-
const
|
|
17
|
-
return
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
const thisPos = stack.indexOf(this);
|
|
21
|
-
if (~thisPos) {
|
|
22
|
-
// We're still in the stack, trim it
|
|
23
|
-
stack.splice(thisPos + 1);
|
|
24
|
-
} else {
|
|
25
|
-
// We're not in the stack, add ourselves
|
|
26
|
-
stack.push(this);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Check if value is in the current ancestor chain
|
|
30
|
-
if (~stack.indexOf(value)) {
|
|
14
|
+
const seen = new WeakSet();
|
|
15
|
+
return (key, value) => {
|
|
16
|
+
if (typeof value === 'object' && value !== null) {
|
|
17
|
+
if (seen.has(value)) {
|
|
31
18
|
return;
|
|
32
19
|
}
|
|
33
|
-
|
|
34
|
-
// First call, initialize with root
|
|
35
|
-
stack.push(value);
|
|
20
|
+
seen.add(value);
|
|
36
21
|
}
|
|
37
22
|
return value;
|
|
38
23
|
};
|
|
@@ -17,7 +17,7 @@ export const V2_TYPES = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Returns the registered target associated with a given ID. Returns
|
|
20
|
+
* Returns the registered target associated with a given ID. Returns an empty array if no target is found.
|
|
21
21
|
* @param {string|number} id
|
|
22
22
|
* @param {*} agentRef the agent reference
|
|
23
23
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
@@ -29,7 +29,7 @@ export function getRegisteredTargetsFromId(id, agentRef) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if
|
|
32
|
+
* Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if no target is found.
|
|
33
33
|
* @param {string} filename
|
|
34
34
|
* @param {*} agentRef
|
|
35
35
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
@@ -49,7 +49,7 @@ export function getRegisteredTargetsFromFilename(filename, agentRef) {
|
|
|
49
49
|
* @returns {Object} returns an empty object if args are not supplied or the aggregate instance is not supporting version 2
|
|
50
50
|
*/
|
|
51
51
|
export function getVersion2Attributes(target, aggregateInstance) {
|
|
52
|
-
if (aggregateInstance
|
|
52
|
+
if (!supportsV2(aggregateInstance)) return {};
|
|
53
53
|
const containerAgentEntityGuid = aggregateInstance.agentRef.runtime.appMetadata.agents[0].entityGuid;
|
|
54
54
|
/** if there's no target, but we are in v2 mode, this means the data belongs to the container agent */
|
|
55
55
|
if (!target) {
|
|
@@ -63,24 +63,40 @@ export function getVersion2Attributes(target, aggregateInstance) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Returns the attributes used for duplicating data in version 2 of the harvest endpoint.
|
|
66
|
+
* Returns the attributes used for duplicating data in version 2 of the harvest endpoint.
|
|
67
|
+
* If not valid for duplication, returns an empty object.
|
|
68
|
+
* @note BEST PRACTICE - Caller should call shouldDuplicate() before utilizing this method to determine if duplication attributes should be added to the event.
|
|
67
69
|
* @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
|
|
68
70
|
* @param {*} aggregateInstance the aggregate instance calling the method
|
|
69
71
|
* @returns {Object}
|
|
70
72
|
*/
|
|
71
73
|
export function getVersion2DuplicationAttributes(target, aggregateInstance) {
|
|
72
|
-
if (
|
|
74
|
+
if (!shouldDuplicate(target, aggregateInstance)) return {};
|
|
73
75
|
return {
|
|
74
76
|
'child.id': target.id,
|
|
75
77
|
'child.type': target.type,
|
|
76
78
|
...getVersion2Attributes(undefined, aggregateInstance)
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Determines if an event should be duplicated for a given target and aggregate instance. This is used to determine if duplication attributes should be added to an event and if the event should be sent to the soft nav feature for evaluation.
|
|
84
|
+
* @note This method is intended to be used in conjunction with getVersion2DuplicationAttributes and should be called before it to determine if duplication attributes should be added to an event.
|
|
85
|
+
* @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
|
|
86
|
+
* @param {*} aggregateInstance The aggregate instance calling the method. This is needed to check if duplication is enabled and if the harvest endpoint version supports it.
|
|
87
|
+
* @returns {boolean} returns true if the event should be duplicated for the target, false otherwise
|
|
88
|
+
*/
|
|
89
|
+
export function shouldDuplicate(target, aggregateInstance) {
|
|
90
|
+
return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.duplicate_registered_data;
|
|
81
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Finds the registered targets from the stack trace for a given agent reference.
|
|
95
|
+
* @param {*} agentRef The agent reference to use for finding targets.
|
|
96
|
+
* @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
|
|
97
|
+
*/
|
|
82
98
|
export function findTargetsFromStackTrace(agentRef) {
|
|
83
|
-
if (!agentRef?.init.api.allow_registered_children) return [];
|
|
99
|
+
if (!agentRef?.init.api.allow_registered_children) return [undefined];
|
|
84
100
|
const targets = [];
|
|
85
101
|
try {
|
|
86
102
|
var urls = extractUrlsFromStack(getDeepStackTrace());
|
|
@@ -91,5 +107,17 @@ export function findTargetsFromStackTrace(agentRef) {
|
|
|
91
107
|
} catch (err) {
|
|
92
108
|
// Silent catch to prevent errors from propagating
|
|
93
109
|
}
|
|
110
|
+
if (!targets.length) targets.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
|
|
94
111
|
return targets;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Determines if the aggregate instance supports version 2 of the harvest endpoint. Nearly all the V2 logic "depends" on
|
|
116
|
+
* the harvest endpoint version, so this is the main gatekeeper method for whether or not V2 logic should be executed across the
|
|
117
|
+
* various functions in this module.
|
|
118
|
+
* @param {*} aggregateInstance The aggregate instance to check.
|
|
119
|
+
* @returns {boolean} Returns true if the aggregate instance supports version 2, false otherwise.
|
|
120
|
+
*/
|
|
121
|
+
function supportsV2(aggregateInstance) {
|
|
122
|
+
return aggregateInstance?.harvestEndpointVersion === 2;
|
|
95
123
|
}
|
|
@@ -72,8 +72,6 @@ export function wrapFetch(sharedEE, agentRef) {
|
|
|
72
72
|
var args = [...arguments];
|
|
73
73
|
const ctx = {};
|
|
74
74
|
const targets = findTargetsFromStackTrace(agentRef);
|
|
75
|
-
// undefined target reports to container
|
|
76
|
-
if (!targets.length) targets.push(undefined);
|
|
77
75
|
|
|
78
76
|
// we are wrapping args in an array so we can preserve the reference
|
|
79
77
|
ee.emit(prefix + 'before-start', [args], ctx);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { ee } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { bundleId } from '../ids/bundle-id';
|
|
12
|
+
import { findTargetsFromStackTrace } from '../util/v2';
|
|
12
13
|
export const flag = "nr@original:".concat(bundleId);
|
|
13
14
|
const LONG_TASK_THRESHOLD = 50;
|
|
14
15
|
|
|
@@ -33,7 +34,7 @@ export default createWrapperWithEmitter;
|
|
|
33
34
|
* @param {boolean} always - If `true`, emit events even if already emitting an event.
|
|
34
35
|
* @returns {function} The wrapped function.
|
|
35
36
|
*/
|
|
36
|
-
export function createWrapperWithEmitter(emitter, always) {
|
|
37
|
+
export function createWrapperWithEmitter(emitter, always, agentRef) {
|
|
37
38
|
emitter || (emitter = ee);
|
|
38
39
|
wrapFn.inPlace = inPlace;
|
|
39
40
|
|
|
@@ -52,9 +53,10 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
52
53
|
* @param {function|object} getContext - The function or object that will serve as the 'this' context for handlers of events emitted by this wrapper.
|
|
53
54
|
* @param {string} methodName - The name of the method being wrapped.
|
|
54
55
|
* @param {boolean} bubble - If true, emitted events should also bubble up to the old emitter upon which the `emitter` in the current scope was based (if it defines one).
|
|
56
|
+
* @param {boolean} [evaluateStack] - If true, the wrapper will attempt to evaluate the stack of the executed wrapped function to find targets of the execution (ex. the MFE source of a console.log).
|
|
55
57
|
* @returns {function} The wrapped function.
|
|
56
58
|
*/
|
|
57
|
-
function wrapFn(fn, prefix, getContext, methodName, bubble) {
|
|
59
|
+
function wrapFn(fn, prefix, getContext, methodName, bubble, evaluateStack) {
|
|
58
60
|
// Unless fn is both wrappable and unwrapped, return it unchanged.
|
|
59
61
|
if (notWrappable(fn)) return fn;
|
|
60
62
|
if (!prefix) prefix = '';
|
|
@@ -73,9 +75,15 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
73
75
|
var ctx;
|
|
74
76
|
var result;
|
|
75
77
|
let thrownError;
|
|
78
|
+
let targets;
|
|
76
79
|
try {
|
|
77
80
|
originalThis = this;
|
|
78
81
|
args = [...arguments];
|
|
82
|
+
|
|
83
|
+
// certain wrappers can inform the function wrapper to evaluate the stack of the executed wrapped function to find targets of the execution
|
|
84
|
+
// (e.g. wrap-logger can inform this method to find try to find the MFE source of a console.log)
|
|
85
|
+
targets = evaluateStack ? findTargetsFromStackTrace(agentRef) : [undefined]; // undefined target always maps to the container agent
|
|
86
|
+
|
|
79
87
|
if (typeof getContext === 'function') {
|
|
80
88
|
ctx = getContext(args, originalThis);
|
|
81
89
|
} else {
|
|
@@ -86,7 +94,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
// Warning: start events may mutate args!
|
|
89
|
-
safeEmit(prefix + 'start', [args, originalThis, methodName], ctx, bubble);
|
|
97
|
+
safeEmit(prefix + 'start', [args, originalThis, methodName, targets], ctx, bubble);
|
|
90
98
|
const fnStartTime = performance.now();
|
|
91
99
|
let fnEndTime;
|
|
92
100
|
try {
|
|
@@ -95,7 +103,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
95
103
|
return result;
|
|
96
104
|
} catch (err) {
|
|
97
105
|
fnEndTime = performance.now();
|
|
98
|
-
safeEmit(prefix + 'err', [args, originalThis, err], ctx, bubble);
|
|
106
|
+
safeEmit(prefix + 'err', [args, originalThis, err, targets], ctx, bubble);
|
|
99
107
|
// rethrow error so we don't effect execution by observing.
|
|
100
108
|
thrownError = err;
|
|
101
109
|
throw thrownError;
|
|
@@ -112,10 +120,10 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
112
120
|
};
|
|
113
121
|
// standalone long task message
|
|
114
122
|
if (task.isLongTask) {
|
|
115
|
-
safeEmit('long-task', [task, originalThis], ctx, bubble);
|
|
123
|
+
safeEmit('long-task', [task, originalThis, targets], ctx, bubble);
|
|
116
124
|
}
|
|
117
125
|
// -end message also includes the task execution info
|
|
118
|
-
safeEmit(prefix + 'end', [args, originalThis, result], ctx, bubble);
|
|
126
|
+
safeEmit(prefix + 'end', [args, originalThis, result, targets], ctx, bubble);
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
}
|
|
@@ -131,7 +139,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
131
139
|
* @param {boolean} [bubble=false] If `true`, emitted events should also bubble up to the old emitter upon which
|
|
132
140
|
* the `emitter` in the current scope was based (if it defines one).
|
|
133
141
|
*/
|
|
134
|
-
function inPlace(obj, methods, prefix, getContext, bubble) {
|
|
142
|
+
function inPlace(obj, methods, prefix, getContext, bubble, evaluateStack) {
|
|
135
143
|
if (!prefix) prefix = '';
|
|
136
144
|
|
|
137
145
|
// If prefix starts with '-' set this boolean to add the method name to the prefix before passing each one to wrap.
|
|
@@ -142,7 +150,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
142
150
|
|
|
143
151
|
// Unless fn is both wrappable and unwrapped, bail so we don't add extra properties with undefined values.
|
|
144
152
|
if (notWrappable(fn)) continue;
|
|
145
|
-
obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble);
|
|
153
|
+
obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble, evaluateStack);
|
|
146
154
|
}
|
|
147
155
|
}
|
|
148
156
|
|