@newrelic/browser-agent 1.262.0 → 1.263.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/config/state/configurable.js +4 -4
  3. package/dist/cjs/common/config/state/init.js +5 -3
  4. package/dist/cjs/common/constants/env.cdn.js +1 -1
  5. package/dist/cjs/common/constants/env.npm.js +1 -1
  6. package/dist/cjs/common/context/shared-context.js +2 -2
  7. package/dist/cjs/common/drain/drain.js +1 -1
  8. package/dist/cjs/common/harvest/harvest.js +1 -1
  9. package/dist/cjs/common/session/session-entity.js +2 -2
  10. package/dist/cjs/common/timing/time-keeper.js +13 -2
  11. package/dist/cjs/common/util/console.js +3 -4
  12. package/dist/cjs/common/util/obfuscate.js +3 -3
  13. package/dist/cjs/common/wrap/wrap-logger.js +1 -2
  14. package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
  15. package/dist/cjs/features/logging/aggregate/index.js +5 -5
  16. package/dist/cjs/features/logging/constants.js +2 -5
  17. package/dist/cjs/features/metrics/aggregate/index.js +16 -0
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +20 -4
  19. package/dist/cjs/features/session_replay/aggregate/index.js +1 -1
  20. package/dist/cjs/features/session_replay/shared/recorder.js +1 -1
  21. package/dist/cjs/features/spa/aggregate/index.js +1 -1
  22. package/dist/cjs/features/utils/aggregate-base.js +4 -3
  23. package/dist/cjs/features/utils/instrument-base.js +2 -2
  24. package/dist/cjs/loaders/agent-base.js +1 -1
  25. package/dist/cjs/loaders/agent.js +3 -4
  26. package/dist/cjs/loaders/api/api.js +9 -7
  27. package/dist/cjs/loaders/micro-agent.js +5 -5
  28. package/dist/esm/common/config/state/configurable.js +4 -4
  29. package/dist/esm/common/config/state/init.js +5 -3
  30. package/dist/esm/common/constants/env.cdn.js +1 -1
  31. package/dist/esm/common/constants/env.npm.js +1 -1
  32. package/dist/esm/common/context/shared-context.js +2 -2
  33. package/dist/esm/common/drain/drain.js +1 -1
  34. package/dist/esm/common/harvest/harvest.js +1 -1
  35. package/dist/esm/common/session/session-entity.js +2 -2
  36. package/dist/esm/common/timing/time-keeper.js +12 -2
  37. package/dist/esm/common/util/console.js +3 -4
  38. package/dist/esm/common/util/obfuscate.js +3 -3
  39. package/dist/esm/common/wrap/wrap-logger.js +1 -2
  40. package/dist/esm/common/wrap/wrap-xhr.js +1 -1
  41. package/dist/esm/features/logging/aggregate/index.js +6 -6
  42. package/dist/esm/features/logging/constants.js +1 -4
  43. package/dist/esm/features/metrics/aggregate/index.js +16 -0
  44. package/dist/esm/features/page_view_event/aggregate/index.js +21 -5
  45. package/dist/esm/features/session_replay/aggregate/index.js +1 -1
  46. package/dist/esm/features/session_replay/shared/recorder.js +1 -1
  47. package/dist/esm/features/spa/aggregate/index.js +1 -1
  48. package/dist/esm/features/utils/aggregate-base.js +4 -3
  49. package/dist/esm/features/utils/instrument-base.js +2 -2
  50. package/dist/esm/loaders/agent-base.js +1 -1
  51. package/dist/esm/loaders/agent.js +3 -4
  52. package/dist/esm/loaders/api/api.js +9 -7
  53. package/dist/esm/loaders/micro-agent.js +5 -5
  54. package/dist/types/common/config/state/init.d.ts.map +1 -1
  55. package/dist/types/common/timing/time-keeper.d.ts +2 -1
  56. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  57. package/dist/types/common/util/console.d.ts +1 -1
  58. package/dist/types/common/util/console.d.ts.map +1 -1
  59. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  60. package/dist/types/features/logging/constants.d.ts +0 -3
  61. package/dist/types/features/logging/constants.d.ts.map +1 -1
  62. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  63. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  64. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  65. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  66. package/dist/types/loaders/agent.d.ts.map +1 -1
  67. package/dist/types/loaders/api/api.d.ts.map +1 -1
  68. package/package.json +1 -1
  69. package/src/common/config/state/configurable.js +4 -4
  70. package/src/common/config/state/init.js +4 -3
  71. package/src/common/context/shared-context.js +2 -2
  72. package/src/common/drain/drain.js +1 -1
  73. package/src/common/harvest/harvest.js +1 -1
  74. package/src/common/session/session-entity.js +2 -2
  75. package/src/common/timing/time-keeper.js +14 -2
  76. package/src/common/util/console.js +3 -4
  77. package/src/common/util/obfuscate.js +3 -3
  78. package/src/common/wrap/wrap-logger.js +1 -2
  79. package/src/common/wrap/wrap-xhr.js +1 -1
  80. package/src/features/logging/aggregate/index.js +6 -6
  81. package/src/features/logging/constants.js +0 -4
  82. package/src/features/metrics/aggregate/index.js +12 -0
  83. package/src/features/page_view_event/aggregate/index.js +22 -5
  84. package/src/features/session_replay/aggregate/index.js +1 -1
  85. package/src/features/session_replay/shared/recorder.js +1 -1
  86. package/src/features/spa/aggregate/index.js +1 -1
  87. package/src/features/utils/aggregate-base.js +4 -3
  88. package/src/features/utils/instrument-base.js +2 -2
  89. package/src/loaders/agent-base.js +1 -1
  90. package/src/loaders/agent.js +3 -4
  91. package/src/loaders/api/api.js +9 -7
  92. package/src/loaders/micro-agent.js +5 -5
