@newrelic/browser-agent 1.261.2 → 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 (103) hide show
  1. package/CHANGELOG.md +27 -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 +22 -16
  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 +2 -0
  14. package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
  15. package/dist/cjs/features/logging/aggregate/index.js +27 -2
  16. package/dist/cjs/features/logging/constants.js +2 -5
  17. package/dist/cjs/features/logging/shared/utils.js +2 -18
  18. package/dist/cjs/features/metrics/aggregate/index.js +16 -0
  19. package/dist/cjs/features/page_view_event/aggregate/index.js +20 -4
  20. package/dist/cjs/features/session_replay/aggregate/index.js +1 -1
  21. package/dist/cjs/features/session_replay/shared/recorder.js +1 -1
  22. package/dist/cjs/features/session_trace/aggregate/index.js +1 -1
  23. package/dist/cjs/features/spa/aggregate/index.js +1 -1
  24. package/dist/cjs/features/utils/aggregate-base.js +4 -3
  25. package/dist/cjs/features/utils/instrument-base.js +2 -2
  26. package/dist/cjs/loaders/agent-base.js +1 -1
  27. package/dist/cjs/loaders/agent.js +3 -4
  28. package/dist/cjs/loaders/api/api.js +13 -16
  29. package/dist/cjs/loaders/micro-agent.js +5 -5
  30. package/dist/esm/common/config/state/configurable.js +4 -4
  31. package/dist/esm/common/config/state/init.js +5 -3
  32. package/dist/esm/common/constants/env.cdn.js +1 -1
  33. package/dist/esm/common/constants/env.npm.js +1 -1
  34. package/dist/esm/common/context/shared-context.js +2 -2
  35. package/dist/esm/common/drain/drain.js +22 -16
  36. package/dist/esm/common/harvest/harvest.js +1 -1
  37. package/dist/esm/common/session/session-entity.js +2 -2
  38. package/dist/esm/common/timing/time-keeper.js +12 -2
  39. package/dist/esm/common/util/console.js +3 -4
  40. package/dist/esm/common/util/obfuscate.js +3 -3
  41. package/dist/esm/common/wrap/wrap-logger.js +2 -0
  42. package/dist/esm/common/wrap/wrap-xhr.js +1 -1
  43. package/dist/esm/features/logging/aggregate/index.js +28 -3
  44. package/dist/esm/features/logging/constants.js +1 -4
  45. package/dist/esm/features/logging/shared/utils.js +2 -18
  46. package/dist/esm/features/metrics/aggregate/index.js +16 -0
  47. package/dist/esm/features/page_view_event/aggregate/index.js +21 -5
  48. package/dist/esm/features/session_replay/aggregate/index.js +1 -1
  49. package/dist/esm/features/session_replay/shared/recorder.js +1 -1
  50. package/dist/esm/features/session_trace/aggregate/index.js +1 -1
  51. package/dist/esm/features/spa/aggregate/index.js +1 -1
  52. package/dist/esm/features/utils/aggregate-base.js +4 -3
  53. package/dist/esm/features/utils/instrument-base.js +2 -2
  54. package/dist/esm/loaders/agent-base.js +1 -1
  55. package/dist/esm/loaders/agent.js +3 -4
  56. package/dist/esm/loaders/api/api.js +15 -18
  57. package/dist/esm/loaders/micro-agent.js +5 -5
  58. package/dist/types/common/config/state/init.d.ts.map +1 -1
  59. package/dist/types/common/drain/drain.d.ts.map +1 -1
  60. package/dist/types/common/timing/time-keeper.d.ts +2 -1
  61. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  62. package/dist/types/common/util/console.d.ts +1 -1
  63. package/dist/types/common/util/console.d.ts.map +1 -1
  64. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  65. package/dist/types/features/logging/aggregate/index.d.ts +1 -1
  66. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  67. package/dist/types/features/logging/constants.d.ts +0 -3
  68. package/dist/types/features/logging/constants.d.ts.map +1 -1
  69. package/dist/types/features/logging/shared/utils.d.ts +1 -1
  70. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  71. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  72. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  73. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  74. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  75. package/dist/types/loaders/agent.d.ts.map +1 -1
  76. package/dist/types/loaders/api/api.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/common/config/state/configurable.js +4 -4
  79. package/src/common/config/state/init.js +4 -3
  80. package/src/common/context/shared-context.js +2 -2
  81. package/src/common/drain/drain.js +21 -16
  82. package/src/common/harvest/harvest.js +1 -1
  83. package/src/common/session/session-entity.js +2 -2
  84. package/src/common/timing/time-keeper.js +14 -2
  85. package/src/common/util/console.js +3 -4
  86. package/src/common/util/obfuscate.js +3 -3
  87. package/src/common/wrap/wrap-logger.js +2 -0
  88. package/src/common/wrap/wrap-xhr.js +1 -1
  89. package/src/features/logging/aggregate/index.js +30 -3
  90. package/src/features/logging/constants.js +0 -4
  91. package/src/features/logging/shared/utils.js +2 -19
  92. package/src/features/metrics/aggregate/index.js +12 -0
  93. package/src/features/page_view_event/aggregate/index.js +22 -5
  94. package/src/features/session_replay/aggregate/index.js +1 -1
  95. package/src/features/session_replay/shared/recorder.js +1 -1
  96. package/src/features/session_trace/aggregate/index.js +1 -1
  97. package/src/features/spa/aggregate/index.js +1 -1
  98. package/src/features/utils/aggregate-base.js +4 -3
  99. package/src/features/utils/instrument-base.js +2 -2
  100. package/src/loaders/agent-base.js +1 -1
  101. package/src/loaders/agent.js +3 -4
  102. package/src/loaders/api/api.js +15 -18
  103. package/src/loaders/micro-agent.js +5 -5
