@newrelic/browser-agent 1.236.0 → 1.237.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/config/state/init.js +1 -0
- package/dist/cjs/common/config/state/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/deny-list/deny-list.js +14 -10
- package/dist/cjs/common/harvest/harvest.js +7 -20
- package/dist/cjs/common/util/submit-data.js +4 -36
- package/dist/cjs/common/wrap/wrap-jsonp.js +12 -6
- package/dist/cjs/features/ajax/aggregate/index.js +24 -27
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/dist/cjs/features/jserrors/constants.js +2 -4
- package/dist/cjs/features/jserrors/instrument/index.js +79 -88
- package/dist/cjs/features/jserrors/instrument/uncaught-error.js +22 -0
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +23 -19
- package/dist/cjs/features/session_replay/aggregate/index.js +65 -34
- package/dist/cjs/features/session_trace/aggregate/index.js +3 -4
- package/dist/cjs/features/utils/instrument-base.js +6 -8
- package/dist/cjs/loaders/agent-base.js +87 -0
- package/dist/cjs/loaders/agent.js +41 -1
- package/dist/cjs/loaders/api/api.js +1 -1
- package/dist/cjs/loaders/api/interaction-types.js +87 -0
- package/dist/cjs/loaders/configure/configure.js +2 -1
- package/dist/cjs/loaders/micro-agent.js +3 -1
- package/dist/esm/common/config/state/init.js +1 -0
- package/dist/esm/common/config/state/runtime.js +2 -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/deny-list/deny-list.js +14 -10
- package/dist/esm/common/harvest/harvest.js +6 -20
- package/dist/esm/common/util/submit-data.js +4 -35
- package/dist/esm/common/wrap/wrap-jsonp.js +12 -6
- package/dist/esm/features/ajax/aggregate/index.js +25 -28
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/dist/esm/features/jserrors/constants.js +1 -2
- package/dist/esm/features/jserrors/instrument/index.js +78 -87
- package/dist/esm/features/jserrors/instrument/uncaught-error.js +15 -0
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +23 -19
- package/dist/esm/features/session_replay/aggregate/index.js +65 -34
- package/dist/esm/features/session_trace/aggregate/index.js +3 -4
- package/dist/esm/features/utils/instrument-base.js +7 -9
- package/dist/esm/loaders/agent-base.js +80 -0
- package/dist/esm/loaders/agent.js +41 -1
- package/dist/esm/loaders/api/api.js +1 -1
- package/dist/esm/loaders/api/interaction-types.js +80 -0
- package/dist/esm/loaders/configure/configure.js +3 -2
- package/dist/esm/loaders/micro-agent.js +3 -1
- package/dist/types/common/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts +6 -6
- package/dist/types/common/util/submit-data.d.ts +2 -20
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts +2 -2
- package/dist/types/common/wrap/wrap-jsonp.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts +5 -5
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/constants.d.ts +0 -1
- package/dist/types/features/jserrors/constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts +0 -13
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/uncaught-error.d.ts +15 -0
- package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +1 -0
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
- package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +16 -30
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +59 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -0
- package/dist/types/loaders/agent.d.ts +35 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/interaction-types.d.ts +122 -0
- package/dist/types/loaders/api/interaction-types.d.ts.map +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +9 -9
- package/dist/types/loaders/micro-agent.d.ts +3 -2
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/state/init.js +1 -1
- package/src/common/config/state/runtime.js +2 -1
- package/src/common/deny-list/deny-list.js +11 -11
- package/src/common/deny-list/deny-list.test.js +31 -0
- package/src/common/harvest/harvest.js +7 -16
- package/src/common/harvest/harvest.test.js +16 -36
- package/src/common/util/__mocks__/submit-data.js +0 -1
- package/src/common/util/submit-data.js +2 -24
- package/src/common/util/submit-data.test.js +0 -56
- package/src/common/wrap/wrap-jsonp.js +11 -6
- package/src/features/ajax/aggregate/index.js +25 -31
- package/src/features/jserrors/aggregate/compute-stack-trace.js +1 -1
- package/src/features/jserrors/constants.js +0 -1
- package/src/features/jserrors/instrument/index.js +91 -87
- package/src/features/jserrors/instrument/uncaught-error.js +15 -0
- package/src/features/page_view_event/aggregate/initialized-features.js +18 -14
- package/src/features/session_replay/aggregate/index.component-test.js +17 -56
- package/src/features/session_replay/aggregate/index.js +47 -28
- package/src/features/session_trace/aggregate/index.js +3 -4
- package/src/features/utils/instrument-base.js +6 -9
- package/src/features/utils/instrument-base.test.js +7 -0
- package/src/loaders/agent-base.js +81 -0
- package/src/loaders/agent.js +42 -1
- package/src/loaders/api/api.js +1 -1
- package/src/loaders/api/interaction-types.js +80 -0
- package/src/loaders/configure/configure.js +4 -3
- package/src/loaders/micro-agent.js +4 -1
- package/dist/cjs/features/jserrors/instrument/debug.js +0 -40
- package/dist/esm/features/jserrors/instrument/debug.js +0 -38
- package/dist/types/features/jserrors/instrument/debug.d.ts +0 -2
- package/dist/types/features/jserrors/instrument/debug.d.ts.map +0 -1
- package/src/features/jserrors/instrument/debug.js +0 -36
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
describe('Setting deny list', () => {
|
|
2
|
+
let DlMod
|
|
3
|
+
beforeEach(() => {
|
|
4
|
+
jest.isolateModules(() => import('./deny-list.js').then(m => DlMod = m)) // give every test its own denyList (sandbox)
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
it('respects path', () => {
|
|
8
|
+
DlMod.setDenyList(['bam.nr-data.net/somepath'])
|
|
9
|
+
|
|
10
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '/somepath' })).toBeFalsy() // shouldCollectEvent returns 'false' when there IS a match...
|
|
11
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
|
|
12
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
|
|
13
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '/someotherpath' })).toBeTruthy()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('ignores port', () => {
|
|
17
|
+
DlMod.setDenyList(['bam.nr-data.net:1234'])
|
|
18
|
+
|
|
19
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', port: '4321' })).toBeFalsy()
|
|
20
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'http', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
|
|
21
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('ignores protocol', () => {
|
|
25
|
+
DlMod.setDenyList(['http://bam.nr-data.net'])
|
|
26
|
+
|
|
27
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', protocol: 'https' })).toBeFalsy()
|
|
28
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
|
|
29
|
+
expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '', protocol: 'http' })).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -16,6 +16,9 @@ import { applyFnToProps } from '../util/traverse'
|
|
|
16
16
|
import { SharedContext } from '../context/shared-context'
|
|
17
17
|
import { VERSION } from '../constants/env'
|
|
18
18
|
import { isWorkerScope, isIE } from '../constants/runtime'
|
|
19
|
+
import { warn } from '../util/console'
|
|
20
|
+
|
|
21
|
+
const warnings = {}
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* @typedef {import('./types.js').NetworkSendSpec} NetworkSendSpec
|
|
@@ -24,7 +27,6 @@ import { isWorkerScope, isIE } from '../constants/runtime'
|
|
|
24
27
|
* @typedef {import('./types.js').FeatureHarvestCallback} FeatureHarvestCallback
|
|
25
28
|
* @typedef {import('./types.js').FeatureHarvestCallbackOptions} FeatureHarvestCallbackOptions
|
|
26
29
|
*/
|
|
27
|
-
|
|
28
30
|
export class Harvest extends SharedContext {
|
|
29
31
|
constructor (parent) {
|
|
30
32
|
super(parent) // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
@@ -107,7 +109,7 @@ export class Harvest extends SharedContext {
|
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
const fullUrl = `${url}?${baseParams}${payloadParams}`
|
|
110
|
-
const gzip = qs
|
|
112
|
+
const gzip = !!qs?.attributes?.includes('gzip')
|
|
111
113
|
|
|
112
114
|
if (!gzip) {
|
|
113
115
|
if (endpoint === 'events') {
|
|
@@ -115,6 +117,8 @@ export class Harvest extends SharedContext {
|
|
|
115
117
|
} else {
|
|
116
118
|
body = stringify(body)
|
|
117
119
|
}
|
|
120
|
+
/** Warn --once per endpoint-- if the agent tries to send large payloads */
|
|
121
|
+
if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(`The Browser Agent is attempting to send a very large payload to /${endpoint}. This is usually tied to large amounts of custom attributes. Please check your configurations.`)
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
if (!body || body.length === 0 || body === '{}' || body === '[]') {
|
|
@@ -156,19 +160,6 @@ export class Harvest extends SharedContext {
|
|
|
156
160
|
}, eventListenerOpts(false))
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
// if beacon request failed, retry with an alternative method -- will not happen for workers
|
|
160
|
-
if (!result && submitMethod === submitData.beacon) {
|
|
161
|
-
// browsers that support sendBeacon also support fetch with keepalive - IE will not retry unload calls
|
|
162
|
-
submitMethod = submitData.fetchKeepAlive
|
|
163
|
-
try {
|
|
164
|
-
submitMethod({ url: fullUrl, body, headers })
|
|
165
|
-
} catch (e) {
|
|
166
|
-
// Ignore error in final harvest
|
|
167
|
-
} finally {
|
|
168
|
-
result = true
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
163
|
return result
|
|
173
164
|
}
|
|
174
165
|
|
|
@@ -239,7 +230,7 @@ export class Harvest extends SharedContext {
|
|
|
239
230
|
*/
|
|
240
231
|
cleanPayload (payload = {}) {
|
|
241
232
|
const clean = (input) => {
|
|
242
|
-
if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array) {
|
|
233
|
+
if ((typeof Uint8Array !== 'undefined' && input instanceof Uint8Array) || typeof input === 'string') {
|
|
243
234
|
return input.length > 0 ? input : null
|
|
244
235
|
}
|
|
245
236
|
return Object.entries(input || {})
|
|
@@ -3,6 +3,7 @@ import { faker } from '@faker-js/faker'
|
|
|
3
3
|
import * as encodeModule from '../url/encode'
|
|
4
4
|
import * as submitDataModule from '../util/submit-data'
|
|
5
5
|
import * as configModule from '../config/config'
|
|
6
|
+
import { warn } from '../util/console'
|
|
6
7
|
import { applyFnToProps } from '../util/traverse'
|
|
7
8
|
|
|
8
9
|
import { Harvest } from './harvest'
|
|
@@ -275,7 +276,7 @@ describe('_send', () => {
|
|
|
275
276
|
})
|
|
276
277
|
|
|
277
278
|
test('should not alter body when gzip qs is present', () => {
|
|
278
|
-
spec.payload.qs.
|
|
279
|
+
spec.payload.qs.attributes += '&content_encoding=gzip'
|
|
279
280
|
|
|
280
281
|
const result = harvestInstance._send(spec)
|
|
281
282
|
|
|
@@ -288,6 +289,20 @@ describe('_send', () => {
|
|
|
288
289
|
})
|
|
289
290
|
})
|
|
290
291
|
|
|
292
|
+
test('should warn (once) if payload is large', () => {
|
|
293
|
+
spec.payload.body = 'x'.repeat(1024 * 1024) // ~1mb string
|
|
294
|
+
|
|
295
|
+
const result = harvestInstance._send(spec)
|
|
296
|
+
|
|
297
|
+
expect(result).toEqual(true)
|
|
298
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('The Browser Agent is attempting to send a very large payload'))
|
|
299
|
+
expect(warn).toHaveBeenCalledTimes(1)
|
|
300
|
+
|
|
301
|
+
const result2 = harvestInstance._send(spec)
|
|
302
|
+
expect(result2).toEqual(true)
|
|
303
|
+
expect(warn).toHaveBeenCalledTimes(1)
|
|
304
|
+
})
|
|
305
|
+
|
|
291
306
|
test('should set body to events when endpoint is events', () => {
|
|
292
307
|
spec.endpoint = 'events'
|
|
293
308
|
spec.payload.body.e = faker.lorem.sentence()
|
|
@@ -440,41 +455,6 @@ describe('_send', () => {
|
|
|
440
455
|
sent: true
|
|
441
456
|
})
|
|
442
457
|
})
|
|
443
|
-
|
|
444
|
-
test('should fallback to fetchKeepAlive when beacon returns false', async () => {
|
|
445
|
-
jest.mocked(submitDataModule.getSubmitMethod).mockReturnValue(submitDataModule.beacon)
|
|
446
|
-
jest.mocked(submitDataModule.beacon).mockReturnValue(false)
|
|
447
|
-
spec.opts.unload = true
|
|
448
|
-
|
|
449
|
-
const results = harvestInstance._send(spec)
|
|
450
|
-
await new Promise(process.nextTick)
|
|
451
|
-
|
|
452
|
-
expect(results).toEqual(true)
|
|
453
|
-
expect(submitDataModule.fetchKeepAlive).toHaveBeenCalledWith({
|
|
454
|
-
body: JSON.stringify(spec.payload.body),
|
|
455
|
-
headers: [{ key: 'content-type', value: 'text/plain' }],
|
|
456
|
-
url: expect.stringContaining(`https://${errorBeacon}/${spec.endpoint}/1/${licenseKey}?`)
|
|
457
|
-
})
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
test('should not throw an exception if fetchKeepAlive throws error', async () => {
|
|
461
|
-
jest.mocked(submitDataModule.getSubmitMethod).mockReturnValue(submitDataModule.beacon)
|
|
462
|
-
jest.mocked(submitDataModule.beacon).mockReturnValue(false)
|
|
463
|
-
jest.mocked(submitDataModule.fetchKeepAlive).mockImplementation(() => {
|
|
464
|
-
throw new Error(faker.lorem.sentence())
|
|
465
|
-
})
|
|
466
|
-
spec.opts.unload = true
|
|
467
|
-
|
|
468
|
-
const results = harvestInstance._send(spec)
|
|
469
|
-
await new Promise(process.nextTick)
|
|
470
|
-
|
|
471
|
-
expect(results).toEqual(true)
|
|
472
|
-
expect(submitDataModule.fetchKeepAlive).toHaveBeenCalledWith({
|
|
473
|
-
body: JSON.stringify(spec.payload.body),
|
|
474
|
-
headers: [{ key: 'content-type', value: 'text/plain' }],
|
|
475
|
-
url: expect.stringContaining(`https://${errorBeacon}/${spec.endpoint}/1/${licenseKey}?`)
|
|
476
|
-
})
|
|
477
|
-
})
|
|
478
458
|
})
|
|
479
459
|
|
|
480
460
|
describe('obfuscateAndSend', () => {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { isBrowserScope, supportsSendBeacon } from '../constants/runtime'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @typedef {xhr|
|
|
10
|
+
* @typedef {xhr|beacon} NetworkMethods
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -54,27 +54,6 @@ export function xhr ({ url, body = null, sync, method = 'POST', headers = [{ key
|
|
|
54
54
|
return request
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* Send via fetch with keepalive true
|
|
59
|
-
* @param {Object} args - The args.
|
|
60
|
-
* @param {string} args.url - The URL to send to.
|
|
61
|
-
* @param {string=} args.body - The Stringified body.
|
|
62
|
-
* @param {string=} [args.method=POST] - The XHR method to use.
|
|
63
|
-
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
|
|
64
|
-
* @returns {XMLHttpRequest}
|
|
65
|
-
*/
|
|
66
|
-
export function fetchKeepAlive ({ url, body = null, method = 'POST', headers = [{ key: 'content-type', value: 'text/plain' }] }) {
|
|
67
|
-
return fetch(url, {
|
|
68
|
-
method,
|
|
69
|
-
headers: headers.reduce((aggregator, header) => {
|
|
70
|
-
aggregator.push([header.key, header.value])
|
|
71
|
-
return aggregator
|
|
72
|
-
}, []),
|
|
73
|
-
body,
|
|
74
|
-
keepalive: true
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
57
|
/**
|
|
79
58
|
* Send via sendBeacon. Do NOT call this function outside of a guaranteed web window environment.
|
|
80
59
|
* @param {Object} args - The args
|
|
@@ -90,8 +69,7 @@ export function beacon ({ url, body }) {
|
|
|
90
69
|
return send(url, body)
|
|
91
70
|
} catch (err) {
|
|
92
71
|
// if sendBeacon still trys to throw an illegal invocation error,
|
|
93
|
-
// we can catch here and return
|
|
94
|
-
// fetchKeepAlive to try to send
|
|
72
|
+
// we can catch here and return
|
|
95
73
|
return false
|
|
96
74
|
}
|
|
97
75
|
}
|
|
@@ -141,62 +141,6 @@ describe('xhr', () => {
|
|
|
141
141
|
})
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
-
describe('fetchKeepAlive', () => {
|
|
145
|
-
beforeEach(() => {
|
|
146
|
-
global.fetch = jest.fn().mockReturnValue(Promise.resolve())
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
afterEach(() => {
|
|
150
|
-
delete global.fetch
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
test('should make a fetch with default values', () => {
|
|
154
|
-
submitData.fetchKeepAlive({ url })
|
|
155
|
-
|
|
156
|
-
expect(global.fetch).toHaveBeenCalledWith(url, {
|
|
157
|
-
method: 'POST',
|
|
158
|
-
body: null,
|
|
159
|
-
keepalive: true,
|
|
160
|
-
headers: [['content-type', 'text/plain']]
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test('should send the body when provided', () => {
|
|
165
|
-
const body = faker.lorem.paragraph()
|
|
166
|
-
submitData.fetchKeepAlive({ url, body })
|
|
167
|
-
|
|
168
|
-
expect(global.fetch).toHaveBeenCalledWith(url, {
|
|
169
|
-
method: 'POST',
|
|
170
|
-
body,
|
|
171
|
-
keepalive: true,
|
|
172
|
-
headers: [['content-type', 'text/plain']]
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
test('should use the provided method', () => {
|
|
177
|
-
submitData.fetchKeepAlive({ url, method: 'HEAD' })
|
|
178
|
-
|
|
179
|
-
expect(global.fetch).toHaveBeenCalledWith(url, {
|
|
180
|
-
method: 'HEAD',
|
|
181
|
-
body: null,
|
|
182
|
-
keepalive: true,
|
|
183
|
-
headers: [['content-type', 'text/plain']]
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
test('should use the provided headers', () => {
|
|
188
|
-
const headers = [{ key: faker.lorem.word(), value: faker.datatype.uuid() }]
|
|
189
|
-
submitData.fetchKeepAlive({ url, headers })
|
|
190
|
-
|
|
191
|
-
expect(global.fetch).toHaveBeenCalledWith(url, {
|
|
192
|
-
method: 'POST',
|
|
193
|
-
body: null,
|
|
194
|
-
keepalive: true,
|
|
195
|
-
headers: [[headers[0].key, headers[0].value]]
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
144
|
describe('beacon', () => {
|
|
201
145
|
afterEach(() => {
|
|
202
146
|
delete window.navigator.sendBeacon
|
|
@@ -98,13 +98,18 @@ export function wrapJsonP (sharedEE) {
|
|
|
98
98
|
return matches ? matches[1] : null
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Traverse a nested object using a '.'-delimited string wherein each substring piece maps to each subsequent object property layer.
|
|
103
|
+
* @param {string} longKey
|
|
104
|
+
* @param {object} obj
|
|
105
|
+
* @returns The final nested object referred to by initial longKey.
|
|
106
|
+
*/
|
|
101
107
|
function discoverValue (longKey, obj) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
+
if (!longKey) return obj // end of object recursion depth when no more key levels
|
|
109
|
+
const matches = longKey.match(VALUE_REGEX)
|
|
110
|
+
// if 'longKey' was not undefined, that is it at least had 1 level left, then the regexp would've at least matched 1st group
|
|
111
|
+
const key = matches[1]
|
|
112
|
+
const remaining = matches[3]
|
|
108
113
|
return discoverValue(remaining, obj[key])
|
|
109
114
|
}
|
|
110
115
|
|
|
@@ -6,7 +6,7 @@ import { registerHandler as register } from '../../../common/event-emitter/regis
|
|
|
6
6
|
import { stringify } from '../../../common/util/stringify'
|
|
7
7
|
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
|
|
8
8
|
import { handle } from '../../../common/event-emitter/handle'
|
|
9
|
-
import {
|
|
9
|
+
import { getConfiguration, getInfo, getRuntime } from '../../../common/config/config'
|
|
10
10
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
11
11
|
import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list'
|
|
12
12
|
import { FEATURE_NAME } from '../constants'
|
|
@@ -19,15 +19,25 @@ export class Aggregate extends AggregateBase {
|
|
|
19
19
|
static featureName = FEATURE_NAME
|
|
20
20
|
constructor (agentIdentifier, aggregator) {
|
|
21
21
|
super(agentIdentifier, aggregator, FEATURE_NAME)
|
|
22
|
+
const agentInit = getConfiguration(agentIdentifier)
|
|
23
|
+
const allAjaxIsEnabled = agentInit.ajax.enabled !== false
|
|
24
|
+
|
|
25
|
+
register('xhr', storeXhr, this.featureName, this.ee)
|
|
26
|
+
if (!allAjaxIsEnabled) {
|
|
27
|
+
drain(this.agentIdentifier, this.featureName)
|
|
28
|
+
return // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const denyList = getRuntime(agentIdentifier).denyList
|
|
32
|
+
setDenyList(denyList)
|
|
33
|
+
|
|
22
34
|
let ajaxEvents = []
|
|
23
35
|
let spaAjaxEvents = {}
|
|
24
36
|
let sentAjaxEvents = []
|
|
25
|
-
let scheduler
|
|
26
|
-
|
|
27
37
|
const ee = this.ee
|
|
28
38
|
|
|
29
|
-
const harvestTimeSeconds =
|
|
30
|
-
const MAX_PAYLOAD_SIZE =
|
|
39
|
+
const harvestTimeSeconds = agentInit.ajax.harvestTimeSeconds || 10
|
|
40
|
+
const MAX_PAYLOAD_SIZE = agentInit.ajax.maxPayloadSize || 1000000
|
|
31
41
|
|
|
32
42
|
// Exposes these methods to browser test files -- future TO DO: can be removed once these fns are extracted from the constructor into class func
|
|
33
43
|
this.storeXhr = storeXhr
|
|
@@ -39,9 +49,8 @@ export class Aggregate extends AggregateBase {
|
|
|
39
49
|
// remove from the spaAjaxEvents buffer, and let spa harvest it
|
|
40
50
|
delete spaAjaxEvents[interaction.id]
|
|
41
51
|
})
|
|
42
|
-
|
|
43
52
|
ee.on('interactionDiscarded', (interaction) => {
|
|
44
|
-
if (!spaAjaxEvents[interaction.id]
|
|
53
|
+
if (!spaAjaxEvents[interaction.id]) return
|
|
45
54
|
|
|
46
55
|
spaAjaxEvents[interaction.id].forEach(function (item) {
|
|
47
56
|
// move it from the spaAjaxEvents buffer to the ajaxEvents buffer for harvesting here
|
|
@@ -50,18 +59,15 @@ export class Aggregate extends AggregateBase {
|
|
|
50
59
|
delete spaAjaxEvents[interaction.id]
|
|
51
60
|
})
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
const scheduler = new HarvestScheduler('events', {
|
|
63
|
+
onFinished: onEventsHarvestFinished,
|
|
64
|
+
getPayload: prepareHarvest
|
|
65
|
+
}, this)
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
scheduler = new HarvestScheduler('events', {
|
|
59
|
-
onFinished: onEventsHarvestFinished,
|
|
60
|
-
getPayload: prepareHarvest
|
|
61
|
-
}, this)
|
|
67
|
+
ee.on(`drain-${this.featureName}`, () => { scheduler.startTimer(harvestTimeSeconds) })
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
drain(this.agentIdentifier, this.featureName)
|
|
70
|
+
return
|
|
65
71
|
|
|
66
72
|
function storeXhr (params, metrics, startTime, endTime, type) {
|
|
67
73
|
metrics.time = startTime
|
|
@@ -79,9 +85,7 @@ export class Aggregate extends AggregateBase {
|
|
|
79
85
|
// store as metric
|
|
80
86
|
aggregator.store('xhr', hash, params, metrics)
|
|
81
87
|
|
|
82
|
-
if (!allAjaxIsEnabled
|
|
83
|
-
return
|
|
84
|
-
}
|
|
88
|
+
if (!allAjaxIsEnabled) return
|
|
85
89
|
|
|
86
90
|
if (!shouldCollectEvent(params)) {
|
|
87
91
|
if (params.hostname === getInfo(agentIdentifier).errorBeacon) {
|
|
@@ -172,7 +176,7 @@ export class Aggregate extends AggregateBase {
|
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
function onEventsHarvestFinished (result) {
|
|
175
|
-
if (result.retry && sentAjaxEvents.length > 0
|
|
179
|
+
if (result.retry && sentAjaxEvents.length > 0) {
|
|
176
180
|
ajaxEvents.unshift(...sentAjaxEvents)
|
|
177
181
|
sentAjaxEvents = []
|
|
178
182
|
}
|
|
@@ -234,15 +238,5 @@ export class Aggregate extends AggregateBase {
|
|
|
234
238
|
return this.payload.length * 2 > maxPayloadSize
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
|
-
|
|
238
|
-
function allAjaxIsEnabled () {
|
|
239
|
-
var enabled = getConfigurationValue(agentIdentifier, 'ajax.enabled')
|
|
240
|
-
if (enabled === false) {
|
|
241
|
-
return false
|
|
242
|
-
}
|
|
243
|
-
return true
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
drain(this.agentIdentifier, this.featureName)
|
|
247
241
|
}
|
|
248
242
|
}
|
|
@@ -233,7 +233,7 @@ function computeStackTraceBySourceAndLine (ex) {
|
|
|
233
233
|
mode: 'sourceline',
|
|
234
234
|
name: className,
|
|
235
235
|
message: ex.message,
|
|
236
|
-
stackString:
|
|
236
|
+
stackString: className + ': ' + ex.message + '\n in evaluated code',
|
|
237
237
|
frames: [{
|
|
238
238
|
func: 'evaluated code'
|
|
239
239
|
}]
|