newrelic 5.13.0 → 6.2.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/.travis.yml +20 -21
- package/NEWS.md +166 -0
- package/README.md +10 -10
- package/api.js +84 -2
- package/bin/run-versioned-tests.sh +3 -1
- package/bin/ssl.sh +43 -1
- package/bin/travis-node.sh +51 -0
- package/bin/travis-setup.sh +0 -4
- package/lib/agent.js +148 -315
- package/lib/aggregators/base-aggregator.js +3 -12
- package/lib/collector/api.js +2 -1
- package/lib/collector/facts.js +1 -1
- package/lib/config/default.js +8 -11
- package/lib/config/env.js +19 -1
- package/lib/config/index.js +52 -8
- package/lib/injector.js +161 -0
- package/lib/instrumentation/bluebird.js +4 -3
- package/lib/instrumentation/core/http-outbound.js +1 -0
- package/lib/instrumentation/mysql.js +10 -2
- package/lib/instrumentations.js +1 -1
- package/lib/shimmer.js +153 -5
- package/lib/spans/span-event-aggregator.js +1 -1
- package/lib/spans/span-event.js +1 -1
- package/lib/transaction/handle.js +11 -0
- package/lib/transaction/index.js +14 -3
- package/lib/transaction/trace/segment.js +10 -0
- package/lib/utilization/common.js +11 -4
- package/package.json +14 -11
- package/stub_api.js +8 -0
- package/lib/event-aggregator.js +0 -94
- package/lib/harvest.js +0 -718
- package/lib/instrumentation/aws-sdk.js +0 -25
package/lib/harvest.js
DELETED
|
@@ -1,718 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const a = require('async')
|
|
4
|
-
const CollectorResponse = require('./collector/response')
|
|
5
|
-
const logger = require('./logger').child({component: 'Harvest'})
|
|
6
|
-
|
|
7
|
-
const FROM_MILLIS = 1e-3
|
|
8
|
-
const NAMES = require('./metrics/names')
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Collects, formats, and cleans up data for a single harvest endpoint.
|
|
12
|
-
*
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
|
-
class HarvestStep {
|
|
16
|
-
constructor(harvest, endpoint, datasource) {
|
|
17
|
-
this.harvest = harvest
|
|
18
|
-
this.success = false
|
|
19
|
-
this.datasource = datasource
|
|
20
|
-
this.result = null
|
|
21
|
-
this.returned = null
|
|
22
|
-
this._endpoint = endpoint
|
|
23
|
-
this._payloads = null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get agent() {
|
|
27
|
-
return this.harvest.agent
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get name() {
|
|
31
|
-
return this._endpoint
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Assembles the payloads to be sent to the collector.
|
|
36
|
-
*
|
|
37
|
-
* @abstract
|
|
38
|
-
* @protected
|
|
39
|
-
*/
|
|
40
|
-
preparePayloads(runId, datasource, callback) { // eslint-disable-line no-unused-vars
|
|
41
|
-
callback(null, this.preparePayloadsSync(runId, datasource))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Synchronously assembles the payloads to be sent to the collector.
|
|
46
|
-
*
|
|
47
|
-
* @abstract
|
|
48
|
-
* @protected
|
|
49
|
-
*/
|
|
50
|
-
preparePayloadsSync(runId, datasource) { // eslint-disable-line no-unused-vars
|
|
51
|
-
throw new Error(
|
|
52
|
-
'Synchronous payload preparation not implemented for ' + this._endpoint
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Merges the indicated payload back into the aggregator for future collection.
|
|
58
|
-
*
|
|
59
|
-
* @abstract
|
|
60
|
-
* @protected
|
|
61
|
-
*/
|
|
62
|
-
mergePayload(payload, idx) { // eslint-disable-line no-unused-vars
|
|
63
|
-
throw new Error('Payload merging not implemented for ' + this._endpoint)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* When the datasource is an `EventAggregator`, this method will generate
|
|
68
|
-
* metrics around how many were seen and will be sent.
|
|
69
|
-
*
|
|
70
|
-
* @protected
|
|
71
|
-
*
|
|
72
|
-
* @param {string} metrics.SEEN - The name of the events seen metric.
|
|
73
|
-
* @param {string} metrics.SENT - The name of the events sent metric.
|
|
74
|
-
* @param {string} metrics.DROPPED - The name of the events dropped metric.
|
|
75
|
-
*/
|
|
76
|
-
createSamplerMetrics(metrics) {
|
|
77
|
-
// Create all the metrics.
|
|
78
|
-
const seenMetric = this.agent.metrics.getOrCreateMetric(metrics.SEEN)
|
|
79
|
-
const sentMetric = this.agent.metrics.getOrCreateMetric(metrics.SENT)
|
|
80
|
-
const droppedMetric = this.agent.metrics.getOrCreateMetric(metrics.DROPPED)
|
|
81
|
-
|
|
82
|
-
// Calculate our seen/sent/dropped counts and record them.
|
|
83
|
-
const seen = this.datasource.seen
|
|
84
|
-
const sent = this.datasource.length
|
|
85
|
-
const dropped = seen - sent
|
|
86
|
-
seenMetric.incrementCallCount(seen)
|
|
87
|
-
sentMetric.incrementCallCount(sent)
|
|
88
|
-
droppedMetric.incrementCallCount(dropped)
|
|
89
|
-
|
|
90
|
-
// If we dropped any, let the customer know.
|
|
91
|
-
if (dropped) {
|
|
92
|
-
logger.warn('Dropped %d of %d datapoints for %s.', dropped, seen, this._endpoint)
|
|
93
|
-
logger.warn('You may want to increase the limits for this event type.')
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Gets the harvest step ready to send.
|
|
99
|
-
*/
|
|
100
|
-
prepare(callback) {
|
|
101
|
-
if (!this.datasource || this.datasource.length === 0) {
|
|
102
|
-
logger.debug('No data to send to %s', this._endpoint)
|
|
103
|
-
return setImmediate(callback)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
this.preparePayloads(this.agent.config.run_id, this.datasource, (err, payloads) => {
|
|
107
|
-
if (err) {
|
|
108
|
-
logger.debug('Failed to prepare payloads for %s', this._endpoint)
|
|
109
|
-
}
|
|
110
|
-
this._payloads = payloads
|
|
111
|
-
callback(err)
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Synchronously gets the harvest step ready to send.
|
|
117
|
-
*/
|
|
118
|
-
prepareSync() {
|
|
119
|
-
if (!this.datasource || this.datasource.length === 0) {
|
|
120
|
-
logger.debug('No data to send to %s', this._endpoint)
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
this._payloads = this.preparePayloadsSync(this.agent.config.run_id, this.datasource)
|
|
126
|
-
return this._payloads
|
|
127
|
-
} catch (err) {
|
|
128
|
-
logger.debug('Failed to prepare payloads for %s', this._endpoint)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Sends all prepared payloads to the collector.
|
|
134
|
-
*/
|
|
135
|
-
send(callback) {
|
|
136
|
-
if (!this._payloads) {
|
|
137
|
-
logger.debug('Payloads were not generated for %s', this._endpoint)
|
|
138
|
-
return setImmediate(callback)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Send each of the payloads in series.
|
|
142
|
-
const self = this
|
|
143
|
-
a.eachOfSeries(self._payloads, function sendEachPayload(payload, i, cb) {
|
|
144
|
-
logger.trace(
|
|
145
|
-
'Sending payload %d of %d to %s',
|
|
146
|
-
i + 1,
|
|
147
|
-
self._payloads.length,
|
|
148
|
-
self._endpoint
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
// Send the payload to the collector.
|
|
152
|
-
let callbackCalled = false
|
|
153
|
-
self._doSend(payload, i, function oncedCallback(err) {
|
|
154
|
-
if (callbackCalled) {
|
|
155
|
-
logger.info(
|
|
156
|
-
'Onced callback called multiple times in ' + self._endpoint,
|
|
157
|
-
err
|
|
158
|
-
)
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
callbackCalled = true
|
|
162
|
-
logger.trace(
|
|
163
|
-
'Onced callback called first time for ' + self._endpoint,
|
|
164
|
-
err
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
return cb(err)
|
|
168
|
-
})
|
|
169
|
-
}, function afterSendingAllPayloads(err) {
|
|
170
|
-
if (!err) {
|
|
171
|
-
self.success = true
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
logger.trace('Finished sending payloads for ' + self._endpoint)
|
|
175
|
-
|
|
176
|
-
callback(err)
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Performs actual payload sending and retrying.
|
|
182
|
-
*
|
|
183
|
-
* @private
|
|
184
|
-
*/
|
|
185
|
-
_doSend(payload, i, callback) {
|
|
186
|
-
const self = this
|
|
187
|
-
self._trySend(payload, function afterSend(err, response) {
|
|
188
|
-
if (err) {
|
|
189
|
-
return callback(err)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Are we clearing our data?
|
|
193
|
-
if (!response.retainData) {
|
|
194
|
-
self._payloads[i] = null
|
|
195
|
-
} else {
|
|
196
|
-
logger.info('Failed to submit data to New Relic, data held for redelivery.')
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Do we need to retry this endpoint right now?
|
|
200
|
-
if (response.retryAfter) {
|
|
201
|
-
const delay = response.retryAfter
|
|
202
|
-
logger.info('Retrying sending to %s in %d ms', self._endpoint, delay)
|
|
203
|
-
setTimeout(() => self._doSend(payload, i, callback), delay)
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Done!
|
|
208
|
-
self.result = response
|
|
209
|
-
self.payload = response.payload
|
|
210
|
-
callback()
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
_trySend(payload, callback) {
|
|
215
|
-
try {
|
|
216
|
-
this.agent.collector[this._endpoint](payload, callback)
|
|
217
|
-
} catch (err) {
|
|
218
|
-
logger.warn(err, 'Failed to call collector method %s', this._endpoint)
|
|
219
|
-
callback(err)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Finishes the harvest step, performing any final cleanup steps.
|
|
225
|
-
*/
|
|
226
|
-
finalize(callback) {
|
|
227
|
-
if (this._payloads) {
|
|
228
|
-
this._payloads.forEach((payload, idx) => {
|
|
229
|
-
if (payload) {
|
|
230
|
-
this.mergePayload(payload, idx)
|
|
231
|
-
}
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
setImmediate(callback)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// -------------------------------------------------------------------------- //
|
|
240
|
-
|
|
241
|
-
class CustomEventsHarvest extends HarvestStep {
|
|
242
|
-
constructor(harvest) {
|
|
243
|
-
super(harvest, 'customEvents', harvest.agent.customEventAggregator)
|
|
244
|
-
this.createSamplerMetrics(NAMES.CUSTOM_EVENTS)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
preparePayloadsSync(runId, customEvents) {
|
|
248
|
-
if (!customEvents || !customEvents.length) {
|
|
249
|
-
logger.debug('No custom events to send.')
|
|
250
|
-
return []
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return [[runId, customEvents.toArray()]]
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
mergePayload() {
|
|
257
|
-
this.agent.customEventAggregator.merge(this.datasource)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// -------------------------------------------------------------------------- //
|
|
262
|
-
|
|
263
|
-
class ErrorEventHarvest extends HarvestStep {
|
|
264
|
-
constructor(harvest) {
|
|
265
|
-
super(harvest, 'errorEvents', harvest.agent.errors.getQueue())
|
|
266
|
-
this.createSamplerMetrics(NAMES.TRANSACTION_ERROR)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
preparePayloadsSync(runId, errorQueue) {
|
|
270
|
-
if (!errorQueue || !errorQueue.length) {
|
|
271
|
-
logger.debug('No error events to send.')
|
|
272
|
-
return []
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const metrics = {
|
|
276
|
-
reservoir_size: errorQueue.limit,
|
|
277
|
-
events_seen: errorQueue.seen
|
|
278
|
-
}
|
|
279
|
-
return [[runId, metrics, errorQueue.toArray()]]
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
mergePayload() {
|
|
283
|
-
this.agent.errors.mergeEvents(this.datasource)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// -------------------------------------------------------------------------- //
|
|
288
|
-
|
|
289
|
-
class ErrorTraceHarvest extends HarvestStep {
|
|
290
|
-
constructor(harvest) {
|
|
291
|
-
const errorAggr = harvest.agent.errors
|
|
292
|
-
super(harvest, 'errorData', errorAggr.getErrors())
|
|
293
|
-
|
|
294
|
-
const metrics = harvest.agent.metrics
|
|
295
|
-
|
|
296
|
-
// Generate metrics for collected errors.
|
|
297
|
-
if (errorAggr.getTotalUnexpectedErrorCount() > 0) {
|
|
298
|
-
let count = errorAggr.getTotalUnexpectedErrorCount()
|
|
299
|
-
metrics.getOrCreateMetric(NAMES.ERRORS.ALL).incrementCallCount(count)
|
|
300
|
-
|
|
301
|
-
count = errorAggr.getUnexpectedWebTransactionsErrorCount()
|
|
302
|
-
metrics.getOrCreateMetric(NAMES.ERRORS.WEB).incrementCallCount(count)
|
|
303
|
-
|
|
304
|
-
count = errorAggr.getUnexpectedOtherTransactionsErrorCount()
|
|
305
|
-
metrics.getOrCreateMetric(NAMES.ERRORS.OTHER).incrementCallCount(count)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const expectedCount = errorAggr.getTotalExpectedErrorCount()
|
|
309
|
-
if (expectedCount > 0) {
|
|
310
|
-
metrics.getOrCreateMetric(NAMES.ERRORS.EXPECTED).incrementCallCount(expectedCount)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
preparePayloadsSync(runId, errors) {
|
|
315
|
-
if (!errors || !errors.length) {
|
|
316
|
-
logger.debug('No error traces to send.')
|
|
317
|
-
return []
|
|
318
|
-
}
|
|
319
|
-
return [[runId, errors]]
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
mergePayload() {
|
|
323
|
-
this.agent.errors.mergeErrors(this.datasource)
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// -------------------------------------------------------------------------- //
|
|
328
|
-
|
|
329
|
-
class MetricsHarvest extends HarvestStep {
|
|
330
|
-
constructor(harvest) {
|
|
331
|
-
super(harvest, 'metricData', harvest.agent.metrics)
|
|
332
|
-
this._beginSeconds = harvest.agent.metrics.started * FROM_MILLIS
|
|
333
|
-
this._endSeconds = Date.now() * FROM_MILLIS
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
preparePayloadsSync(runId, metrics) {
|
|
337
|
-
if (!metrics || metrics.empty) {
|
|
338
|
-
logger.debug('No metrics to send.')
|
|
339
|
-
return []
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return [[runId, this._beginSeconds, this._endSeconds, metrics.toJSON()]]
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
mergePayload() {
|
|
346
|
-
this.agent.metrics.merge(this.datasource, true)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
finalize(callback) {
|
|
350
|
-
// The collector may send back metric naming rules for us to load.
|
|
351
|
-
if (this.payload) {
|
|
352
|
-
this.agent.mapper.load(this.payload)
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
super.finalize(callback)
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// -------------------------------------------------------------------------- //
|
|
360
|
-
|
|
361
|
-
class QueryHarvest extends HarvestStep {
|
|
362
|
-
constructor(harvest) {
|
|
363
|
-
super(harvest, 'queryData', harvest.agent.queries)
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
preparePayloads(runId, queries, callback) {
|
|
367
|
-
if (!queries || !queries.samples.size) {
|
|
368
|
-
logger.debug('No queries to send.')
|
|
369
|
-
return setImmediate(callback, null, [])
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
queries.prepareJSON((err, data) => callback(err, [[data]]))
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
preparePayloadsSync(runId, queries) {
|
|
376
|
-
if (!queries || !queries.samples.size) {
|
|
377
|
-
logger.debug('No queries to send.')
|
|
378
|
-
return []
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return [[queries.prepareJSONSync()]]
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
mergePayload() {
|
|
385
|
-
this.agent.queries.merge(this.datasource)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// -------------------------------------------------------------------------- //
|
|
390
|
-
|
|
391
|
-
class SpanEventHarvest extends HarvestStep {
|
|
392
|
-
constructor(harvest) {
|
|
393
|
-
super(harvest, 'spanEvents', harvest.agent.spanEventAggregator.getQueue())
|
|
394
|
-
this.createSamplerMetrics(NAMES.SPAN_EVENTS)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
preparePayloadsSync(runId, spanQueue) {
|
|
398
|
-
if (!spanQueue || !spanQueue.length) {
|
|
399
|
-
logger.debug('No span events to send.')
|
|
400
|
-
return []
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const metrics = {
|
|
404
|
-
reservoir_size: spanQueue.limit,
|
|
405
|
-
events_seen: spanQueue.seen
|
|
406
|
-
}
|
|
407
|
-
return [[runId, metrics, spanQueue.toArray()]]
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
mergePayload() {
|
|
411
|
-
this.agent.spanEventAggregator.mergeEvents(this.datasource)
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// -------------------------------------------------------------------------- //
|
|
416
|
-
|
|
417
|
-
class TransactionEventHarvest extends HarvestStep {
|
|
418
|
-
constructor(harvest) {
|
|
419
|
-
super(harvest, 'analyticsEvents', harvest.agent.events)
|
|
420
|
-
this.createSamplerMetrics(NAMES.EVENTS)
|
|
421
|
-
this._mergeablePayloads = []
|
|
422
|
-
}
|
|
423
|
-
preparePayloadsSync(runId, events) {
|
|
424
|
-
if (!events || !events.length) {
|
|
425
|
-
logger.debug('No transaction events to send.')
|
|
426
|
-
return []
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const splits = _splitPayload(runId, events)
|
|
430
|
-
const payloads = []
|
|
431
|
-
splits.forEach((split) => {
|
|
432
|
-
this._mergeablePayloads.push(split.toMerge)
|
|
433
|
-
payloads.push(split.payload)
|
|
434
|
-
})
|
|
435
|
-
return payloads
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
mergePayload(payload, idx) {
|
|
439
|
-
if (this._mergeablePayloads[idx]) {
|
|
440
|
-
this.agent.events.merge(this._mergeablePayloads[idx])
|
|
441
|
-
this._mergeablePayloads[idx] = null
|
|
442
|
-
} else {
|
|
443
|
-
logger.debug('Invalid idx (%d) provided for merging transaction events.', idx)
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// -------------------------------------------------------------------------- //
|
|
449
|
-
|
|
450
|
-
class TransactionTraceHarvest extends HarvestStep {
|
|
451
|
-
constructor(harvest) {
|
|
452
|
-
const traceAggr = harvest.agent.traces
|
|
453
|
-
const maxTraceSegments = harvest.agent.config.max_trace_segments
|
|
454
|
-
const traces = [].concat(traceAggr.syntheticsTraces)
|
|
455
|
-
|
|
456
|
-
if (traceAggr.trace) {
|
|
457
|
-
const trace = traceAggr.trace
|
|
458
|
-
if (trace.segmentsSeen > maxTraceSegments) {
|
|
459
|
-
logger.warn(
|
|
460
|
-
'Transaction %s (%s) contained %d segments, only collecting the first %d',
|
|
461
|
-
trace.transaction.name,
|
|
462
|
-
trace.transaction.id,
|
|
463
|
-
trace.segmentsSeen,
|
|
464
|
-
maxTraceSegments
|
|
465
|
-
)
|
|
466
|
-
}
|
|
467
|
-
traceAggr.noTraceSubmitted = 0
|
|
468
|
-
traces.push(trace)
|
|
469
|
-
} else if (++traceAggr.noTraceSubmitted >= 5) {
|
|
470
|
-
traceAggr.resetTimingTracker()
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
super(harvest, 'transactionSampleData', traces)
|
|
474
|
-
this._traces = traces
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
preparePayloads(runId, traces, callback) {
|
|
478
|
-
if (!traces.length) {
|
|
479
|
-
logger.debug('No transaction traces to send.')
|
|
480
|
-
return setImmediate(callback, null, [])
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
a.map(
|
|
484
|
-
traces,
|
|
485
|
-
(trace, cb) => trace.generateJSON(cb),
|
|
486
|
-
(err, encodedTraces) => callback(err, [[runId, encodedTraces]])
|
|
487
|
-
)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
preparePayloadsSync(runId, traces) {
|
|
491
|
-
if (!traces.length) {
|
|
492
|
-
logger.debug('No transaction traces to send.')
|
|
493
|
-
return []
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return [[
|
|
497
|
-
runId,
|
|
498
|
-
traces.map((trace) => trace.generateJSONSync())
|
|
499
|
-
]]
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
mergePayload() {
|
|
503
|
-
if (this._traces) {
|
|
504
|
-
for (let i = 0; i < this._traces.length; ++i) {
|
|
505
|
-
this.agent.traces.add(this._traces[i].transaction)
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
508
|
-
logger.debug('No transaction traces to merge back.')
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
finalize(callback) {
|
|
513
|
-
if (this.success) {
|
|
514
|
-
++this.agent.traces.reported
|
|
515
|
-
}
|
|
516
|
-
super.finalize(callback)
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// -------------------------------------------------------------------------- //
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Sequences harvest steps and manages the harvest cycle.
|
|
524
|
-
*
|
|
525
|
-
* @private
|
|
526
|
-
*/
|
|
527
|
-
class Harvest {
|
|
528
|
-
constructor(agent) {
|
|
529
|
-
this.agent = agent
|
|
530
|
-
this.startTime = Date.now()
|
|
531
|
-
this._steps = Object.create(null)
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
static get ALL_ENDPOINTS() {
|
|
535
|
-
return {
|
|
536
|
-
customEvents: false,
|
|
537
|
-
metrics: false,
|
|
538
|
-
errorEvents: false,
|
|
539
|
-
errorTraces: false,
|
|
540
|
-
transactionTraces: false,
|
|
541
|
-
transactionEvents: false,
|
|
542
|
-
queries: false,
|
|
543
|
-
spanEvents: false
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Assembles all the harvest steps that this harvest will perform.
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* @param {object.<string,bool>} endpoints
|
|
552
|
-
* A map indicating all the endpoints that
|
|
553
|
-
*/
|
|
554
|
-
prepare(endpoints) {
|
|
555
|
-
// Fetch references to configuration pieces to simplify checks below.
|
|
556
|
-
const config = this.agent.config
|
|
557
|
-
const ecConfig = config.error_collector
|
|
558
|
-
|
|
559
|
-
// Create steps for each of the requested endpoints.
|
|
560
|
-
if (endpoints.customEvents && config.custom_insights_events.enabled) {
|
|
561
|
-
this._steps.customEvents = new CustomEventsHarvest(this)
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (endpoints.metrics) {
|
|
565
|
-
this._steps.metrics = new MetricsHarvest(this)
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (endpoints.errorEvents && ecConfig.enabled && ecConfig.capture_events) {
|
|
569
|
-
this._steps.errorEvents = new ErrorEventHarvest(this)
|
|
570
|
-
}
|
|
571
|
-
if (endpoints.errorTraces && config.collect_errors && ecConfig.enabled) {
|
|
572
|
-
this._steps.errorTraces = new ErrorTraceHarvest(this)
|
|
573
|
-
}
|
|
574
|
-
if (
|
|
575
|
-
endpoints.transactionTraces &&
|
|
576
|
-
config.collect_traces &&
|
|
577
|
-
config.transaction_tracer.enabled
|
|
578
|
-
) {
|
|
579
|
-
this._steps.transactionTraces = new TransactionTraceHarvest(this)
|
|
580
|
-
}
|
|
581
|
-
if (endpoints.transactionEvents && config.transaction_events.enabled) {
|
|
582
|
-
this._steps.transactionEvents = new TransactionEventHarvest(this)
|
|
583
|
-
}
|
|
584
|
-
if (endpoints.queries && config.slow_sql.enabled) {
|
|
585
|
-
this._steps.queries = new QueryHarvest(this)
|
|
586
|
-
}
|
|
587
|
-
if (
|
|
588
|
-
endpoints.spanEvents &&
|
|
589
|
-
config.span_events.enabled &&
|
|
590
|
-
config.distributed_tracing.enabled
|
|
591
|
-
) {
|
|
592
|
-
this._steps.spanEvents = new SpanEventHarvest(this)
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
if (logger.traceEnabled()) {
|
|
596
|
-
logger.trace(endpoints, 'Harvesting %j', Object.keys(this._steps))
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
send(callback) {
|
|
601
|
-
const self = this
|
|
602
|
-
|
|
603
|
-
a.map(this._steps, function eachHarvestStep(step, cb) {
|
|
604
|
-
logger.trace('Doing harvest step %s.', step.name)
|
|
605
|
-
if (!self.agent.collector.isConnected()) {
|
|
606
|
-
const connectionLostMessage = 'Connection to New Relic lost during harvest.'
|
|
607
|
-
logger.warn(connectionLostMessage)
|
|
608
|
-
return setImmediate(callback, new Error(connectionLostMessage))
|
|
609
|
-
}
|
|
610
|
-
a.series([
|
|
611
|
-
step.prepare.bind(step),
|
|
612
|
-
step.send.bind(step)
|
|
613
|
-
], function afterHarvestStep(err) {
|
|
614
|
-
step.finalize(function afterFinalize(finalizeErr) {
|
|
615
|
-
// log finalize errors as may be hidden by errors in other steps
|
|
616
|
-
// and errors during finalize might result in incorrect data retention
|
|
617
|
-
if (finalizeErr) {
|
|
618
|
-
logger.warn(finalizeErr, 'Error during finalize of harvest step.')
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
cb(null, {
|
|
622
|
-
error: err || finalizeErr || null,
|
|
623
|
-
agentRun: step.result && step.result.agentRun
|
|
624
|
-
})
|
|
625
|
-
})
|
|
626
|
-
})
|
|
627
|
-
}, function afterAllHarvestSteps(err, results) {
|
|
628
|
-
const BEHAVIOR = CollectorResponse.AGENT_RUN_BEHAVIOR
|
|
629
|
-
let agentRunAction = BEHAVIOR.PRESERVE
|
|
630
|
-
|
|
631
|
-
if (err) {
|
|
632
|
-
// Any runtime errors should preserve the agent run.
|
|
633
|
-
callback(err, agentRunAction)
|
|
634
|
-
return
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// Pull out and log any errors from harvest steps.
|
|
638
|
-
const errors = results.map((r) => r.error).filter((e) => !!e)
|
|
639
|
-
if (errors.length > 0) {
|
|
640
|
-
logger.warn({errors}, 'Errors during harvest!')
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// See if any endpoints told us to shutdown or restart. A shutdown trumps
|
|
644
|
-
// everything, restart just trumps a preserve.
|
|
645
|
-
for (let i = 0; i < results.length; ++i) {
|
|
646
|
-
const agentRun = results[i].agentRun
|
|
647
|
-
if (agentRun === BEHAVIOR.SHUTDOWN) {
|
|
648
|
-
agentRunAction = BEHAVIOR.SHUTDOWN
|
|
649
|
-
break
|
|
650
|
-
} else if (agentRun === BEHAVIOR.RESTART) {
|
|
651
|
-
agentRunAction = BEHAVIOR.RESTART
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
callback(errors[0], agentRunAction)
|
|
656
|
-
})
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
getPayloads() {
|
|
660
|
-
const stepNames = Object.keys(this._steps)
|
|
661
|
-
const harvest = this
|
|
662
|
-
// This will only grab the first payload in a split payload case
|
|
663
|
-
return stepNames.reduce(function processStep(processedSteps, name) {
|
|
664
|
-
const payload = harvest._steps[name].prepareSync()
|
|
665
|
-
if (payload) {
|
|
666
|
-
processedSteps[name] = payload[0]
|
|
667
|
-
}
|
|
668
|
-
return processedSteps
|
|
669
|
-
}, Object.create(null))
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function _splitPayload(runId, queue) {
|
|
674
|
-
// If we're less than 1/3 full, don't bother splitting the payload.
|
|
675
|
-
if (queue.length === 0) {
|
|
676
|
-
return []
|
|
677
|
-
}
|
|
678
|
-
if (queue.length < queue.limit / 3) {
|
|
679
|
-
return [{
|
|
680
|
-
toMerge: queue,
|
|
681
|
-
payload: [
|
|
682
|
-
runId,
|
|
683
|
-
{reservoir_size: queue.limit, events_seen: queue.seen},
|
|
684
|
-
queue.toArray()
|
|
685
|
-
]
|
|
686
|
-
}]
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Our payload is large, so split it in half.
|
|
690
|
-
// TODO: update this to pull the priority off the event when DT is released
|
|
691
|
-
const events = queue.getRawEvents()
|
|
692
|
-
const size = Math.floor(queue.length / 2)
|
|
693
|
-
const limit = Math.floor(queue.limit / 2)
|
|
694
|
-
const seen = Math.floor(queue.seen / 2)
|
|
695
|
-
const firstHalf = events.splice(0, size)
|
|
696
|
-
|
|
697
|
-
return [{
|
|
698
|
-
toMerge: firstHalf,
|
|
699
|
-
payload: [
|
|
700
|
-
runId,
|
|
701
|
-
{reservoir_size: limit, events_seen: seen},
|
|
702
|
-
firstHalf.map(rawEventsToValues)
|
|
703
|
-
]
|
|
704
|
-
}, {
|
|
705
|
-
toMerge: events,
|
|
706
|
-
payload: [
|
|
707
|
-
runId,
|
|
708
|
-
{reservoir_size: queue.limit - limit, events_seen: queue.seen - seen},
|
|
709
|
-
events.map(rawEventsToValues)
|
|
710
|
-
]
|
|
711
|
-
}]
|
|
712
|
-
|
|
713
|
-
function rawEventsToValues(ev) {
|
|
714
|
-
return ev.value
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
module.exports = Harvest
|