@@ -182,7 +182,7 @@ export class SessionEntity {
182
182
 
183
183
  return obj
184
184
  } catch (e) {
185
- warn('Failed to read from storage API', e)
185
+ warn(10, e)
186
186
  // storage is inaccessible
187
187
  return {}
188
188
  }
@@ -208,7 +208,7 @@ export class SessionEntity {
208
208
  return data
209
209
  } catch (e) {
210
210
  // storage is inaccessible
211
- warn('Failed to write to the storage API', e)
211
+ warn(11, e)
212
212
  return null
213
213
  }
214
214
  }
@@ -1,6 +1,8 @@
1
1
  import { originTime } from '../constants/runtime'
2
2
  import { getRuntime } from '../config/config'
3
3
 
4
+ 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$/
5
+
4
6
  /**
5
7
  * Class used to adjust the timestamp of harvested data to New Relic server time. This
6
8
  * is done by tracking the performance timings of the RUM call and applying a calculation
@@ -29,7 +31,7 @@ export class TimeKeeper {
29
31
  /**
30
32
  * Represents whether the timekeeper is in a state that it can accurately convert
31
33
  * timestamps.
32
- * @type {number}
34
+ * @type {boolean}
33
35
  */
34
36
  #ready = false
35
37
 
@@ -46,6 +48,10 @@ export class TimeKeeper {
46
48
  return this.#correctedOriginTime
47
49
  }
48
50
 
51
+ get localTimeDiff () {
52
+ return this.#localTimeDiff
53
+ }
54
+
49
55
  /**
50
56
  * Process a rum request to calculate NR server time.
51
57
  * @param rumRequest {XMLHttpRequest} The xhr for the rum request
@@ -53,12 +59,16 @@ export class TimeKeeper {
53
59
  * @param endTime {number} The end time of the RUM request
54
60
  */
