@newrelic/browser-agent 1.316.0-rc.6 → 1.317.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cjs/common/config/init-types.js +1 -0
- package/dist/cjs/common/config/init.js +6 -4
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/payloads/payloads.js +127 -0
- package/dist/cjs/common/serialize/bel-serializer.js +2 -1
- package/dist/cjs/common/util/data-size.js +2 -2
- package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
- package/dist/cjs/features/ajax/aggregate/gql.js +40 -1
- package/dist/cjs/features/ajax/aggregate/index.js +46 -13
- package/dist/cjs/features/ajax/constants.js +6 -1
- package/dist/cjs/features/ajax/instrument/index.js +105 -17
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +2 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +1 -1
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +35 -4
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
- package/dist/esm/common/config/init-types.js +1 -0
- package/dist/esm/common/config/init.js +3 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/payloads/payloads.js +118 -0
- package/dist/esm/common/serialize/bel-serializer.js +2 -1
- package/dist/esm/common/util/data-size.js +2 -2
- package/dist/esm/common/wrap/wrap-xhr.js +1 -1
- package/dist/esm/features/ajax/aggregate/gql.js +39 -1
- package/dist/esm/features/ajax/aggregate/index.js +47 -14
- package/dist/esm/features/ajax/constants.js +5 -0
- package/dist/esm/features/ajax/instrument/index.js +106 -18
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +2 -2
- package/dist/esm/features/jserrors/aggregate/index.js +1 -1
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +35 -4
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +4 -0
- package/dist/types/common/config/init-types.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/payloads/payloads.d.ts +42 -0
- package/dist/types/common/payloads/payloads.d.ts.map +1 -0
- package/dist/types/common/serialize/bel-serializer.d.ts +1 -1
- package/dist/types/common/serialize/bel-serializer.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/gql.d.ts +8 -0
- package/dist/types/features/ajax/aggregate/gql.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/ajax/constants.d.ts +5 -0
- package/dist/types/features/ajax/constants.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +5 -0
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/init-types.js +1 -0
- package/src/common/config/init.js +2 -1
- package/src/common/payloads/payloads.js +135 -0
- package/src/common/serialize/bel-serializer.js +2 -1
- package/src/common/util/data-size.js +2 -2
- package/src/common/wrap/wrap-xhr.js +1 -1
- package/src/features/ajax/aggregate/gql.js +42 -1
- package/src/features/ajax/aggregate/index.js +38 -13
- package/src/features/ajax/constants.js +5 -1
- package/src/features/ajax/instrument/index.js +114 -18
- package/src/features/jserrors/aggregate/format-stack-trace.js +3 -3
- package/src/features/jserrors/aggregate/index.js +1 -1
- package/src/features/soft_navigations/aggregate/ajax-node.js +25 -4
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
|
@@ -6,12 +6,13 @@ import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
|
6
6
|
import { stringify } from '../../../common/util/stringify'
|
|
7
7
|
import { handle } from '../../../common/event-emitter/handle'
|
|
8
8
|
import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list'
|
|
9
|
-
import {
|
|
9
|
+
import { FEATURE_NAME, AJAX_ID } from '../constants'
|
|
10
10
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
11
11
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
12
|
-
import { parseGQL } from './gql'
|
|
13
12
|
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
|
|
14
13
|
import { gosNREUMOriginals } from '../../../common/window/nreum'
|
|
14
|
+
import { hasGQLErrors, parseGQL } from './gql'
|
|
15
|
+
import { canCapturePayload, isLikelyHumanReadable, parseQueryString, createStringAdders } from '../../../common/payloads/payloads'
|
|
15
16
|
import { Obfuscator } from '../../../common/util/obfuscate'
|
|
16
17
|
import { getVersion2Attributes, getVersion2DuplicationAttributes, shouldDuplicate } from '../../../common/v2/utils'
|
|
17
18
|
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
@@ -101,6 +102,24 @@ export class Aggregate extends AggregateBase {
|
|
|
101
102
|
[AJAX_ID]: generateUuid() // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
event.gql = params.gql = parseGQL({
|
|
106
|
+
body: ctx.requestBody,
|
|
107
|
+
query: ctx.parsedOrigin?.search
|
|
108
|
+
})
|
|
109
|
+
if (event.gql) event.gql.operationHasErrors = params.gql.operationHasErrors = hasGQLErrors(ctx.responseBody)
|
|
110
|
+
|
|
111
|
+
const capturePayloadSetting = this.agentRef.init.ajax.capture_payloads
|
|
112
|
+
const shouldCapturePayload = canCapturePayload(capturePayloadSetting, params.status, event.gql?.operationHasErrors)
|
|
113
|
+
|
|
114
|
+
if (shouldCapturePayload) {
|
|
115
|
+
// Store raw data; obfuscation and truncation will happen in the serializer
|
|
116
|
+
params.requestQuery = event.requestQuery = parseQueryString(ctx.parsedOrigin?.search)
|
|
117
|
+
params.requestHeaders = event.requestHeaders = ctx.requestHeaders
|
|
118
|
+
params.responseHeaders = event.responseHeaders = ctx.responseHeaders
|
|
119
|
+
if (isLikelyHumanReadable(ctx.requestHeaders, ctx.requestBody)) params.requestBody = event.requestBody = ctx.requestBody
|
|
120
|
+
if (isLikelyHumanReadable(ctx.responseHeaders, ctx.responseBody)) params.responseBody = event.responseBody = ctx.responseBody
|
|
121
|
+
}
|
|
122
|
+
|
|
104
123
|
if (ctx.dt) {
|
|
105
124
|
event.spanId = ctx.dt.spanId
|
|
106
125
|
event.traceId = ctx.dt.traceId
|
|
@@ -109,11 +128,6 @@ export class Aggregate extends AggregateBase {
|
|
|
109
128
|
)
|
|
110
129
|
}
|
|
111
130
|
|
|
112
|
-
// parsed from the AJAX body, looking for operationName param & parsing query for operationType
|
|
113
|
-
event.gql = params.gql = parseGQL({
|
|
114
|
-
body: ctx.body,
|
|
115
|
-
query: ctx.parsedOrigin?.search
|
|
116
|
-
})
|
|
117
131
|
if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length)
|
|
118
132
|
|
|
119
133
|
/** make a copy of the event for the MFE target if it exists */
|
|
@@ -136,7 +150,9 @@ export class Aggregate extends AggregateBase {
|
|
|
136
150
|
|
|
137
151
|
serializer (eventBuffer) {
|
|
138
152
|
if (!eventBuffer.length) return
|
|
139
|
-
|
|
153
|
+
|
|
154
|
+
const { addString, addStringWithTruncation } = createStringAdders(getAddStringContext, this.obfuscator)
|
|
155
|
+
|
|
140
156
|
let payload = 'bel.7;'
|
|
141
157
|
|
|
142
158
|
let firstTimestamp = 0
|
|
@@ -170,15 +186,24 @@ export class Aggregate extends AggregateBase {
|
|
|
170
186
|
// Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
|
|
171
187
|
const jsAttributes = this.agentRef.info.jsAttributes
|
|
172
188
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
const attrParts = addCustomAttributes({
|
|
189
|
+
// Regular attributes: obfuscate only
|
|
190
|
+
const regularAttrs = addCustomAttributes({
|
|
176
191
|
...(jsAttributes || {}),
|
|
177
|
-
|
|
192
|
+
[AJAX_ID]: event[AJAX_ID], // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
178
193
|
...(event.targetAttributes || {}), // used to supply the version 2 attributes, either MFE target or duplication attributes for the main agent app
|
|
179
|
-
|
|
194
|
+
...(event.gql || {})
|
|
180
195
|
}, addString)
|
|
181
196
|
|
|
197
|
+
// Payload attributes: obfuscate then truncate
|
|
198
|
+
const payloadAttrs = addCustomAttributes({
|
|
199
|
+
...(event.requestBody ? { requestBody: event.requestBody } : {}),
|
|
200
|
+
...(event.requestHeaders ? { requestHeaders: event.requestHeaders } : {}),
|
|
201
|
+
...(event.requestQuery ? { requestQuery: event.requestQuery } : {}),
|
|
202
|
+
...(event.responseBody ? { responseBody: event.responseBody } : {}),
|
|
203
|
+
...(event.responseHeaders ? { responseHeaders: event.responseHeaders } : {})
|
|
204
|
+
}, addStringWithTruncation)
|
|
205
|
+
|
|
206
|
+
const attrParts = [...regularAttrs, ...payloadAttrs]
|
|
182
207
|
fields.unshift(numeric(attrParts.length))
|
|
183
208
|
|
|
184
209
|
insert += fields.join(',')
|
|
@@ -5,5 +5,9 @@
|
|
|
5
5
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
6
6
|
|
|
7
7
|
export const FEATURE_NAME = FEATURE_NAMES.ajax
|
|
8
|
-
|
|
8
|
+
export const CAPTURE_PAYLOAD_SETTINGS = {
|
|
9
|
+
NONE: 'none',
|
|
10
|
+
FAILURES: 'failures',
|
|
11
|
+
ALL: 'all'
|
|
12
|
+
}
|
|
9
13
|
export const AJAX_ID = 'ajaxRequest.id'
|
|
@@ -14,7 +14,7 @@ import { parseUrl } from '../../../common/url/parse-url'
|
|
|
14
14
|
import { DT } from './distributed-tracing'
|
|
15
15
|
import { responseSizeFromXhr } from './response-size'
|
|
16
16
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
17
|
-
import { FEATURE_NAME } from '../constants'
|
|
17
|
+
import { CAPTURE_PAYLOAD_SETTINGS, FEATURE_NAME } from '../constants'
|
|
18
18
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
19
19
|
import { SUPPORTABILITY_METRIC } from '../../metrics/constants'
|
|
20
20
|
import { now } from '../../../common/timing/now'
|
|
@@ -27,6 +27,7 @@ var handlersLen = handlers.length
|
|
|
27
27
|
var origRequest = gosNREUMOriginals().o.REQ
|
|
28
28
|
var origXHR = gosNREUMOriginals().o.XHR
|
|
29
29
|
const NR_CAT_HEADER = 'X-NewRelic-App-Data'
|
|
30
|
+
const INTERNAL_ERROR = 'internal-error'
|
|
30
31
|
|
|
31
32
|
export class Instrument extends InstrumentBase {
|
|
32
33
|
static featureName = FEATURE_NAME
|
|
@@ -62,16 +63,18 @@ export class Instrument extends InstrumentBase {
|
|
|
62
63
|
wrapFetch(this.ee, agentRef)
|
|
63
64
|
wrapXhr(this.ee, agentRef)
|
|
64
65
|
subscribeToEvents(agentRef, this.ee, this.handler, this.dt)
|
|
65
|
-
|
|
66
66
|
this.importAggregator(agentRef, () => import(/* webpackChunkName: "ajax-aggregate" */ '../aggregate/index.js'))
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
71
|
+
const shouldInterceptPayloads = [CAPTURE_PAYLOAD_SETTINGS.ALL, CAPTURE_PAYLOAD_SETTINGS.FAILURES].includes(agentRef.init.ajax?.capture_payloads)
|
|
72
|
+
|
|
71
73
|
ee.on('new-xhr', onNewXhr)
|
|
72
74
|
ee.on('open-xhr-start', onOpenXhrStart)
|
|
73
75
|
ee.on('open-xhr-end', onOpenXhrEnd)
|
|
74
76
|
ee.on('send-xhr-start', onSendXhrStart)
|
|
77
|
+
ee.on('setRequestHeader-xhr-start', onSetRequestHeader)
|
|
75
78
|
ee.on('xhr-cb-time', onXhrCbTime)
|
|
76
79
|
ee.on('xhr-load-added', onXhrLoadAdded)
|
|
77
80
|
ee.on('xhr-load-removed', onXhrLoadRemoved)
|
|
@@ -147,6 +150,14 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
|
|
153
|
+
function onSetRequestHeader (args, xhr) {
|
|
154
|
+
// args[0] = header name, args[1] = header value
|
|
155
|
+
if (shouldInterceptPayloads && args.length >= 2) {
|
|
156
|
+
this.requestHeaders ??= {}
|
|
157
|
+
this.requestHeaders[args[0].toLowerCase()] = args[1]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
150
161
|
function onSendXhrStart (args, xhr) {
|
|
151
162
|
var metrics = this.metrics
|
|
152
163
|
var data = args[0]
|
|
@@ -159,7 +170,7 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
159
170
|
|
|
160
171
|
this.startTime = now()
|
|
161
172
|
|
|
162
|
-
this.
|
|
173
|
+
this.requestBody = data
|
|
163
174
|
|
|
164
175
|
this.listener = function (evt) {
|
|
165
176
|
try {
|
|
@@ -169,7 +180,7 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
169
180
|
if (evt.type !== 'load' || ((context.called === context.totalCbs) && (context.onloadCalled || typeof (xhr.onload) !== 'function') && typeof context.end === 'function')) context.end(xhr)
|
|
170
181
|
} catch (e) {
|
|
171
182
|
try {
|
|
172
|
-
ee.emit(
|
|
183
|
+
ee.emit(INTERNAL_ERROR, [e])
|
|
173
184
|
} catch (err) {
|
|
174
185
|
// do nothing
|
|
175
186
|
}
|
|
@@ -321,9 +332,29 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
321
332
|
const method = ('' + ((target && target instanceof origRequest && target.method) ||
|
|
322
333
|
opts.method || 'GET')).toUpperCase()
|
|
323
334
|
this.params.method = method
|
|
324
|
-
this.body = opts.body
|
|
325
335
|
|
|
326
|
-
this.txSize = dataSize(opts.body) || 0
|
|
336
|
+
this.txSize = dataSize(opts.body || (target?.body)) || 0
|
|
337
|
+
|
|
338
|
+
// Capture request headers
|
|
339
|
+
try {
|
|
340
|
+
var headers = opts.headers || (target?.headers)
|
|
341
|
+
if (shouldInterceptPayloads && headers) {
|
|
342
|
+
this.requestHeaders ??= {}
|
|
343
|
+
if (headers instanceof Headers) {
|
|
344
|
+
headers.forEach(function (value, key) {
|
|
345
|
+
this.requestHeaders[key.toLowerCase()] = value
|
|
346
|
+
}.bind(this))
|
|
347
|
+
} else if (typeof headers === 'object') {
|
|
348
|
+
for (var key in headers) {
|
|
349
|
+
this.requestHeaders[key.toLowerCase()] = headers[key]
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
} catch (e) {
|
|
354
|
+
// Silently fail if we can't access headers
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
this.requestBody = opts.body || (target?.body)
|
|
327
358
|
}
|
|
328
359
|
|
|
329
360
|
// we capture failed call as status 0, the actual error is ignored
|
|
@@ -335,20 +366,47 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
335
366
|
|
|
336
367
|
this.params.status = res ? res.status : 0
|
|
337
368
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
responseSize =
|
|
369
|
+
const finishAndReport = () => {
|
|
370
|
+
// convert rxSize to a number - handle both string (from content-length header) and number (from fallback)
|
|
371
|
+
const num = +this.rxSize
|
|
372
|
+
const responseSize = this.rxSize != null && !isNaN(num) ? num : undefined
|
|
373
|
+
|
|
374
|
+
const metrics = {
|
|
375
|
+
txSize: this.txSize,
|
|
376
|
+
rxSize: responseSize,
|
|
377
|
+
duration: this.endTime - this.startTime
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const payload = [this.params, metrics, this.startTime, this.endTime, 'fetch']
|
|
381
|
+
this.targets.forEach(target => reportToAgg(payload, this, target))
|
|
342
382
|
}
|
|
343
383
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
384
|
+
/** Since accessing fetch bodies is an async process, these are
|
|
385
|
+
* reasonable conditions to check to not do needless extra work */
|
|
386
|
+
if (!res || !shouldInterceptPayloads) {
|
|
387
|
+
finishAndReport()
|
|
388
|
+
return
|
|
348
389
|
}
|
|
349
390
|
|
|
350
|
-
|
|
351
|
-
|
|
391
|
+
// Clone the response to read the body without consuming the original
|
|
392
|
+
res.clone().text().then((text) => {
|
|
393
|
+
this.responseBody = text
|
|
394
|
+
// Use captured payload size as fallback if content-length header was missing or is 0 with a body
|
|
395
|
+
// Only apply fallback for non-network-error responses (status !== 0)
|
|
396
|
+
if ((!this.rxSize || this.rxSize === '0' || this.rxSize === 0) && text !== undefined && this.params.status !== 0) {
|
|
397
|
+
this.rxSize = dataSize(text)
|
|
398
|
+
}
|
|
399
|
+
if (res?.headers) {
|
|
400
|
+
this.responseHeaders = {}
|
|
401
|
+
res.headers.forEach(function (value, key) {
|
|
402
|
+
this.responseHeaders[key.toLowerCase()] = value
|
|
403
|
+
}.bind(this))
|
|
404
|
+
}
|
|
405
|
+
}).catch((err) => {
|
|
406
|
+
ee.emit(INTERNAL_ERROR, [err])
|
|
407
|
+
}).finally(() => {
|
|
408
|
+
finishAndReport()
|
|
409
|
+
})
|
|
352
410
|
}
|
|
353
411
|
|
|
354
412
|
// Create report for XHR request that has finished
|
|
@@ -365,7 +423,7 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
365
423
|
if (params.aborted) return
|
|
366
424
|
if (hasUndefinedHostname(params)) return // don't bother with XHR of url with no hostname
|
|
367
425
|
|
|
368
|
-
metrics.duration =
|
|
426
|
+
metrics.duration = this.endTime - this.startTime
|
|
369
427
|
if (!this.loadCaptureCalled && xhr.readyState === 4) {
|
|
370
428
|
captureXhrData(this, xhr)
|
|
371
429
|
} else if (params.status == null) {
|
|
@@ -375,6 +433,27 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
375
433
|
// Always send cbTime, even if no noticeable time was taken.
|
|
376
434
|
metrics.cbTime = this.cbTime
|
|
377
435
|
|
|
436
|
+
if (shouldInterceptPayloads) {
|
|
437
|
+
try {
|
|
438
|
+
this.responseBody = xhr.responseText
|
|
439
|
+
} catch (e) {
|
|
440
|
+
this.responseBody = xhr.response
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Use captured payload size as fallback if not already determined or is 0 with a body
|
|
444
|
+
// Only apply fallback for non-network-error responses (status !== 0)
|
|
445
|
+
if ((!metrics.rxSize || metrics.rxSize === 0) && this.responseBody !== undefined && params.status !== 0) {
|
|
446
|
+
const size = dataSize(this.responseBody)
|
|
447
|
+
if (size !== undefined) metrics.rxSize = size
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
this.responseHeaders = parseResponseHeaders(xhr.getAllResponseHeaders())
|
|
452
|
+
} catch (err) {
|
|
453
|
+
ee.emit(INTERNAL_ERROR, [err])
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
378
457
|
const payload = [params, metrics, this.startTime, this.endTime, 'xhr']
|
|
379
458
|
this.targets.forEach(target => reportToAgg(payload, this, target))
|
|
380
459
|
}
|
|
@@ -387,7 +466,8 @@ function subscribeToEvents (agentRef, ee, handler, dt) {
|
|
|
387
466
|
ctx.params.status = xhr.status
|
|
388
467
|
|
|
389
468
|
var size = responseSizeFromXhr(xhr, ctx.lastSize)
|
|
390
|
-
|
|
469
|
+
// Don't set rxSize for network errors (status 0)
|
|
470
|
+
if (size !== undefined && xhr.status !== 0) ctx.metrics.rxSize = size
|
|
391
471
|
|
|
392
472
|
if (ctx.sameOrigin && xhr.getAllResponseHeaders().indexOf(NR_CAT_HEADER) >= 0) {
|
|
393
473
|
var header = xhr.getResponseHeader(NR_CAT_HEADER)
|
|
@@ -414,4 +494,20 @@ function addUrl (ctx, url) {
|
|
|
414
494
|
ctx.sameOrigin = parsed.sameOrigin
|
|
415
495
|
}
|
|
416
496
|
|
|
497
|
+
function parseResponseHeaders (headerStr) {
|
|
498
|
+
const headers = {}
|
|
499
|
+
if (!headerStr) return headers
|
|
500
|
+
|
|
501
|
+
headerStr.split('\r\n').forEach(function (line) {
|
|
502
|
+
const separatorIndex = line.indexOf(': ')
|
|
503
|
+
if (separatorIndex > 0) {
|
|
504
|
+
const name = line.substring(0, separatorIndex)
|
|
505
|
+
const value = line.substring(separatorIndex + 2)
|
|
506
|
+
headers[name.toLowerCase()] = value
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
return headers
|
|
511
|
+
}
|
|
512
|
+
|
|
417
513
|
export const Ajax = Instrument
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ export function formatStackTrace (stackLines) {
|
|
|
10
10
|
return truncateStackLines(stackLines).replace(stripNewlinesRegex, '')
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// takes array of stack lines and returns string with top 50 and
|
|
13
|
+
// takes array of stack lines and returns string with top 50 and bottom 50 lines
|
|
14
14
|
function truncateStackLines (stackLines) {
|
|
15
15
|
var stackString
|
|
16
16
|
if (stackLines.length > 100) {
|
|
@@ -26,5 +26,5 @@ function truncateStackLines (stackLines) {
|
|
|
26
26
|
|
|
27
27
|
// truncates stack string to limit what is sent to backend
|
|
28
28
|
export function truncateSize (stackString) {
|
|
29
|
-
return
|
|
29
|
+
return stackString.length > MAX_STACK_TRACE_LENGTH ? stackString.substr(0, MAX_STACK_TRACE_LENGTH) : stackString
|
|
30
30
|
}
|
|
@@ -267,7 +267,7 @@ export class Aggregate extends AggregateBase {
|
|
|
267
267
|
params.stack_trace = truncateSize(stackInfo.stackString)
|
|
268
268
|
this.observedAt[bucketHash] = Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(time))
|
|
269
269
|
} else {
|
|
270
|
-
params.browser_stack_hash = stringHashCode(stackInfo.stackString)
|
|
270
|
+
params.browser_stack_hash = stringHashCode(truncateSize(stackInfo.stackString))
|
|
271
271
|
}
|
|
272
272
|
params.releaseIds = stringify(this.agentRef.runtime.releaseIds)
|
|
273
273
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { addCustomAttributes, getAddStringContext, nullable, numeric } from '../../../common/serialize/bel-serializer'
|
|
6
|
+
import { createStringAdders } from '../../../common/payloads/payloads'
|
|
6
7
|
import { AJAX_ID } from '../../ajax/constants'
|
|
7
8
|
import { NODE_TYPE } from '../constants'
|
|
8
9
|
import { BelNode } from './bel-node'
|
|
@@ -25,6 +26,13 @@ export class AjaxNode extends BelNode {
|
|
|
25
26
|
this.targetAttributes = ajaxEvent.targetAttributes
|
|
26
27
|
this[AJAX_ID] = ajaxEvent[AJAX_ID] // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
27
28
|
|
|
29
|
+
// optional payload metadata attributes
|
|
30
|
+
this.requestBody = ajaxEvent.requestBody
|
|
31
|
+
this.requestHeaders = ajaxEvent.requestHeaders
|
|
32
|
+
this.requestQuery = ajaxEvent.requestQuery
|
|
33
|
+
this.responseBody = ajaxEvent.responseBody
|
|
34
|
+
this.responseHeaders = ajaxEvent.responseHeaders
|
|
35
|
+
|
|
28
36
|
this.start = ajaxEvent.startTime
|
|
29
37
|
this.end = ajaxEvent.endTime
|
|
30
38
|
if (ajaxContext?.latestLongtaskEnd) {
|
|
@@ -34,7 +42,8 @@ export class AjaxNode extends BelNode {
|
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
serialize (parentStartTimestamp, agentRef, ajaxObfuscator) {
|
|
37
|
-
const addString = getAddStringContext
|
|
45
|
+
const { addString, addStringWithTruncation } = createStringAdders(getAddStringContext, ajaxObfuscator)
|
|
46
|
+
|
|
38
47
|
const nodeList = []
|
|
39
48
|
|
|
40
49
|
// IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
|
|
@@ -55,11 +64,23 @@ export class AjaxNode extends BelNode {
|
|
|
55
64
|
addString(this.nodeId),
|
|
56
65
|
nullable(this.spanId, addString, true) + nullable(this.traceId, addString, true) + nullable(this.spanTimestamp, numeric)
|
|
57
66
|
]
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
// Regular attributes: obfuscate only
|
|
68
|
+
const regularAttrs = addCustomAttributes({
|
|
69
|
+
[AJAX_ID]: this[AJAX_ID],
|
|
60
70
|
...(this.targetAttributes || {}),
|
|
61
|
-
|
|
71
|
+
...(this.gql || {})
|
|
62
72
|
}, addString)
|
|
73
|
+
|
|
74
|
+
// Payload attributes: obfuscate then truncate
|
|
75
|
+
const payloadAttrs = addCustomAttributes({
|
|
76
|
+
...(this.requestBody ? { requestBody: this.requestBody } : {}),
|
|
77
|
+
...(this.requestHeaders ? { requestHeaders: this.requestHeaders } : {}),
|
|
78
|
+
...(this.requestQuery ? { requestQuery: this.requestQuery } : {}),
|
|
79
|
+
...(this.responseBody ? { responseBody: this.responseBody } : {}),
|
|
80
|
+
...(this.responseHeaders ? { responseHeaders: this.responseHeaders } : {})
|
|
81
|
+
}, addStringWithTruncation)
|
|
82
|
+
|
|
83
|
+
let allAttachedNodes = [...regularAttrs, ...payloadAttrs]
|
|
63
84
|
this.children.forEach(node => allAttachedNodes.push(node.serialize())) // no children is expected under ajax nodes at this time
|
|
64
85
|
|
|
65
86
|
fields[1] = numeric(allAttachedNodes.length)
|