@@ -65,11 +65,11 @@ class MicroAgent extends _agentBase.AgentBase {
65
65
  const featNames = nonAutoFeatures;
66
66
  if (features === undefined) features = featNames;else {
67
67
  features = Array.isArray(features) && features.length ? features : [features];
68
- if (features.some(f => !featNames.includes(f))) return (0, _console.warn)("Invalid feature name supplied. Acceptable feature names are: ".concat(featNames));
68
+ if (features.some(f => !featNames.includes(f))) return (0, _console.warn)(37, featNames);
69
69
  if (!features.includes(_features.FEATURE_NAMES.pageViewEvent)) features.push(_features.FEATURE_NAMES.pageViewEvent);
70
70
  }
71
71
  } catch (err) {
72
- (0, _console.warn)('An unexpected issue occurred', err);
72
+ (0, _console.warn)(23, err);
73
73
  }
74
74
  try {
75
75
  const enabledFeatures = (0, _enabledFeatures.getEnabledFeatures)(this.agentIdentifier);
@@ -77,7 +77,7 @@ class MicroAgent extends _agentBase.AgentBase {
77
77
  // a biproduct of doing this is that the "session manager" is automatically handled through importing this feature
78
78
  this.features.page_view_event = new _instrument.Instrument(this.agentIdentifier, this.sharedAggregator);
79
79
  } catch (err) {
80
- (0, _console.warn)('Something prevented the agent from instrumenting.', err);
80
+ (0, _console.warn)(24, err);
81
81
  }
82
82
  (0, _load.onWindowLoad)(() => {
83
83
  // these features do not import an "instrument" file, meaning they are only hooked up to the API.
@@ -93,13 +93,13 @@ class MicroAgent extends _agentBase.AgentBase {
93
93
  Aggregate
94
94
  } = _ref2;
95
95
  this.features[f] = new Aggregate(this.agentIdentifier, this.sharedAggregator);
96
- }).catch(err => (0, _console.warn)('Something prevented the agent from being downloaded.', err));
96
+ }).catch(err => (0, _console.warn)(25, err));
97
97
  }
98
98
  });
99
99
  });
100
100
  return true;
101
101
  } catch (err) {
102
- (0, _console.warn)('Failed to initialize instrument classes.', err);
102
+ (0, _console.warn)(26, err);
103
103
  return false;
104
104
  }
105
105
  }
@@ -1,8 +1,8 @@
1
1
  import { warn } from '../../util/console';
2
2
  export function getModeledObject(obj, model) {
3
3
  try {
4
- if (!obj || typeof obj !== 'object') return warn('Setting a Configurable requires an object as input');
5
- if (!model || typeof model !== 'object') return warn('Setting a Configurable requires a model to set its initial properties');
4
+ if (!obj || typeof obj !== 'object') return warn(3);
5
+ if (!model || typeof model !== 'object') return warn(4);
6
6
  // allow getters and setters to pass from model to target
7
7
  const output = Object.create(Object.getPrototypeOf(model), Object.getOwnPropertyDescriptors(model));
8
8
  const target = Object.keys(output).length === 0 ? obj : output;
@@ -15,11 +15,11 @@ export function getModeledObject(obj, model) {
15
15
  }
16
16
  if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
17
17
  } catch (e) {
18
- warn('An error occurred while setting a property of a Configurable', e);
18
+ warn(1, e);
19
19
  }
20
20
  }