55
61
  processRumRequest (rumRequest, startTime, endTime) {
56
- this.processStoredDiff()
62
+ this.processStoredDiff() // Check session entity for stored time diff
57
63
  if (this.#ready) return // Server time calculated from session entity
64
+
58
65
  const responseDateHeader = rumRequest.getResponseHeader('Date')
59
66
  if (!responseDateHeader) {
60
67
  throw new Error('Missing date header on rum response.')
61
68
  }
69
+ if (!rfc2616Regex.test(responseDateHeader)) {
70
+ throw new Error('Date header invalid format.')
71
+ }
62
72
 
63
73
  const medianRumOffset = (endTime - startTime) / 2
64
74
  const serverOffset = startTime + medianRumOffset
@@ -96,6 +106,8 @@ export class TimeKeeper {
96
106
 
97
107
  /** Process the session entity and use the info to set the main time calculations if present */
98
108
  processStoredDiff () {
109
+ if (this.#ready) return // Time diff has already been calculated
110
+
99
111
  const storedServerTimeDiff = this.#session?.read()?.serverTimeDiff
100
112
  if (typeof storedServerTimeDiff === 'number' && !isNaN(storedServerTimeDiff)) {
101
113
  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: ${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#${code}`, secondary)
11
10
  }
@@ -49,16 +49,16 @@ export function validateRules (rules) {
49
49
  var invalidRegexDetected = false
50
50
  for (var i = 0; i < rules.length; i++) {
51
51
  if (!('regex' in rules[i])) {
52
- warn('An obfuscation replacement rule was detected missing a "regex" value.')
52
+ warn(12)
53
53
  invalidRegexDetected = true
54
54
  } else if (typeof rules[i].regex !== 'string' && !(rules[i].regex instanceof RegExp)) {
55
- warn('An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp)')
55
+ warn(13)
56
56
  invalidRegexDetected = true
57
57
  }
58
58
 
59
59
  var replacement = rules[i].replacement
60
60
  if (replacement && typeof replacement !== 'string') {
61
- warn('An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string)')
61
+ warn(14)
62
62
  invalidReplacementDetected = true
63
63
  }
64
64
  }
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
11
11
  import { EventContext } from '../event-emitter/event-context'
12
+ import { warn } from '../util/console'
12
13
  import { createWrapperWithEmitter as wfn } from './wrap-function'
13
14
 
14
15
  /**
@@ -20,6 +21,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function'
20
21
  */
21
22
  // eslint-disable-next-line
22
23
  export function wrapLogger(sharedEE, parent, loggerFn, context) {
24
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(29)
23
25
  const ee = scopedEE(sharedEE)
24
26
  const wrapFn = wfn(ee)
25
27
 
@@ -56,7 +56,7 @@ export function wrapXhr (sharedEE) {
56
56
  ee.emit('new-xhr', [xhr], context)
57
57
  xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false))
58
58
  } catch (e) {
59
- warn('An error occurred while intercepting XHR', e)
59
+ warn(15, e)
60
60
  try {
61
61
  ee.emit('internal-error', [e])
62
62
  } catch (err) {
@@ -6,8 +6,9 @@ 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, 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
+ import { isValidLogLevel } from '../shared/utils'
11
12
 
12
13
  export class Aggregate extends AggregateBase {
13
14
  static featureName = FEATURE_NAME
@@ -43,8 +44,34 @@ export class Aggregate extends AggregateBase {
43
44
  })
44
45
  }
45
46
 
46
- handleLog (timestamp, message, attributes, level) {
47
+ handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO) {
47
48
  if (this.blocked) return
49
+
50
+ if (!attributes || typeof attributes !== 'object') attributes = {}
51
+ if (typeof level === 'string') level = level.toUpperCase()
52
+ if (!isValidLogLevel(level)) return warn(30, level)
53
+
54
+ try {
55
+ if (typeof message !== 'string') {
56
+ const stringified = stringify(message)
57
+ /**
58
+ * Error instances convert to `{}` when stringified
59
+ * Symbol converts to '' when stringified
60
+ * other cases tbd
61
+ * */
62
+ if (!!stringified && stringified !== '{}') message = stringified
63
+ else message = String(message)
64
+ }
65
+ } catch (err) {
66
+ warn(16, message)
67
+ return
68
+ }
69
+ if (typeof message !== 'string' || !message) return warn(32)
70
+ if (message.length > MAX_PAYLOAD_SIZE) {
71
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length])
72
+ return warn(31, message.slice(0, 25) + '...')
73
+ }
74
+
48
75
  const log = new Log(
49
76
  this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp),
50
77
  message,
@@ -54,7 +81,7 @@ export class Aggregate extends AggregateBase {
54
81
  const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10 // timestamp == 10 chars
55
82
  if (logBytes > MAX_PAYLOAD_SIZE) {
56
83
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes])
57
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes', log.message.slice(0, 25) + '...')
84
+ return warn(31, log.message.slice(0, 25) + '...')
58
85
  }
59
86
 
60
87
  if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
@@ -13,7 +13,3 @@ export const LOGGING_EVENT_EMITTER_CHANNEL = 'log'
13
13
  export const FEATURE_NAME = FEATURE_NAMES.logging
14
14
 
15
15
  export const MAX_PAYLOAD_SIZE = 1000000
16
-
17
- export const LOGGING_FAILURE_MESSAGE = 'failed to wrap logger: '
18
- export const LOGGING_LEVEL_FAILURE_MESSAGE = 'invalid log level: '
19
- export const LOGGING_IGNORED = 'ignored log: '
@@ -1,7 +1,5 @@
1
1
  import { handle } from '../../../common/event-emitter/handle'
2
2
  import { now } from '../../../common/timing/now'
3
- import { warn } from '../../../common/util/console'
4
- import { stringify } from '../../../common/util/stringify'
5
3
  import { FEATURE_NAMES } from '../../../loaders/features/features'
6
4
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
7
5
  import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
@@ -13,31 +11,16 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
13
11
  * @param {enum} level - the log level enum
14
12
  */
15
13
  export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO) {
16
- try {
17
- if (typeof message !== 'string') {
18
- const stringified = stringify(message)
19
- /**
20
- * Error instances convert to `{}` when stringified
21
- * Symbol converts to '' when stringified
22
- * other cases tbd
23
- * */
24
- if (!!stringified && stringified !== '{}') message = stringified
25
- else message = String(message)
26
- }
27
- } catch (err) {
28
- warn('could not cast log message to string', message)
29
- return
30
- }
31
14
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
32
15
  handle(LOGGING_EVENT_EMITTER_CHANNEL, [now(), message, customAttributes, level], undefined, FEATURE_NAMES.logging, ee)
33
16
  }
34
17
 
35
18
  /**
36
19
  * Checks if a supplied log level is acceptable for use in generating a log event
37
- * @param {string} level
20
+ * @param {string} level -- must be cast to uppercase before running this test
38
21
  * @returns {boolean}
39
22
  */
40
23
  export function isValidLogLevel (level) {
41
24
  if (typeof level !== 'string') return false
42
- return Object.values(LOG_LEVELS).some(logLevel => logLevel.toUpperCase() === level.toUpperCase())
25
+ return Object.values(LOG_LEVELS).some(logLevel => logLevel === level)
43
26
  }
@@ -96,6 +96,18 @@ export class Aggregate extends AggregateBase {
96
96
  // Check if proxy for either chunks or beacon is being used
97
97
  if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed')
98
98
  if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed')
99
+
100
+ if (isBrowserScope && window.MutationObserver) {
101
+ this.storeSupportabilityMetrics('Generic/VideoElement/Added', window.document.querySelectorAll('video').length)
102
+ const mo = new MutationObserver(records => {
103
+ records.forEach(record => {
104
+ record.addedNodes.forEach(addedNode => {
105
+ if (addedNode instanceof HTMLVideoElement) { this.storeSupportabilityMetrics('Generic/VideoElement/Added', 1) }
106
+ })
107
+ })
108
+ })
109
+ mo.observe(window.document.body, { childList: true, subtree: true })
110
+ }
99
111
  }
100
112
 
101
113
  eachSessionChecks () {
@@ -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'
@@ -28,6 +28,11 @@ export class Aggregate extends AggregateBase {
28
28
  this.firstByteToDomContent = 0 // our "dom processing" duration
29
29
  this.timeKeeper = new TimeKeeper(this.agentIdentifier)
30
30
 
31
+ if (!isConfigured(agentIdentifier)) {
32
+ this.ee.abort()
33
+ return warn(43)
34
+ }
35
+
31
36
  if (isBrowserScope) {
32
37
  timeToFirstByte.subscribe(({ value, attrs }) => {
33
38
  const navEntry = attrs.navigationEntry
@@ -48,7 +53,6 @@ export class Aggregate extends AggregateBase {
48
53
  const agentRuntime = getRuntime(this.agentIdentifier)
49
54
  const harvester = new Harvest(this)
50
55
 
51
- if (!info.beacon) return
52
56
  if (info.queueTime) this.aggregator.store('measures', 'qt', { value: info.queueTime })
53
57
  if (info.applicationTime) this.aggregator.store('measures', 'ap', { value: info.applicationTime })
54
58
 
@@ -126,11 +130,24 @@ export class Aggregate extends AggregateBase {
126
130
  if (!this.timeKeeper.ready) throw new Error('TimeKeeper not ready')
127
131
 
128
132
  agentRuntime.timeKeeper = this.timeKeeper
133
+
134
+ // Check if the time diff is such that we need to capture a supportability metric
135
+ if (this.timeKeeper.localTimeDiff >= 12 * 60 * 60 * 1000) {
136
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed12Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
137
+ } else if (this.timeKeeper.localTimeDiff >= 6 * 60 * 60 * 1000) {
138
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed6Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
139
+ } else if (this.timeKeeper.localTimeDiff >= 60 * 60 * 1000) {
140
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed1Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
141
+ }
129
142
  } catch (error) {
130
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee)
143
+ if (error?.message?.indexOf('invalid format') > 0) {
144
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/InvalidFormat'], undefined, FEATURE_NAMES.metrics, this.ee)
145
+ } else {
146
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee)
147
+ }
131
148
  drain(this.agentIdentifier, FEATURE_NAMES.metrics, true)
132
149
  this.ee.abort()
133
- warn('Could not calculate New Relic server time. Agent shutting down.', error)
150
+ warn(17, error)
134
151
  return
135
152
  }
136
153
 
@@ -141,7 +158,7 @@ export class Aggregate extends AggregateBase {
141
158
  this.drain()
142
159
  } catch (err) {
143
160
  this.ee.abort()
144
- warn('RUM call failed. Agent shutting down.', err)
161
+ warn(18, err)
145
162
  }
146
163
  }
147
164
  })
@@ -394,7 +394,7 @@ export class Aggregate extends AggregateBase {
394
394
 
395
395
  /** Abort the feature, once aborted it will not resume */
396
396
  abort (reason = {}, data) {
397
- warn(`SR aborted -- ${reason.message}`)
397
+ warn(33, reason.message)
398
398
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`SessionReplay/Abort/${reason.sm}`, data], undefined, FEATURE_NAMES.metrics, this.ee)
399
399
  this.blocked = true
400
400
  this.mode = MODE.OFF
@@ -35,7 +35,7 @@ export class Recorder {
35
35
  /** Config to inform to inline stylesheet contents (true default) */
36
36
  this.shouldInlineStylesheets = getConfigurationValue(this.parent.agentIdentifier, 'session_replay.inline_stylesheet')
37
37
  /** A flag that can be set to false by failing conversions to stop the fetching process */
38
- this.shouldFix = this.shouldInlineStylesheets
38
+ this.shouldFix = this.shouldInlineStylesheets && getConfigurationValue(this.parent.agentIdentifier, 'session_replay.fix_stylesheets')
39
39
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
40
40
  this.stopRecording = () => { /* no-op until set by rrweb initializer */ }
41
41
  }
@@ -205,6 +205,6 @@ export class Aggregate extends AggregateBase {
205
205
  this.blocked = true
206
206
  this.mode = MODE.OFF
207
207
  this.agentRuntime.session.write({ sessionTraceMode: this.mode })
208
- this.scheduler.stopTimer()
208
+ this.scheduler?.stopTimer()
209
209
  }
210
210
  }
@@ -757,7 +757,7 @@ export class Aggregate extends AggregateBase {
757
757
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`Spa/Interaction/${smCategory}/Duration/Ms`, Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE)
758
758
 
759
759
  scheduler?.scheduleHarvest(0)
760
- if (!scheduler) warn('SPA scheduler is not initialized. Saved interaction is not sent!')
760
+ if (!scheduler) warn(19)
761
761
  }
762
762
 
763
763
  function isEnabled () {
@@ -49,7 +49,8 @@ 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
- let jsAttributes = { ...gosCDN().info?.jsAttributes }
52
+ const cdn = gosCDN()
53
+ let jsAttributes = { ...cdn.info?.jsAttributes }
53
54
  try {
54
55
  jsAttributes = {
55
56
  ...jsAttributes,
@@ -59,9 +60,9 @@ export class AggregateBase extends FeatureBase {
59
60
  // do nothing
60
61
  }
61
62
  configure({ agentIdentifier: this.agentIdentifier }, {
62
- ...gosCDN(),
63
+ ...cdn,
63
64
  info: {
64
- ...gosCDN().info,
65
+ ...cdn.info,
65
66
  jsAttributes
66
67
  },
67
68
  runtime: getRuntime(this.agentIdentifier)
@@ -85,7 +85,7 @@ export class InstrumentBase extends FeatureBase {
85
85
  session = setupAgentSession(this.agentIdentifier)
86
86
  }
87
87
  } catch (e) {
88
- warn('A problem occurred when starting up session manager. This page will not start or extend any session.', e)
88
+ warn(20, e)
89
89
  this.ee.emit('internal-error', [e])
90
90
  if (this.featureName === FEATURE_NAMES.sessionReplay) this.abortHandler?.() // SR should stop recording if session DNE
91
91
  }
@@ -105,7 +105,7 @@ export class InstrumentBase extends FeatureBase {
105
105
  this.featAggregate = new Aggregate(this.agentIdentifier, this.aggregator, argsObjFromInstrument)
106
106
  loadedSuccessfully(true)
107
107
  } catch (e) {
108
- warn(`Downloading and initializing ${this.featureName} failed...`, e)
108
+ warn(34, e)
109
109
  this.abortHandler?.() // undo any important alterations made to the page
110
110
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
111
111
  drain(this.agentIdentifier, this.featureName, true)
@@ -25,7 +25,7 @@ export class AgentBase {
25
25
  * @param {...any} args
26
26
  */
27
27
  #callMethod (methodName, ...args) {
28
- if (typeof this.api?.[methodName] !== 'function') warn(`Call to agent api ${methodName} failed. The API is not currently initialized.`)
28
+ if (typeof this.api?.[methodName] !== 'function') warn(35, methodName)
29
29
  else return this.api[methodName](...args)
30
30
  }
31
31
 
@@ -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
  /**
@@ -31,7 +30,7 @@ export class Agent extends AgentBase {
31
30
  if (!globalScope) {
32
31
  // We could not determine the runtime environment. Short-circuite the agent here
33
32
  // to avoid possible exceptions later that may cause issues with customer's application.
34
- warn('Failed to initialize the agent. Could not determine the runtime environment.')
33
+ warn(21)
35
34
  return
36
35
  }
37
36
 
@@ -74,12 +73,12 @@ export class Agent extends AgentBase {
74
73
 
75
74
  const dependencies = getFeatureDependencyNames(InstrumentCtor.featureName)
76
75
  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
77
- if (!hasAllDeps) warn(`${InstrumentCtor.featureName} is enabled but one or more dependent features has not been initialized (${stringify(dependencies)}). This may cause unintended consequences or missing data...`)
76
+ if (!hasAllDeps) warn(36, InstrumentCtor.featureName)
78
77
 
79
78
  this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator)
80
79
  })
81
80
  } catch (err) {
82
- warn('Failed to initialize all enabled instrument classes (agent aborted) -', err)
81
+ warn(22, err)
83
82
  for (const featName in this.features) { // this.features hold only features that have been instantiated
84
83
  this.features[featName].abortHandler?.()
85
84
  }
@@ -16,8 +16,8 @@ import { apiMethods, asyncApiMethods } from './api-methods'
16
16
  import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants'
17
17
  import { now } from '../../common/timing/now'
18
18
  import { MODE } from '../../common/session/constants'
19
- import { LOGGING_FAILURE_MESSAGE, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../../features/logging/constants'
20
- import { bufferLog, isValidLogLevel } from '../../features/logging/shared/utils'
19
+ import { LOG_LEVELS } from '../../features/logging/constants'
20
+ import { bufferLog } from '../../features/logging/shared/utils'
21
21
  import { wrapLogger } from '../../common/wrap/wrap-logger'
22
22
 
23
23
  export function setTopLevelCallers () {
@@ -29,7 +29,9 @@ export function setTopLevelCallers () {
29
29
  function caller (fnName, ...args) {
30
30
  let returnVals = []
31
31
  Object.values(nr.initializedAgents).forEach(val => {
32
- if (val.exposed && val.api[fnName]) {
32
+ if (!val || !val.api) {
33
+ warn(38, fnName)
34
+ } else if (val.exposed && val.api[fnName]) {
33
35
  returnVals.push(val.api[fnName](...args))
34
36
  }
35
37
  })
@@ -55,18 +57,13 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
55
57
  var spaPrefix = prefix + 'ixn-'
56
58
 
57
59
  apiInterface.log = function (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}) {
58
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {}
59
- if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message')
60
- if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS)
61
- if (message.length > MAX_PAYLOAD_SIZE) return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...')
62
- bufferLog(instanceEE, message, customAttributes, level.toUpperCase())
60
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/log/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
61
+ bufferLog(instanceEE, message, customAttributes, level)
63
62
  }
64
63
 
65
64
  apiInterface.wrapLogger = (parent, functionName, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}) => {
66
- if (!customAttributes || typeof customAttributes !== 'object') customAttributes = {}
67
- if (!(typeof parent === 'object' && !!parent && typeof functionName === 'string' && !!functionName && typeof parent[functionName] === 'function' && typeof customAttributes === 'object')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)')
68
- if (!isValidLogLevel(level)) return warn(LOGGING_FAILURE_MESSAGE + LOGGING_LEVEL_FAILURE_MESSAGE + level, LOG_LEVELS)
69
- wrapLogger(instanceEE, parent, functionName, { customAttributes, level: level.toUpperCase() })
65
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/wrapLogger/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
66
+ wrapLogger(instanceEE, parent, functionName, { customAttributes, level })
70
67
  }
71
68
 
72
69
  // Setup stub functions that queue calls for later processing.
@@ -100,11 +97,11 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
100
97
  }
101
98
  apiInterface.setCustomAttribute = function (name, value, persistAttribute = false) {
102
99
  if (typeof name !== 'string') {
103
- warn(`Failed to execute setCustomAttribute.\nName must be a string type, but a type of <${typeof name}> was provided.`)
100
+ warn(39, typeof name)
104
101
  return
105
102
  }
106
103
  if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
107
- warn(`Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <${typeof value}> was provided.`)
104
+ warn(40, typeof value)
108
105
  return
109
106
  }
110
107
  return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute)
@@ -116,7 +113,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
116
113
  */
117
114
  apiInterface.setUserId = function (value) {
118
115
  if (!(typeof value === 'string' || value === null)) {
119
- warn(`Failed to execute setUserId.\nNon-null value must be a string type, but a type of <${typeof value}> was provided.`)
116
+ warn(41, typeof value)
120
117
  return
121
118
  }
122
119
  return appendJsAttribute('enduser.id', value, 'setUserId', true)
@@ -129,7 +126,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
129
126
  */
130
127
  apiInterface.setApplicationVersion = function (value) {
131
128
  if (!(typeof value === 'string' || value === null)) {
132
- warn(`Failed to execute setApplicationVersion. Expected <String | null>, but got <${typeof value}>.`)
129
+ warn(42, typeof value)
133
130
  return
134
131
  }
135
132
  return appendJsAttribute('application.version', value, 'setApplicationVersion', false)
@@ -140,7 +137,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
140
137
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
141
138
  instanceEE.emit('manual-start-all')
142
139
  } catch (err) {
143
- warn('An unexpected issue occurred', err)
140
+ warn(23, err)
144
141
  }
145
142
  }
146
143
 
@@ -215,7 +212,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
215
212
  setAPI(agentIdentifier)
216
213
  drain(agentIdentifier, 'api')
217
214
  }).catch((err) => {
218
- warn('Downloading runtime APIs failed...', err)
215
+ warn(27, err)
219
216
  instanceEE.abort()
220
217
  })
221
218
  }
@@ -61,11 +61,11 @@ export class MicroAgent extends AgentBase {
61
61
  if (features === undefined) features = featNames
62
62
  else {
63
63
  features = Array.isArray(features) && features.length ? features : [features]
64
- if (features.some(f => !featNames.includes(f))) return warn(`Invalid feature name supplied. Acceptable feature names are: ${featNames}`)
64
+ if (features.some(f => !featNames.includes(f))) return warn(37, featNames)
65
65
  if (!features.includes(FEATURE_NAMES.pageViewEvent)) features.push(FEATURE_NAMES.pageViewEvent)
66
66
  }
67
67
  } catch (err) {
68
- warn('An unexpected issue occurred', err)
68
+ warn(23, err)
69
69
  }
70
70
 
71
71
  try {
@@ -75,7 +75,7 @@ export class MicroAgent extends AgentBase {
75
75
  // a biproduct of doing this is that the "session manager" is automatically handled through importing this feature
76
76
  this.features.page_view_event = new PVE(this.agentIdentifier, this.sharedAggregator)
77
77
  } catch (err) {
78
- warn('Something prevented the agent from instrumenting.', err)
78
+ warn(24, err)
79
79
  }
80
80
 
81
81
  onWindowLoad(() => {
@@ -87,13 +87,13 @@ export class MicroAgent extends AgentBase {
87
87
  }).then(({ Aggregate }) => {
88
88
  this.features[f] = new Aggregate(this.agentIdentifier, this.sharedAggregator)
89
89
  }).catch(err =>
90
- warn('Something prevented the agent from being downloaded.', err))
90
+ warn(25, err))
91
91
  }
92
92
  })
93
93
  })
94
94
  return true
95
95
  } catch (err) {
96
- warn('Failed to initialize instrument classes.', err)
96
+ warn(26, err)
97
97
  return false
98
98
  }
99
99
  }