21
21
  return output;
22
22
  } catch (err) {
23
- warn('An error occured while setting a Configurable', err);
23
+ warn(2, err);
24
24
  }
25
25
  }
@@ -109,6 +109,8 @@ const model = () => {
109
109
  // serialize images for collection without public asset url -- right now this is only useful for testing as it easily generates payloads too large to be harvested
110
110
  inline_stylesheet: true,
111
111
  // serialize css for collection without public asset url
112
+ fix_stylesheets: true,
113
+ // fetch missing stylesheet resources for inlining, only works if 'inline_stylesheet' is also true
112
114
  // recording config settings
113
115
  mask_all_inputs: true,
114
116
  // this has a getter/setter to facilitate validation of the selectors
@@ -116,7 +118,7 @@ const model = () => {
116
118
  return hiddenState.mask_selector;
117
119
  },
118
120
  set mask_text_selector(val) {
119
- if (isValidSelector(val)) hiddenState.mask_selector = "".concat(val, ",").concat(nrMask);else if (val === '' || val === null) hiddenState.mask_selector = nrMask;else warn('An invalid session_replay.mask_selector was provided. \'*\' will be used.', val);
121
+ if (isValidSelector(val)) hiddenState.mask_selector = "".concat(val, ",").concat(nrMask);else if (val === '' || val === null) hiddenState.mask_selector = nrMask;else warn(5, val);
120
122
  },
121
123
  // these properties only have getters because they are enforcable constants and should error if someone tries to override them
122
124
  get block_class() {
@@ -134,7 +136,7 @@ const model = () => {
134
136
  return hiddenState.block_selector;
135
137
  },
136
138
  set block_selector(val) {
137
- if (isValidSelector(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') warn('An invalid session_replay.block_selector was provided and will not be used', val);
139
+ if (isValidSelector(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') warn(6, val);
138
140
  },
139
141
  // password: must always be present and true no matter what customer sets
140
142
  get mask_input_options() {
@@ -144,7 +146,7 @@ const model = () => {
144
146
  if (val && typeof val === 'object') hiddenState.mask_input_options = {
145
147
  ...val,
146
148
  password: true
147
- };else warn('An invalid session_replay.mask_input_option was provided and will not be used', val);
149
+ };else warn(7, val);
148
150
  }
149
151
  },
150
152
  session_trace: {
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.262.0";
9
+ export const VERSION = "1.263.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.262.0";
9
+ export const VERSION = "1.263.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@ const model = {
6
6
  export class SharedContext {
7
7
  constructor(context) {
8
8
  try {
9
- if (typeof context !== 'object') return warn('shared context requires an object as input');
9
+ if (typeof context !== 'object') return warn(8);
10
10
  this.sharedContext = {};
11
11
  Object.assign(this.sharedContext, model);
12
12
  Object.entries(context).forEach(_ref => {
@@ -14,7 +14,7 @@ export class SharedContext {
14
14
  if (Object.keys(model).includes(key)) this.sharedContext[key] = value;
15
15
  });
16
16
  } catch (err) {
17
- warn('An error occurred while setting SharedContext', err);
17
+ warn(9, err);
18
18
  }
19
19
  }
20
20
  }
@@ -99,7 +99,7 @@ function drainGroup(agentIdentifier, group) {
99
99
  let activateGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
100
100
  const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee;
101
101
  const handlers = defaultRegister.handlers; // other storage in registerHandler
102
- if (!baseEE.backlog || !handlers) return;
102
+ if (baseEE.aborted || !baseEE.backlog || !handlers) return;
103
103
 
104
104
  // Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
105
105
  if (activateGroup) {
@@ -148,7 +148,7 @@ export class Harvest extends SharedContext {
148
148
  body = stringify(body);
149
149
  }
150
150
  /** Warn --once per endpoint-- if the agent tries to send large payloads */
151
- if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn("The Browser Agent is attempting to send a very large payload to /".concat(endpoint, ". This is usually tied to large amounts of custom attributes. Please check your configurations."));
151
+ if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(28, endpoint);
152
152
  }
153
153
  if (!body || body.length === 0 || body === '{}' || body === '[]') {
154
154
  // If body is null, undefined, or an empty object or array, send an empty string instead
@@ -190,7 +190,7 @@ export class SessionEntity {
190
190
  }
191
191
  return obj;
192
192
  } catch (e) {
193
- warn('Failed to read from storage API', e);
193
+ warn(10, e);
194
194
  // storage is inaccessible
195
195
  return {};
196
196
  }
@@ -216,7 +216,7 @@ export class SessionEntity {
216
216
  return data;
217
217
  } catch (e) {
218
218
  // storage is inaccessible
219
- warn('Failed to write to the storage API', e);
219
+ warn(11, e);
220
220
  return null;
221
221
  }
222
222
  }
@@ -1,5 +1,6 @@
1
1
  import { originTime } from '../constants/runtime';
2
2
  import { getRuntime } from '../config/config';
3
+ const rfc2616Regex = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0-3][0-9]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-9]{4}) ([01][0-9]|2[0-3])(:[0-5][0-9]){2} GMT$/;
3
4
 
4
5
  /**
5
6
  * Class used to adjust the timestamp of harvested data to New Relic server time. This
@@ -29,7 +30,7 @@ export class TimeKeeper {
29
30
  /**
30
31
  * Represents whether the timekeeper is in a state that it can accurately convert
31
32
  * timestamps.
32
- * @type {number}
33
+ * @type {boolean}
33
34
  */
34
35
  #ready = false;
35
36
  constructor(agentIdentifier) {
@@ -42,6 +43,9 @@ export class TimeKeeper {
42
43
  get correctedOriginTime() {
43
44
  return this.#correctedOriginTime;
44
45
  }
46
+ get localTimeDiff() {
47
+ return this.#localTimeDiff;
48
+ }
45
49
 
46
50
  /**
47
51
  * Process a rum request to calculate NR server time.
@@ -50,12 +54,16 @@ export class TimeKeeper {
50
54
  * @param endTime {number} The end time of the RUM request
51
55
  */
52
56
  processRumRequest(rumRequest, startTime, endTime) {
53
- this.processStoredDiff();
57
+ this.processStoredDiff(); // Check session entity for stored time diff
54
58
  if (this.#ready) return; // Server time calculated from session entity
59
+
55
60
  const responseDateHeader = rumRequest.getResponseHeader('Date');
56
61
  if (!responseDateHeader) {
57
62
  throw new Error('Missing date header on rum response.');
58
63
  }
64
+ if (!rfc2616Regex.test(responseDateHeader)) {
65
+ throw new Error('Date header invalid format.');
66
+ }
59
67
  const medianRumOffset = (endTime - startTime) / 2;
60
68
  const serverOffset = startTime + medianRumOffset;
61
69
 
@@ -92,6 +100,8 @@ export class TimeKeeper {
92
100
 
93
101
  /** Process the session entity and use the info to set the main time calculations if present */
94
102
  processStoredDiff() {
103
+ if (this.#ready) return; // Time diff has already been calculated
104
+
95
105
  const storedServerTimeDiff = this.#session?.read()?.serverTimeDiff;
96
106
  if (typeof storedServerTimeDiff === 'number' && !isNaN(storedServerTimeDiff)) {
97
107
  this.#localTimeDiff = storedServerTimeDiff;
@@ -4,8 +4,7 @@
4
4
  * @param {*} [secondary] Secondary data to include, usually an error or object
5
5
  * @returns
6
6
  */
7
- export function warn(message, secondary) {
8
- if (typeof console.warn !== 'function') return;
9
- console.warn("New Relic: ".concat(message));
10
- if (secondary) console.warn(secondary);
7
+ export function warn(code, secondary) {
8
+ if (typeof console.debug !== 'function') return;
9
+ console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
11
10
  }
@@ -45,15 +45,15 @@ export function validateRules(rules) {
45
45
  var invalidRegexDetected = false;
46
46
  for (var i = 0; i < rules.length; i++) {
47
47
  if (!('regex' in rules[i])) {
48
- warn('An obfuscation replacement rule was detected missing a "regex" value.');
48
+ warn(12);
49
49
  invalidRegexDetected = true;
50
50
  } else if (typeof rules[i].regex !== 'string' && !(rules[i].regex instanceof RegExp)) {
51
- warn('An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp)');
51
+ warn(13);
52
52
  invalidRegexDetected = true;
53
53
  }
54
54
  var replacement = rules[i].replacement;
55
55
  if (replacement && typeof replacement !== 'string') {
56
- warn('An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string)');
56
+ warn(14);
57
57
  invalidReplacementDetected = true;
58
58
  }
59
59
  }
@@ -7,7 +7,6 @@
7
7
  * This module is used by: jserrors, spa.
8
8
  */
9
9
 
10
- import { LOGGING_FAILURE_MESSAGE } from '../../features/logging/constants';
11
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
12
11
  import { EventContext } from '../event-emitter/event-context';
13
12
  import { warn } from '../util/console';
@@ -22,7 +21,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function';
22
21
  */
23
22
  // eslint-disable-next-line
24
23
  export function wrapLogger(sharedEE, parent, loggerFn, context) {
25
- if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)');
24
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(29);
26
25
  const ee = scopedEE(sharedEE);
27
26
  const wrapFn = wfn(ee);
28
27
 
@@ -49,7 +49,7 @@ export function wrapXhr(sharedEE) {
49
49
  ee.emit('new-xhr', [xhr], context);
50
50
  xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false));
51
51
  } catch (e) {
52
- warn('An error occurred while intercepting XHR', e);
52
+ warn(15, e);
53
53
  try {
54
54
  ee.emit('internal-error', [e]);
55
55
  } catch (err) {
@@ -6,7 +6,7 @@ import { warn } from '../../../common/util/console';
6
6
  import { stringify } from '../../../common/util/stringify';
7
7
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
8
8
  import { AggregateBase } from '../../utils/aggregate-base';
9
- import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants';
9
+ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants';
10
10
  import { Log } from '../shared/log';
11
11
  import { isValidLogLevel } from '../shared/utils';
12
12
  export class Aggregate extends AggregateBase {
@@ -45,7 +45,7 @@ export class Aggregate extends AggregateBase {
45
45
  if (this.blocked) return;
46
46
  if (!attributes || typeof attributes !== 'object') attributes = {};
47
47
  if (typeof level === 'string') level = level.toUpperCase();
48
- if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, Object.values(LOG_LEVELS));
48
+ if (!isValidLogLevel(level)) return warn(30, level);
49
49
  try {
50
50
  if (typeof message !== 'string') {
51
51
  const stringified = stringify(message);
@@ -57,19 +57,19 @@ export class Aggregate extends AggregateBase {
57
57
  if (!!stringified && stringified !== '{}') message = stringified;else message = String(message);
58
58
  }
59
59
  } catch (err) {
60
- warn('could not cast log message to string', message);
60
+ warn(16, message);
61
61
  return;
62
62
  }
63
- if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message');
63
+ if (typeof message !== 'string' || !message) return warn(32);
64
64
  if (message.length > MAX_PAYLOAD_SIZE) {
65
65
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length]);
66
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...');
66
+ return warn(31, message.slice(0, 25) + '...');
67
67
  }
68
68
  const log = new Log(this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp), message, attributes, level);
69
69
  const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10; // timestamp == 10 chars
70
70
  if (logBytes > MAX_PAYLOAD_SIZE) {
71
71
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes]);
72
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', log.message.slice(0, 25) + '...');
72
+ return warn(31, log.message.slice(0, 25) + '...');
73
73
  }
74
74
  if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
75
75
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.estimatedBytes + logBytes]);
@@ -8,7 +8,4 @@ export const LOG_LEVELS = {
8
8
  };
9
9
  export const LOGGING_EVENT_EMITTER_CHANNEL = 'log';
10
10
  export const FEATURE_NAME = FEATURE_NAMES.logging;
11
- export const MAX_PAYLOAD_SIZE = 1000000;
12
- export const LOGGING_FAILURE_MESSAGE = 'failed to wrap logger: ';
13
- export const LOGGING_LEVEL_FAILURE_MESSAGE = 'invalid log level: ';
14
- export const LOGGING_IGNORED = 'ignored log: ';
11
+ export const MAX_PAYLOAD_SIZE = 1000000;
@@ -102,6 +102,22 @@ export class Aggregate extends AggregateBase {
102
102
  // Check if proxy for either chunks or beacon is being used
103
103
  if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed');
104
104
  if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed');
105
+ if (isBrowserScope && window.MutationObserver) {
106
+ this.storeSupportabilityMetrics('Generic/VideoElement/Added', window.document.querySelectorAll('video').length);
107
+ const mo = new MutationObserver(records => {
108
+ records.forEach(record => {
109
+ record.addedNodes.forEach(addedNode => {
110
+ if (addedNode instanceof HTMLVideoElement) {
111
+ this.storeSupportabilityMetrics('Generic/VideoElement/Added', 1);
112
+ }
113
+ });
114
+ });
115
+ });
116
+ mo.observe(window.document.body, {
117
+ childList: true,
118
+ subtree: true
119
+ });
120
+ }
105
121
  }
106
122
  eachSessionChecks() {
107
123
  if (!isBrowserScope) return;
@@ -1,7 +1,7 @@
1
1
  import { globalScope, isBrowserScope, originTime } from '../../../common/constants/runtime';
2
2
  import { addPT, addPN } from '../../../common/timing/nav-timing';
3
3
  import { stringify } from '../../../common/util/stringify';
4
- import { getInfo, getRuntime } from '../../../common/config/config';
4
+ import { getInfo, getRuntime, isConfigured } from '../../../common/config/config';
5
5
  import { Harvest } from '../../../common/harvest/harvest';
6
6
  import * as CONSTANTS from '../constants';
7
7
  import { getActivatedFeaturesFlags } from './initialized-features';
@@ -25,6 +25,10 @@ export class Aggregate extends AggregateBase {
25
25
  this.firstByteToWindowLoad = 0; // our "frontend" duration
26
26
  this.firstByteToDomContent = 0; // our "dom processing" duration
27
27
  this.timeKeeper = new TimeKeeper(this.agentIdentifier);
28
+ if (!isConfigured(agentIdentifier)) {
29
+ this.ee.abort();
30
+ return warn(43);
31
+ }
28
32
  if (isBrowserScope) {
29
33
  timeToFirstByte.subscribe(_ref => {
30
34
  let {
@@ -47,7 +51,6 @@ export class Aggregate extends AggregateBase {
47
51
  const info = getInfo(this.agentIdentifier);
48
52
  const agentRuntime = getRuntime(this.agentIdentifier);
49
53
  const harvester = new Harvest(this);
50
- if (!info.beacon) return;
51
54
  if (info.queueTime) this.aggregator.store('measures', 'qt', {
52
55
  value: info.queueTime
53
56
  });
@@ -140,11 +143,24 @@ export class Aggregate extends AggregateBase {
140
143
  this.timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
141
144
  if (!this.timeKeeper.ready) throw new Error('TimeKeeper not ready');
142
145
  agentRuntime.timeKeeper = this.timeKeeper;
146
+
147
+ // Check if the time diff is such that we need to capture a supportability metric
148
+ if (this.timeKeeper.localTimeDiff >= 12 * 60 * 60 * 1000) {
149
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed12Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
150
+ } else if (this.timeKeeper.localTimeDiff >= 6 * 60 * 60 * 1000) {
151
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed6Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
152
+ } else if (this.timeKeeper.localTimeDiff >= 60 * 60 * 1000) {
153
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed1Hrs'], undefined, FEATURE_NAMES.metrics, this.ee);
154
+ }
143
155
  } catch (error) {
144
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee);
156
+ if (error?.message?.indexOf('invalid format') > 0) {
157
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/InvalidFormat'], undefined, FEATURE_NAMES.metrics, this.ee);
158
+ } else {
159
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee);
160
+ }
145
161
  drain(this.agentIdentifier, FEATURE_NAMES.metrics, true);
146
162
  this.ee.abort();
147
- warn('Could not calculate New Relic server time. Agent shutting down.', error);
163
+ warn(17, error);
148
164
  return;
149
165
  }
150
166
  try {
@@ -157,7 +173,7 @@ export class Aggregate extends AggregateBase {
157
173
  this.drain();
158
174
  } catch (err) {
159
175
  this.ee.abort();
160
- warn('RUM call failed. Agent shutting down.', err);
176
+ warn(18, err);
161
177
  }
162
178
  }
163
179
  });
@@ -416,7 +416,7 @@ export class Aggregate extends AggregateBase {
416
416
  abort() {
417
417
  let reason = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
418
418
  let data = arguments.length > 1 ? arguments[1] : undefined;
419
- warn("SR aborted -- ".concat(reason.message));
419
+ warn(33, reason.message);
420
420
  handle(SUPPORTABILITY_METRIC_CHANNEL, ["SessionReplay/Abort/".concat(reason.sm), data], undefined, FEATURE_NAMES.metrics, this.ee);
421
421
  this.blocked = true;
422
422
  this.mode = MODE.OFF;
@@ -33,7 +33,7 @@ export class Recorder {
33
33
  /** Config to inform to inline stylesheet contents (true default) */
34
34
  this.shouldInlineStylesheets = getConfigurationValue(this.parent.agentIdentifier, 'session_replay.inline_stylesheet');
35
35
  /** A flag that can be set to false by failing conversions to stop the fetching process */
36
- this.shouldFix = this.shouldInlineStylesheets;
36
+ this.shouldFix = this.shouldInlineStylesheets && getConfigurationValue(this.parent.agentIdentifier, 'session_replay.fix_stylesheets');
37
37
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
38
38
  this.stopRecording = () => {/* no-op until set by rrweb initializer */};
39
39
  }
@@ -693,7 +693,7 @@ export class Aggregate extends AggregateBase {
693
693
  if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.routeChange) smCategory = 'RouteChange';else smCategory = 'Custom';
694
694
  handle(SUPPORTABILITY_METRIC_CHANNEL, ["Spa/Interaction/".concat(smCategory, "/Duration/Ms"), Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE);
695
695
  scheduler?.scheduleHarvest(0);
696
- if (!scheduler) warn('SPA scheduler is not initialized. Saved interaction is not sent!');
696
+ if (!scheduler) warn(19);
697
697
  }
698
698
  function isEnabled() {
699
699
  var enabled = getConfigurationValue(agentIdentifier, 'spa.enabled');
@@ -49,8 +49,9 @@ export class AggregateBase extends FeatureBase {
49
49
  checkConfiguration() {
50
50
  // NOTE: This check has to happen at aggregator load time
51
51
  if (!isConfigured(this.agentIdentifier)) {
52
+ const cdn = gosCDN();
52
53
  let jsAttributes = {
53
- ...gosCDN().info?.jsAttributes
54
+ ...cdn.info?.jsAttributes
54
55
  };
55
56
  try {
56
57
  jsAttributes = {
@@ -63,9 +64,9 @@ export class AggregateBase extends FeatureBase {
63
64
  configure({
64
65
  agentIdentifier: this.agentIdentifier
65
66
  }, {
66
- ...gosCDN(),
67
+ ...cdn,
67
68
  info: {
68
- ...gosCDN().info,
69
+ ...cdn.info,
69
70
  jsAttributes
70
71
  },
71
72
  runtime: getRuntime(this.agentIdentifier)
@@ -87,7 +87,7 @@ export class InstrumentBase extends FeatureBase {
87
87
  session = setupAgentSession(this.agentIdentifier);
88
88
  }
89
89
  } catch (e) {
90
- warn('A problem occurred when starting up session manager. This page will not start or extend any session.', e);
90
+ warn(20, e);
91
91
  this.ee.emit('internal-error', [e]);
92
92
  if (this.featureName === FEATURE_NAMES.sessionReplay) this.abortHandler?.(); // SR should stop recording if session DNE
93
93
  }
@@ -111,7 +111,7 @@ export class InstrumentBase extends FeatureBase {
111
111
  this.featAggregate = new Aggregate(this.agentIdentifier, this.aggregator, argsObjFromInstrument);
112
112
  loadedSuccessfully(true);
113
113
  } catch (e) {
114
- warn("Downloading and initializing ".concat(this.featureName, " failed..."), e);
114
+ warn(34, e);
115
115
  this.abortHandler?.(); // undo any important alterations made to the page
116
116
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
117
117
  drain(this.agentIdentifier, this.featureName, true);
@@ -28,7 +28,7 @@ export class AgentBase {
28
28
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
29
29
  args[_key - 1] = arguments[_key];
30
30
  }
31
- if (typeof this.api?.[methodName] !== 'function') warn("Call to agent api ".concat(methodName, " failed. The API is not currently initialized."));else return this.api[methodName](...args);
31
+ if (typeof this.api?.[methodName] !== 'function') warn(35, methodName);else return this.api[methodName](...args);
32
32
  }
33
33
 
34
34
  /**
@@ -13,7 +13,6 @@ import { Instrument as PageViewEvent } from '../features/page_view_event/instrum
13
13
  import { Aggregator } from '../common/aggregate/aggregator';
14
14
  import { gosNREUM, setNREUMInitializedAgent } from '../common/window/nreum';
15
15
  import { warn } from '../common/util/console';
16
- import { stringify } from '../common/util/stringify';
17
16
  import { globalScope } from '../common/constants/runtime';
18
17
 
19
18
  /**
@@ -30,7 +29,7 @@ export class Agent extends AgentBase {
30
29
  if (!globalScope) {
31
30
  // We could not determine the runtime environment. Short-circuite the agent here
32
31
  // to avoid possible exceptions later that may cause issues with customer's application.
33
- warn('Failed to initialize the agent. Could not determine the runtime environment.');
32
+ warn(21);
34
33
  return;
35
34
  }
36
35
  this.sharedAggregator = new Aggregator({
@@ -69,11 +68,11 @@ export class Agent extends AgentBase {
69
68
  if (!this.runSoftNavOverSpa && InstrumentCtor.featureName === FEATURE_NAMES.softNav) return;
70
69
  const dependencies = getFeatureDependencyNames(InstrumentCtor.featureName);
71
70
  const hasAllDeps = dependencies.every(featName => featName in this.features); // any other feature(s) this depends on should've been initialized on prior iterations by priority order
72
- if (!hasAllDeps) warn("".concat(InstrumentCtor.featureName, " is enabled but one or more dependent features has not been initialized (").concat(stringify(dependencies), "). This may cause unintended consequences or missing data..."));
71
+ if (!hasAllDeps) warn(36, InstrumentCtor.featureName);
73
72
  this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator);
74
73
  });
75
74
  } catch (err) {
76
- warn('Failed to initialize all enabled instrument classes (agent aborted) -', err);
75
+ warn(22, err);
77
76
  for (const featName in this.features) {
78
77
  // this.features hold only features that have been instantiated
79
78
  this.features[featName].abortHandler?.();
@@ -36,7 +36,7 @@ export function setTopLevelCallers() {
36
36
  let returnVals = [];
37
37
  Object.values(nr.initializedAgents).forEach(val => {
38
38
  if (!val || !val.api) {
39
- warn("Call to api '".concat(fnName, "' made before agent fully initialized."));
39
+ warn(38, fnName);
40
40
  } else if (val.exposed && val.api[fnName]) {
41
41
  returnVals.push(val.api[fnName](...args));
42
42
  }
@@ -62,6 +62,7 @@ export function setAPI(agentIdentifier, forceDrain) {
62
62
  customAttributes = {},
63
63
  level = LOG_LEVELS.INFO
64
64
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
65
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/log/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
65
66
  bufferLog(instanceEE, message, customAttributes, level);
66
67
  };
67
68
  apiInterface.wrapLogger = function (parent, functionName) {
@@ -69,6 +70,7 @@ export function setAPI(agentIdentifier, forceDrain) {
69
70
  customAttributes = {},
70
71
  level = LOG_LEVELS.INFO
71
72
  } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
73
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/wrapLogger/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
72
74
  wrapLogger(instanceEE, parent, functionName, {
73
75
  customAttributes,
74
76
  level
@@ -113,11 +115,11 @@ export function setAPI(agentIdentifier, forceDrain) {
113
115
  apiInterface.setCustomAttribute = function (name, value) {
114
116
  let persistAttribute = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
115
117
  if (typeof name !== 'string') {
116
- warn("Failed to execute setCustomAttribute.\nName must be a string type, but a type of <".concat(typeof name, "> was provided."));
118
+ warn(39, typeof name);
117
119
  return;
118
120
  }
119
121
  if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
120
- warn("Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <".concat(typeof value, "> was provided."));
122
+ warn(40, typeof value);
121
123
  return;
122
124
  }
123
125
  return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute);
@@ -129,7 +131,7 @@ export function setAPI(agentIdentifier, forceDrain) {
129
131
  */
130
132
  apiInterface.setUserId = function (value) {
131
133
  if (!(typeof value === 'string' || value === null)) {
132
- warn("Failed to execute setUserId.\nNon-null value must be a string type, but a type of <".concat(typeof value, "> was provided."));
134
+ warn(41, typeof value);
133
135
  return;
134
136
  }
135
137
  return appendJsAttribute('enduser.id', value, 'setUserId', true);
@@ -142,7 +144,7 @@ export function setAPI(agentIdentifier, forceDrain) {
142
144
  */
143
145
  apiInterface.setApplicationVersion = function (value) {
144
146
  if (!(typeof value === 'string' || value === null)) {
145
- warn("Failed to execute setApplicationVersion. Expected <String | null>, but got <".concat(typeof value, ">."));
147
+ warn(42, typeof value);
146
148
  return;
147
149
  }
148
150
  return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
@@ -152,7 +154,7 @@ export function setAPI(agentIdentifier, forceDrain) {
152
154
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
153
155
  instanceEE.emit('manual-start-all');
154
156
  } catch (err) {
155
- warn('An unexpected issue occurred', err);
157
+ warn(23, err);
156
158
  }
157
159
  };
158
160
  apiInterface[SR_EVENT_EMITTER_TYPES.RECORD] = function () {
@@ -221,7 +223,7 @@ export function setAPI(agentIdentifier, forceDrain) {
221
223
  setAPI(agentIdentifier);
222
224
  drain(agentIdentifier, 'api');
223
225
  }).catch(err => {
224
- warn('Downloading runtime APIs failed...', err);
226
+ warn(27, err);
225
227
  instanceEE.abort();
226
228
  });
227
229
  }