newrelic 5.9.1 → 5.13.1
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 +14 -20
- package/CONTRIBUTING.md +4 -4
- package/NEWS.md +141 -0
- package/api.js +4 -3
- package/bin/cassandra-setup.sh +8 -0
- package/bin/run-versioned-tests.sh +3 -1
- package/bin/travis-install-mongo.sh +10 -6
- package/bin/travis-setup.sh +3 -1
- package/lib/agent.js +380 -307
- package/lib/aggregators/base-aggregator.js +111 -0
- package/lib/aggregators/event-aggregator.js +148 -0
- package/lib/aggregators/trace-aggregator.js +11 -0
- package/lib/collector/api.js +133 -48
- package/lib/collector/facts.js +10 -1
- package/lib/collector/http-agents.js +43 -4
- package/lib/collector/remote-method.js +2 -13
- package/lib/collector/serverless.js +34 -53
- package/lib/config/default.js +14 -1
- package/lib/config/env.js +13 -1
- package/lib/config/harvest-config-validator.js +26 -0
- package/lib/config/index.js +316 -56
- package/lib/config/lasp.js +5 -5
- package/lib/config/merge-server-config.js +56 -0
- package/lib/custom-events/custom-event-aggregator.js +31 -0
- package/lib/db/parsed-statement.js +1 -1
- package/lib/db/query-sample.js +110 -0
- package/lib/db/query-trace-aggregator.js +139 -0
- package/lib/db/slow-query.js +34 -0
- package/lib/errors/{aggregator.js → error-collector.js} +196 -149
- package/lib/errors/error-event-aggregator.js +36 -0
- package/lib/errors/error-trace-aggregator.js +59 -0
- package/lib/errors/helper.js +137 -0
- package/lib/errors/index.js +25 -35
- package/lib/injector.js +161 -0
- package/lib/instrumentation/@hapi/hapi.js +3 -0
- package/lib/instrumentation/core/globals.js +6 -13
- package/lib/instrumentation/core/http-outbound.js +3 -1
- package/lib/instrumentations.js +1 -0
- package/lib/metrics/index.js +4 -4
- package/lib/metrics/metric-aggregator.js +176 -0
- package/lib/metrics/names.js +14 -1
- package/lib/serverless/aws-lambda.js +33 -1
- package/lib/shimmer.js +120 -4
- package/lib/spans/span-event-aggregator.js +72 -0
- package/lib/transaction/index.js +47 -2
- package/lib/transaction/trace/aggregator.js +246 -145
- package/lib/transaction/trace/index.js +4 -4
- package/lib/transaction/transaction-event-aggregator.js +149 -0
- package/lib/util/urltils.js +23 -0
- package/lib/utilization/common.js +11 -4
- package/package.json +13 -11
- package/lib/db/tracer.js +0 -237
- package/lib/event-aggregator.js +0 -94
- package/lib/harvest.js +0 -708
- package/lib/spans/aggregator.js +0 -39
package/lib/agent.js
CHANGED
|
@@ -4,27 +4,27 @@ const AdaptiveSampler = require('./adaptive-sampler')
|
|
|
4
4
|
const CollectorAPI = require('./collector/api')
|
|
5
5
|
const ServerlessCollector = require('./collector/serverless')
|
|
6
6
|
const DESTINATIONS = require('./config/attribute-filter').DESTINATIONS
|
|
7
|
-
const
|
|
7
|
+
const CustomEventAggregator = require('./custom-events/custom-event-aggregator')
|
|
8
|
+
const ErrorCollector = require('./errors/error-collector')
|
|
9
|
+
const ErrorTraceAggregator = require('./errors/error-trace-aggregator')
|
|
10
|
+
const ErrorEventAggregator = require('./errors/error-event-aggregator')
|
|
8
11
|
const EventEmitter = require('events').EventEmitter
|
|
9
|
-
const Harvest = require('./harvest')
|
|
10
12
|
const hashes = require('./util/hashes')
|
|
11
13
|
const logger = require('./logger')
|
|
12
14
|
const MetricMapper = require('./metrics/mapper')
|
|
13
15
|
const MetricNormalizer = require('./metrics/normalizer')
|
|
14
|
-
const
|
|
16
|
+
const MetricAggregator = require('./metrics/metric-aggregator')
|
|
15
17
|
const NAMES = require('./metrics/names')
|
|
16
|
-
const
|
|
17
|
-
const QueryTracer = require('./db/tracer')
|
|
18
|
+
const QueryTraceAggregator = require('./db/query-trace-aggregator')
|
|
18
19
|
const sampler = require('./sampler')
|
|
19
|
-
const
|
|
20
|
-
const
|
|
20
|
+
const TransactionTraceAggregator = require('./transaction/trace/aggregator')
|
|
21
|
+
const SpanEventAggregator = require('./spans/span-event-aggregator')
|
|
22
|
+
const TransactionEventAggregator = require('./transaction/transaction-event-aggregator')
|
|
21
23
|
const Tracer = require('./transaction/tracer')
|
|
22
24
|
const TxSegmentNormalizer = require('./metrics/normalizer/tx_segment')
|
|
23
25
|
const uninstrumented = require('./uninstrumented')
|
|
24
26
|
const util = require('util')
|
|
25
27
|
|
|
26
|
-
const AGENT_RUN_BEHAVIOR = require('./collector/response').AGENT_RUN_BEHAVIOR
|
|
27
|
-
|
|
28
28
|
// Map of valid states to whether or not data collection is valid
|
|
29
29
|
const STATES = {
|
|
30
30
|
stopped: false,
|
|
@@ -37,8 +37,9 @@ const STATES = {
|
|
|
37
37
|
errored: false
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
const
|
|
40
|
+
const MAX_ERROR_TRACES_DEFAULT = 20
|
|
41
|
+
const INITIAL_HARVEST_DELAY_MS = 1000
|
|
42
|
+
const DEFAULT_HARVEST_INTERVAL_MS = 60000
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* There's a lot of stuff in this constructor, due to Agent acting as the
|
|
@@ -65,24 +66,94 @@ function Agent(config) {
|
|
|
65
66
|
this.collector = new CollectorAPI(this)
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.
|
|
69
|
+
this.mapper = new MetricMapper()
|
|
70
|
+
this.metricNameNormalizer = new MetricNormalizer(this.config, 'metric name')
|
|
71
|
+
|
|
72
|
+
this.metrics = new MetricAggregator(
|
|
73
|
+
{
|
|
74
|
+
periodMs: DEFAULT_HARVEST_INTERVAL_MS,
|
|
75
|
+
apdexT: this.config.apdex_t,
|
|
76
|
+
mapper: this.mapper,
|
|
77
|
+
normalizer: this.metricNameNormalizer
|
|
78
|
+
},
|
|
79
|
+
this.collector
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
this.metrics.on(
|
|
83
|
+
'starting metric_data data send.',
|
|
84
|
+
this._beforeMetricDataSend.bind(this)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
// Open tracing.
|
|
88
|
+
this.spanEventAggregator = new SpanEventAggregator({
|
|
89
|
+
periodMs: config.event_harvest_config.report_period_ms,
|
|
90
|
+
limit: config.event_harvest_config.harvest_limits.span_event_data
|
|
91
|
+
},
|
|
92
|
+
this.collector,
|
|
93
|
+
this.metrics)
|
|
94
|
+
|
|
95
|
+
this.transactionNameNormalizer = new MetricNormalizer(this.config, 'transaction name')
|
|
96
|
+
// Segment term based tx renaming for MGI mitigation.
|
|
97
|
+
this.txSegmentNormalizer = new TxSegmentNormalizer()
|
|
98
|
+
|
|
99
|
+
// User naming and ignoring rules.
|
|
100
|
+
this.urlNormalizer = new MetricNormalizer(this.config, 'URL')
|
|
101
|
+
this.userNormalizer = new MetricNormalizer(this.config, 'user')
|
|
102
|
+
this.userNormalizer.loadFromConfig()
|
|
103
|
+
|
|
104
|
+
this.transactionEventAggregator = new TransactionEventAggregator(
|
|
105
|
+
{
|
|
106
|
+
periodMs: config.event_harvest_config.report_period_ms,
|
|
107
|
+
limit: config.event_harvest_config.harvest_limits.analytic_event_data
|
|
108
|
+
},
|
|
109
|
+
this.collector,
|
|
110
|
+
this.metrics
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
this.customEventAggregator = new CustomEventAggregator(
|
|
114
|
+
{
|
|
115
|
+
periodMs: config.event_harvest_config.report_period_ms,
|
|
116
|
+
limit: config.event_harvest_config.harvest_limits.custom_event_data
|
|
117
|
+
},
|
|
118
|
+
this.collector,
|
|
119
|
+
this.metrics
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const errorTraceAggregator = new ErrorTraceAggregator(
|
|
123
|
+
{
|
|
124
|
+
periodMs: DEFAULT_HARVEST_INTERVAL_MS,
|
|
125
|
+
limit: MAX_ERROR_TRACES_DEFAULT
|
|
126
|
+
},
|
|
127
|
+
this.collector
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const errorEventAggregator = new ErrorEventAggregator(
|
|
131
|
+
{
|
|
132
|
+
periodMs: config.event_harvest_config.report_period_ms,
|
|
133
|
+
limit: config.event_harvest_config.harvest_limits.error_event_data
|
|
134
|
+
},
|
|
135
|
+
this.collector,
|
|
136
|
+
this.metrics
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
this.errors = new ErrorCollector(
|
|
140
|
+
config,
|
|
141
|
+
errorTraceAggregator,
|
|
142
|
+
errorEventAggregator,
|
|
143
|
+
this.metrics
|
|
144
|
+
)
|
|
82
145
|
|
|
83
146
|
// Transaction tracing.
|
|
84
147
|
this.tracer = new Tracer(this)
|
|
85
|
-
this.traces = new
|
|
148
|
+
this.traces = new TransactionTraceAggregator(
|
|
149
|
+
{
|
|
150
|
+
periodMs: DEFAULT_HARVEST_INTERVAL_MS,
|
|
151
|
+
config: this.config,
|
|
152
|
+
isAsync: !config.serverless_mode.enabled,
|
|
153
|
+
method: 'transaction_sample_data'
|
|
154
|
+
},
|
|
155
|
+
this.collector
|
|
156
|
+
)
|
|
86
157
|
this.transactionSampler = new AdaptiveSampler({
|
|
87
158
|
agent: this,
|
|
88
159
|
serverless: config.serverless_mode.enabled,
|
|
@@ -90,8 +161,15 @@ function Agent(config) {
|
|
|
90
161
|
target: config.sampling_target
|
|
91
162
|
})
|
|
92
163
|
|
|
93
|
-
|
|
94
|
-
|
|
164
|
+
this.queries = new QueryTraceAggregator(
|
|
165
|
+
{
|
|
166
|
+
config: this.config,
|
|
167
|
+
periodMs: DEFAULT_HARVEST_INTERVAL_MS,
|
|
168
|
+
method: 'sql_trace_data',
|
|
169
|
+
isAsync: !config.serverless_mode.enabled
|
|
170
|
+
},
|
|
171
|
+
this.collector
|
|
172
|
+
)
|
|
95
173
|
|
|
96
174
|
// Set up all the configuration events the agent needs to listen for.
|
|
97
175
|
this._listenForConfigChanges()
|
|
@@ -100,12 +178,8 @@ function Agent(config) {
|
|
|
100
178
|
this.totalActiveSegments = 0
|
|
101
179
|
this.segmentsCreatedInHarvest = 0
|
|
102
180
|
this.segmentsClearedInHarvest = 0
|
|
181
|
+
// Used by shutdown code as well as entity tracking stats
|
|
103
182
|
this.activeTransactions = 0
|
|
104
|
-
this.transactionsCreatedInHarvest = 0
|
|
105
|
-
|
|
106
|
-
// Harvest attributes.
|
|
107
|
-
this.harvesterHandle = null
|
|
108
|
-
this._lastHarvest = null
|
|
109
183
|
|
|
110
184
|
// Finally, add listeners for the agent's own events.
|
|
111
185
|
this.on('transactionFinished', this._transactionFinished.bind(this))
|
|
@@ -153,10 +227,9 @@ Agent.prototype.start = function start(callback) {
|
|
|
153
227
|
callback(new Error('Not starting without license key!'))
|
|
154
228
|
})
|
|
155
229
|
}
|
|
156
|
-
|
|
157
230
|
logger.info('Starting New Relic for Node.js connection process.')
|
|
158
231
|
|
|
159
|
-
this.collector.connect(function
|
|
232
|
+
this.collector.connect(function onStartConnect(error, response) {
|
|
160
233
|
if (error || response.shouldShutdownRun()) {
|
|
161
234
|
agent.setState('errored')
|
|
162
235
|
sampler.stop()
|
|
@@ -168,10 +241,12 @@ Agent.prototype.start = function start(callback) {
|
|
|
168
241
|
}
|
|
169
242
|
|
|
170
243
|
if (agent.collector.isConnected()) {
|
|
244
|
+
agent.onConnect()
|
|
171
245
|
agent.setState('started')
|
|
172
246
|
const config = response.payload
|
|
247
|
+
|
|
173
248
|
if (agent.config.no_immediate_harvest) {
|
|
174
|
-
agent.
|
|
249
|
+
agent.startAggregators()
|
|
175
250
|
callback(null, config)
|
|
176
251
|
} else {
|
|
177
252
|
// Harvest immediately for quicker data display, but after at least 1
|
|
@@ -180,10 +255,13 @@ Agent.prototype.start = function start(callback) {
|
|
|
180
255
|
// NOTE: this setTimeout is deliberately NOT unref'd due to it being
|
|
181
256
|
// the last step in the Agent startup process
|
|
182
257
|
setTimeout(function afterTimeout() {
|
|
183
|
-
|
|
184
|
-
|
|
258
|
+
logger.info(`Starting initial ${INITIAL_HARVEST_DELAY_MS}ms harvest.`)
|
|
259
|
+
|
|
260
|
+
agent.forceHarvestAll(function afterAllAggregatorsSend() {
|
|
261
|
+
agent.startAggregators()
|
|
262
|
+
callback(null, config)
|
|
185
263
|
})
|
|
186
|
-
},
|
|
264
|
+
}, INITIAL_HARVEST_DELAY_MS)
|
|
187
265
|
}
|
|
188
266
|
} else {
|
|
189
267
|
callback(new Error('Collector did not connect and did not error'))
|
|
@@ -191,6 +269,181 @@ Agent.prototype.start = function start(callback) {
|
|
|
191
269
|
})
|
|
192
270
|
}
|
|
193
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Forces all aggregators to send the data collected.
|
|
274
|
+
* @param {Function} callback The callback to invoke when all data types have been sent.
|
|
275
|
+
*/
|
|
276
|
+
Agent.prototype.forceHarvestAll = function forceHarvestAll(callback) {
|
|
277
|
+
const agent = this
|
|
278
|
+
const promises = []
|
|
279
|
+
|
|
280
|
+
const metricPromise = new Promise((resolve) => {
|
|
281
|
+
agent.metrics.once(
|
|
282
|
+
'finished metric_data data send.',
|
|
283
|
+
function onMetricsFinished() {
|
|
284
|
+
resolve()
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
agent.metrics.send()
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
promises.push(metricPromise)
|
|
291
|
+
|
|
292
|
+
// TODO: plumb config through to aggregators so they can do their own checking.
|
|
293
|
+
if (agent.config.distributed_tracing.enabled &&
|
|
294
|
+
agent.config.span_events.enabled) {
|
|
295
|
+
const spanPromise = new Promise((resolve) => {
|
|
296
|
+
agent.spanEventAggregator.once(
|
|
297
|
+
'finished span_event_data data send.',
|
|
298
|
+
function onSpansFinished() {
|
|
299
|
+
resolve()
|
|
300
|
+
}
|
|
301
|
+
)
|
|
302
|
+
agent.spanEventAggregator.send()
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
promises.push(spanPromise)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (agent.config.custom_insights_events.enabled) {
|
|
309
|
+
const customEventPromise = new Promise((resolve) => {
|
|
310
|
+
agent.customEventAggregator.once(
|
|
311
|
+
'finished custom_event_data data send.',
|
|
312
|
+
function onCustomEventsFinished() {
|
|
313
|
+
resolve()
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
agent.customEventAggregator.send()
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
promises.push(customEventPromise)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (agent.config.transaction_events.enabled) {
|
|
323
|
+
const transactionEventPromise = new Promise((resolve) => {
|
|
324
|
+
agent.transactionEventAggregator.once(
|
|
325
|
+
'finished analytic_event_data data send.',
|
|
326
|
+
function onTransactionEventsFinished() {
|
|
327
|
+
resolve()
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
agent.transactionEventAggregator.send()
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
promises.push(transactionEventPromise)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (agent.config.transaction_tracer.enabled && agent.config.collect_traces) {
|
|
337
|
+
const transactionTracePromise = new Promise((resolve) => {
|
|
338
|
+
agent.traces.once(
|
|
339
|
+
'finished transaction_sample_data data send.',
|
|
340
|
+
function onTracesFinished() {
|
|
341
|
+
resolve()
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
agent.traces.send()
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
promises.push(transactionTracePromise)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (agent.config.slow_sql.enabled) {
|
|
351
|
+
const sqlTracePromise = new Promise((resolve) => {
|
|
352
|
+
agent.queries.once(
|
|
353
|
+
'finished sql_trace_data data send.',
|
|
354
|
+
function onSqlTracesFinished() {
|
|
355
|
+
resolve()
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
agent.queries.send()
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
promises.push(sqlTracePromise)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const errorCollectorEnabled =
|
|
365
|
+
agent.config.error_collector && agent.config.error_collector.enabled
|
|
366
|
+
|
|
367
|
+
if (errorCollectorEnabled && agent.config.collect_errors) {
|
|
368
|
+
const errorTracePromise = new Promise((resolve) => {
|
|
369
|
+
agent.errors.traceAggregator.once(
|
|
370
|
+
'finished error_data data send.',
|
|
371
|
+
function onErrorTracesFinished() {
|
|
372
|
+
resolve()
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
agent.errors.traceAggregator.send()
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
promises.push(errorTracePromise)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (errorCollectorEnabled && agent.config.error_collector.capture_events) {
|
|
382
|
+
const errorEventPromise = new Promise((resolve) => {
|
|
383
|
+
agent.errors.eventAggregator.once(
|
|
384
|
+
'finished error_event_data data send.',
|
|
385
|
+
function onErrorEventsFinished() {
|
|
386
|
+
resolve()
|
|
387
|
+
}
|
|
388
|
+
)
|
|
389
|
+
agent.errors.eventAggregator.send()
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
promises.push(errorEventPromise)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
Promise.all(promises).then(() => {
|
|
396
|
+
// Get out of the promise so callback errors aren't treated as
|
|
397
|
+
// promise rejections.
|
|
398
|
+
setImmediate(callback)
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
Agent.prototype.stopAggregators = function stopAggregators() {
|
|
403
|
+
this.metrics.stop()
|
|
404
|
+
this.errors.stop()
|
|
405
|
+
this.traces.stop()
|
|
406
|
+
this.queries.stop()
|
|
407
|
+
this.spanEventAggregator.stop()
|
|
408
|
+
this.transactionEventAggregator.stop()
|
|
409
|
+
this.customEventAggregator.stop()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
Agent.prototype.startAggregators = function startAggregators() {
|
|
413
|
+
this.metrics.start()
|
|
414
|
+
this.errors.start()
|
|
415
|
+
if (this.config.transaction_tracer.enabled && this.config.collect_traces) {
|
|
416
|
+
this.traces.start()
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (this.config.slow_sql.enabled) {
|
|
420
|
+
this.queries.start()
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (this.config.distributed_tracing.enabled &&
|
|
424
|
+
this.config.span_events.enabled) {
|
|
425
|
+
this.spanEventAggregator.start()
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (this.config.transaction_events.enabled) {
|
|
429
|
+
this.transactionEventAggregator.start()
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (this.config.custom_insights_events.enabled) {
|
|
433
|
+
this.customEventAggregator.start()
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
Agent.prototype.onConnect = function onConnect() {
|
|
438
|
+
this.metrics.reconfigure(this.config)
|
|
439
|
+
this.errors.reconfigure(this.config)
|
|
440
|
+
this.traces.reconfigure(this.config)
|
|
441
|
+
this.queries.reconfigure(this.config)
|
|
442
|
+
this.spanEventAggregator.reconfigure(this.config)
|
|
443
|
+
this.transactionEventAggregator.reconfigure(this.config)
|
|
444
|
+
this.customEventAggregator.reconfigure(this.config)
|
|
445
|
+
}
|
|
446
|
+
|
|
194
447
|
/**
|
|
195
448
|
* Bypasses standard collector connection by immediately invoking the startup
|
|
196
449
|
* callback, after gathering local environment details.
|
|
@@ -218,7 +471,9 @@ Agent.prototype.stop = function stop(callback) {
|
|
|
218
471
|
const agent = this
|
|
219
472
|
|
|
220
473
|
this.setState('stopping')
|
|
221
|
-
|
|
474
|
+
|
|
475
|
+
this.stopAggregators()
|
|
476
|
+
|
|
222
477
|
sampler.stop()
|
|
223
478
|
|
|
224
479
|
if (this.collector.isConnected()) {
|
|
@@ -234,32 +489,24 @@ Agent.prototype.stop = function stop(callback) {
|
|
|
234
489
|
callback(error)
|
|
235
490
|
})
|
|
236
491
|
} else {
|
|
492
|
+
logger.trace('Collector was not connected, invoking callback.')
|
|
493
|
+
|
|
237
494
|
process.nextTick(callback)
|
|
238
495
|
}
|
|
239
496
|
}
|
|
240
497
|
|
|
241
498
|
/**
|
|
242
499
|
* Resets queries.
|
|
243
|
-
*
|
|
244
|
-
* @param {boolean} forceReset
|
|
245
|
-
* Flag signalling unconditional reset, sent during LASP application.
|
|
246
500
|
*/
|
|
247
|
-
Agent.prototype._resetQueries = function resetQueries(
|
|
248
|
-
|
|
249
|
-
this.queries = new QueryTracer(this.config)
|
|
250
|
-
}
|
|
501
|
+
Agent.prototype._resetQueries = function resetQueries() {
|
|
502
|
+
this.queries.clear()
|
|
251
503
|
}
|
|
252
504
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
*/
|
|
259
|
-
Agent.prototype._resetErrors = function resetErrors(forceReset) {
|
|
260
|
-
if (!this.errors || forceReset) {
|
|
261
|
-
this.errors = new ErrorAggregator(this.config)
|
|
262
|
-
}
|
|
505
|
+
Agent.prototype._resetErrors = function resetErrors() {
|
|
506
|
+
this.errors.clearAll()
|
|
507
|
+
|
|
508
|
+
// TODO: is this still necessary?
|
|
509
|
+
// Likely do more direct with new config
|
|
263
510
|
this.errors.reconfigure(this.config)
|
|
264
511
|
}
|
|
265
512
|
|
|
@@ -267,15 +514,7 @@ Agent.prototype._resetErrors = function resetErrors(forceReset) {
|
|
|
267
514
|
* Resets events.
|
|
268
515
|
*/
|
|
269
516
|
Agent.prototype._resetEvents = function resetEvents() {
|
|
270
|
-
|
|
271
|
-
this.events = new PriorityQueue()
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
this.events.setLimit(this.config.transaction_events.max_samples_per_minute)
|
|
275
|
-
|
|
276
|
-
if (!this.customEvents) {
|
|
277
|
-
this.customEvents = new PriorityQueue()
|
|
278
|
-
}
|
|
517
|
+
this.transactionEventAggregator.clear()
|
|
279
518
|
}
|
|
280
519
|
|
|
281
520
|
/**
|
|
@@ -284,105 +523,8 @@ Agent.prototype._resetEvents = function resetEvents() {
|
|
|
284
523
|
* @param {boolean} forceReset
|
|
285
524
|
* Flag signalling unconditional reset, sent during LASP application.
|
|
286
525
|
*/
|
|
287
|
-
Agent.prototype._resetCustomEvents = function resetCustomEvents(
|
|
288
|
-
|
|
289
|
-
this.customEvents = new PriorityQueue()
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
this.customEvents.setLimit(this.config.custom_insights_events.max_samples_stored)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Builds all of the sub-properties of the agent that rely on configurations.
|
|
297
|
-
*/
|
|
298
|
-
Agent.prototype.reset = function reset() {
|
|
299
|
-
// Insights events.
|
|
300
|
-
this._resetEvents()
|
|
301
|
-
this._resetCustomEvents()
|
|
302
|
-
|
|
303
|
-
// Error tracing.
|
|
304
|
-
this._resetErrors()
|
|
305
|
-
|
|
306
|
-
// Open tracing.
|
|
307
|
-
this.spans = new SpanAggregator()
|
|
308
|
-
|
|
309
|
-
// Metrics.
|
|
310
|
-
this.mapper = new MetricMapper()
|
|
311
|
-
this.metricNameNormalizer = new MetricNormalizer(this.config, 'metric name')
|
|
312
|
-
this.metrics = new Metrics(this.config.apdex_t, this.mapper, this.metricNameNormalizer)
|
|
313
|
-
|
|
314
|
-
// Transaction naming.
|
|
315
|
-
this.transactionNameNormalizer = new MetricNormalizer(this.config, 'transaction name')
|
|
316
|
-
this.urlNormalizer = new MetricNormalizer(this.config, 'URL')
|
|
317
|
-
|
|
318
|
-
// Segment term based tx renaming for MGI mitigation.
|
|
319
|
-
this.txSegmentNormalizer = new TxSegmentNormalizer()
|
|
320
|
-
|
|
321
|
-
// User naming and ignoring rules.
|
|
322
|
-
this.userNormalizer = new MetricNormalizer(this.config, 'user')
|
|
323
|
-
this.userNormalizer.loadFromConfig()
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* On agent startup, an interval timer is started that calls this method once
|
|
328
|
-
* a minute, which in turn invokes the pieces of the harvest cycle. It calls
|
|
329
|
-
* the various collector API methods in order, bailing out if one of them fails,
|
|
330
|
-
* to ensure that the agents don't pummel the collector if it's already
|
|
331
|
-
* struggling.
|
|
332
|
-
*/
|
|
333
|
-
Agent.prototype.harvest = function harvest(callback) {
|
|
334
|
-
if (!callback) {
|
|
335
|
-
throw new TypeError('callback required!')
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Generate metrics for this harvest and then check we are connected to the
|
|
339
|
-
// collector.
|
|
340
|
-
this._generateHarvestMetrics()
|
|
341
|
-
if (!this.collector.isConnected()) {
|
|
342
|
-
return setImmediate(function immediatelyError() {
|
|
343
|
-
callback(new Error('Not connected to New Relic!'))
|
|
344
|
-
})
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// We have a connection, create a new harvest.
|
|
348
|
-
this.emit('harvestStarted')
|
|
349
|
-
this._lastHarvest = new Harvest(this)
|
|
350
|
-
this._lastHarvest.prepare(Harvest.ALL_ENDPOINTS)
|
|
351
|
-
|
|
352
|
-
// Reset all our collections. The harvest has all the data it needs at this point.
|
|
353
|
-
this._resetHarvestables()
|
|
354
|
-
|
|
355
|
-
// Send the harvest!
|
|
356
|
-
const collector = this.collector
|
|
357
|
-
const agent = this
|
|
358
|
-
this._lastHarvest.send(function afterHarvest(err, agentRunAction) {
|
|
359
|
-
// Do we need to do anything to the agent run?
|
|
360
|
-
if (agentRunAction === AGENT_RUN_BEHAVIOR.SHUTDOWN) {
|
|
361
|
-
agent.emit('harvestFinished')
|
|
362
|
-
agent.stop(function afterStop(stopError) {
|
|
363
|
-
const shutdownError = stopError || err
|
|
364
|
-
callback(shutdownError)
|
|
365
|
-
})
|
|
366
|
-
} else if (agentRunAction === AGENT_RUN_BEHAVIOR.RESTART) {
|
|
367
|
-
collector.restart(function afterRestart(restartError) {
|
|
368
|
-
// TODO: What if preconnect/connect respond with shutdown here?
|
|
369
|
-
if (restartError) {
|
|
370
|
-
logger.warn('Failed to restart agent run after harvest')
|
|
371
|
-
callback(restartError)
|
|
372
|
-
} else {
|
|
373
|
-
_finish(err)
|
|
374
|
-
}
|
|
375
|
-
})
|
|
376
|
-
} else {
|
|
377
|
-
_finish(err)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function _finish(error) {
|
|
381
|
-
agent.emit('harvestFinished')
|
|
382
|
-
agent._scheduleHarvester(agent.config.data_report_period)
|
|
383
|
-
callback(error)
|
|
384
|
-
}
|
|
385
|
-
})
|
|
526
|
+
Agent.prototype._resetCustomEvents = function resetCustomEvents() {
|
|
527
|
+
this.customEventAggregator.clear()
|
|
386
528
|
}
|
|
387
529
|
|
|
388
530
|
/**
|
|
@@ -391,78 +533,57 @@ Agent.prototype.harvest = function harvest(callback) {
|
|
|
391
533
|
* NOTE: this doesn't currently work outside of serverless mode.
|
|
392
534
|
*/
|
|
393
535
|
Agent.prototype.harvestSync = function harvestSync() {
|
|
394
|
-
|
|
395
|
-
// collector.
|
|
396
|
-
this._generateHarvestMetrics()
|
|
536
|
+
logger.trace('Peparing to harvest.')
|
|
397
537
|
|
|
398
538
|
if (!this.collector.isConnected()) {
|
|
399
|
-
throw new Error('
|
|
539
|
+
throw new Error('Sync harvest not connected/enabled!')
|
|
400
540
|
}
|
|
401
541
|
|
|
402
542
|
// We have a connection, create a new harvest.
|
|
403
543
|
this.emit('harvestStarted')
|
|
404
|
-
|
|
405
|
-
this._lastHarvest.prepare(Harvest.ALL_ENDPOINTS)
|
|
406
|
-
|
|
407
|
-
// Reset all our collections. The harvest has all the data it needs at this point.
|
|
408
|
-
this._resetHarvestables()
|
|
544
|
+
logger.info('Harvest started.')
|
|
409
545
|
|
|
410
546
|
const collector = this.collector
|
|
411
547
|
const agent = this
|
|
412
548
|
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
549
|
+
// "Sends" data to the serverless collector collection
|
|
550
|
+
this.metrics.send()
|
|
551
|
+
this.errors.traceAggregator.send()
|
|
552
|
+
this.errors.eventAggregator.send()
|
|
553
|
+
this.traces.send()
|
|
554
|
+
this.queries.send()
|
|
555
|
+
this.spanEventAggregator.send()
|
|
556
|
+
this.transactionEventAggregator.send()
|
|
557
|
+
this.customEventAggregator.send()
|
|
558
|
+
|
|
559
|
+
// Write serverless output
|
|
416
560
|
collector.flushPayloadSync()
|
|
561
|
+
|
|
417
562
|
agent.emit('harvestFinished')
|
|
563
|
+
logger.info('Harvest finished.')
|
|
418
564
|
}
|
|
419
565
|
|
|
420
|
-
Agent.prototype.
|
|
566
|
+
Agent.prototype._beforeMetricDataSend = function _beforeMetricDataSend() {
|
|
567
|
+
this._generateEntityStatsAndClear()
|
|
568
|
+
|
|
569
|
+
// Send uninstrumented supportability metrics every metric harvest cycle
|
|
570
|
+
uninstrumented.createMetrics(this.metrics)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
Agent.prototype._generateEntityStatsAndClear = function _generateHarvestMetrics() {
|
|
421
574
|
// Note some information about the size of this harvest.
|
|
422
575
|
if (logger.traceEnabled()) {
|
|
423
576
|
logger.trace({
|
|
424
577
|
segmentTotal: this.totalActiveSegments,
|
|
425
578
|
harvestCreated: this.segmentsCreatedInHarvest,
|
|
426
579
|
harvestCleared: this.segmentsClearedInHarvest,
|
|
427
|
-
activeTransactions: this.activeTransactions
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}, 'Entity stats on harvest')
|
|
431
|
-
}
|
|
432
|
-
this.recordSupportability(
|
|
433
|
-
'Nodejs/Transactions/Created',
|
|
434
|
-
this.transactionsCreatedInHarvest
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
// Send uninstrumented supportability metrics every harvest cycle
|
|
438
|
-
uninstrumented.createMetrics(this.metrics)
|
|
580
|
+
activeTransactions: this.activeTransactions
|
|
581
|
+
}, 'Entity stats on metric harvest')
|
|
582
|
+
}
|
|
439
583
|
|
|
440
584
|
// Reset the counters.
|
|
441
585
|
this.segmentsCreatedInHarvest = 0
|
|
442
586
|
this.segmentsClearedInHarvest = 0
|
|
443
|
-
this.transactionsCreatedInHarvest = 0
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
Agent.prototype._resetHarvestables = function _resetHarvestables() {
|
|
447
|
-
// TODO: Make each aggregator able to compose its own payload and clean itself
|
|
448
|
-
// up. Then the Harvest class can just iterate over all aggregations without
|
|
449
|
-
// having to know bespoke reset information.
|
|
450
|
-
this.metrics = new Metrics(
|
|
451
|
-
this.config.apdex_t,
|
|
452
|
-
this.mapper,
|
|
453
|
-
this.metricNameNormalizer
|
|
454
|
-
)
|
|
455
|
-
this.events = new PriorityQueue(this.config.transaction_events.max_samples_per_minute)
|
|
456
|
-
|
|
457
|
-
this.customEvents = new PriorityQueue(
|
|
458
|
-
this.config.custom_insights_events.max_samples_stored
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
this.errors.clearEvents()
|
|
462
|
-
this.errors.clearErrors()
|
|
463
|
-
this.traces.reset()
|
|
464
|
-
this.queries = new QueryTracer(this.config)
|
|
465
|
-
this.spans.clearEvents()
|
|
466
587
|
}
|
|
467
588
|
|
|
468
589
|
/**
|
|
@@ -489,7 +610,7 @@ Agent.prototype.setState = function setState(newState) {
|
|
|
489
610
|
throw new TypeError('Invalid state ' + newState)
|
|
490
611
|
}
|
|
491
612
|
|
|
492
|
-
logger.
|
|
613
|
+
logger.info('Agent state changed from %s to %s.', this._state, newState)
|
|
493
614
|
this._state = newState
|
|
494
615
|
this.emit(this._state)
|
|
495
616
|
}
|
|
@@ -502,90 +623,6 @@ Agent.prototype.canCollectData = function canCollectData() {
|
|
|
502
623
|
return STATES[this._state]
|
|
503
624
|
}
|
|
504
625
|
|
|
505
|
-
/**
|
|
506
|
-
* Server-side configuration value.
|
|
507
|
-
*
|
|
508
|
-
* @param {number} apdexT Apdex tolerating value, in seconds.
|
|
509
|
-
*/
|
|
510
|
-
Agent.prototype._apdexTChange = function _apdexTChange(apdexT) {
|
|
511
|
-
logger.debug('Apdex tolerating value changed to %s.', apdexT)
|
|
512
|
-
this.metrics.apdexT = apdexT
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Server-side configuration value. When run, forces a harvest cycle
|
|
517
|
-
* so as to not cause the agent to go too long without reporting.
|
|
518
|
-
*
|
|
519
|
-
* @param {number} interval Time in seconds between harvest runs.
|
|
520
|
-
*/
|
|
521
|
-
Agent.prototype._harvesterIntervalChange = _harvesterIntervalChange
|
|
522
|
-
|
|
523
|
-
function _harvesterIntervalChange(interval, callback) {
|
|
524
|
-
const agent = this
|
|
525
|
-
|
|
526
|
-
// only change the setup if the harvester is currently running
|
|
527
|
-
if (this.harvesterHandle) {
|
|
528
|
-
// force a harvest now, to be safe
|
|
529
|
-
this.harvest(function onHarvest(error) {
|
|
530
|
-
agent._restartHarvester(interval)
|
|
531
|
-
if (callback) callback(error)
|
|
532
|
-
})
|
|
533
|
-
} else if (callback) {
|
|
534
|
-
process.nextTick(callback)
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Restart the harvest cycle timer.
|
|
540
|
-
*
|
|
541
|
-
* @param {number} harvestSeconds How many seconds between harvests.
|
|
542
|
-
*/
|
|
543
|
-
Agent.prototype._restartHarvester = function _restartHarvester(harvestSeconds) {
|
|
544
|
-
this._stopHarvester()
|
|
545
|
-
this._scheduleHarvester(harvestSeconds)
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Safely stop the harvest cycle timer.
|
|
550
|
-
*/
|
|
551
|
-
Agent.prototype._stopHarvester = function _stopHarvester() {
|
|
552
|
-
if (this.harvesterHandle) {
|
|
553
|
-
clearTimeout(this.harvesterHandle)
|
|
554
|
-
}
|
|
555
|
-
this._lastHarvest = null
|
|
556
|
-
this.harvesterHandle = null
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Safely start the harvest cycle timer, and ensure that the harvest
|
|
561
|
-
* cycle won't keep an application from exiting if nothing else is
|
|
562
|
-
* happening to keep it up.
|
|
563
|
-
*
|
|
564
|
-
* @param {number} harvestSeconds - How many seconds between harvests.
|
|
565
|
-
*/
|
|
566
|
-
Agent.prototype._scheduleHarvester = function _scheduleHarvester(harvestSeconds) {
|
|
567
|
-
const agent = this
|
|
568
|
-
let harvestDelay = harvestSeconds * TO_MILLIS
|
|
569
|
-
|
|
570
|
-
// If there was a previous harvest, we want to schedule the next one based on
|
|
571
|
-
// its start time.
|
|
572
|
-
if (this._lastHarvest && this._lastHarvest.startTime) {
|
|
573
|
-
const timeSinceHarvest = Date.now() - this._lastHarvest.startTime
|
|
574
|
-
harvestDelay = Math.max(0, harvestDelay - timeSinceHarvest)
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
this.harvesterHandle = setTimeout(function doHarvest() {
|
|
578
|
-
// Agent#harvest handles scheduling the next harvest and properly reacting to
|
|
579
|
-
// any errors or commands. All we need to do is note any errors it spits out.
|
|
580
|
-
agent.harvest(function harvestError(error) {
|
|
581
|
-
if (error) {
|
|
582
|
-
logger.warn(error, 'Error on submission to New Relic.')
|
|
583
|
-
}
|
|
584
|
-
})
|
|
585
|
-
}, harvestDelay)
|
|
586
|
-
this.harvesterHandle.unref()
|
|
587
|
-
}
|
|
588
|
-
|
|
589
626
|
/**
|
|
590
627
|
* `agent_enabled` changed. This will generally only happen because of a high
|
|
591
628
|
* security mode mismatch between the agent and the collector. This only
|
|
@@ -715,7 +752,7 @@ Agent.prototype._addEventFromTransaction = function _addEventFromTransaction(tx)
|
|
|
715
752
|
agentAttributes
|
|
716
753
|
]
|
|
717
754
|
|
|
718
|
-
this.
|
|
755
|
+
this.transactionEventAggregator.add(event, tx.priority || Math.random())
|
|
719
756
|
}
|
|
720
757
|
|
|
721
758
|
/**
|
|
@@ -734,8 +771,10 @@ Agent.prototype._transactionFinished = function _transactionFinished(transaction
|
|
|
734
771
|
if (transaction.forceIgnore === false) {
|
|
735
772
|
logger.debug('Explicitly not ignoring %s (%s).', transaction.name, transaction.id)
|
|
736
773
|
}
|
|
737
|
-
this.metrics.merge(transaction.metrics)
|
|
738
|
-
|
|
774
|
+
this.metrics.merge(transaction.metrics, false)
|
|
775
|
+
|
|
776
|
+
this.errors.onTransactionFinished(transaction)
|
|
777
|
+
|
|
739
778
|
this.traces.add(transaction)
|
|
740
779
|
|
|
741
780
|
const trace = transaction.trace
|
|
@@ -779,7 +818,6 @@ Agent.prototype.recordSupportability = function recordSupportability(name, value
|
|
|
779
818
|
|
|
780
819
|
Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
|
|
781
820
|
const self = this
|
|
782
|
-
this.config.on('apdex_t', this._apdexTChange.bind(this))
|
|
783
821
|
this.config.on('agent_enabled', this._enabledChange.bind(this))
|
|
784
822
|
this.config.on('change', this._configChange.bind(this))
|
|
785
823
|
this.config.on('metric_name_rules', function updateMetricNameNormalizer() {
|
|
@@ -803,12 +841,47 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
|
|
|
803
841
|
self.transactionSampler.samplingPeriod = period * 1000
|
|
804
842
|
}
|
|
805
843
|
)
|
|
806
|
-
this.config.on(
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
self.events.setLimit(maxSamples)
|
|
844
|
+
this.config.on('event_harvest_config', function onHarvestConfigReceived(harvestConfig) {
|
|
845
|
+
if (harvestConfig) {
|
|
846
|
+
generateEventHarvestSupportMetrics(self, harvestConfig)
|
|
810
847
|
}
|
|
811
|
-
)
|
|
848
|
+
})
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function generateEventHarvestSupportMetrics(agent, harvestConfig) {
|
|
852
|
+
const harvestLimits = harvestConfig.harvest_limits
|
|
853
|
+
|
|
854
|
+
const harvestNames = NAMES.EVENT_HARVEST
|
|
855
|
+
const harvestLimitNames = harvestNames.HARVEST_LIMIT
|
|
856
|
+
|
|
857
|
+
const reportPeriodMetric = agent.metrics.getOrCreateMetric(harvestNames.REPORT_PERIOD)
|
|
858
|
+
reportPeriodMetric.recordValue(harvestConfig.report_period_ms)
|
|
859
|
+
|
|
860
|
+
const analyticLimit = harvestLimits.analytic_event_data
|
|
861
|
+
if (analyticLimit) {
|
|
862
|
+
const analyticLimitMetric = agent.metrics.getOrCreateMetric(
|
|
863
|
+
harvestLimitNames.ANALYTIC
|
|
864
|
+
)
|
|
865
|
+
analyticLimitMetric.recordValue(analyticLimit)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const customLimit = harvestLimits.custom_event_data
|
|
869
|
+
if (customLimit) {
|
|
870
|
+
const customLimitMetric = agent.metrics.getOrCreateMetric(harvestLimitNames.CUSTOM)
|
|
871
|
+
customLimitMetric.recordValue(customLimit)
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const errorLimit = harvestLimits.error_event_data
|
|
875
|
+
if (errorLimit) {
|
|
876
|
+
const errorLimitMetric = agent.metrics.getOrCreateMetric(harvestLimitNames.ERROR)
|
|
877
|
+
errorLimitMetric.recordValue(errorLimit)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const spanLimit = harvestLimits.span_event_data
|
|
881
|
+
if (spanLimit) {
|
|
882
|
+
const spanLimitMetric = agent.metrics.getOrCreateMetric(harvestLimitNames.SPAN)
|
|
883
|
+
spanLimitMetric.recordValue(spanLimit)
|
|
884
|
+
}
|
|
812
885
|
}
|
|
813
886
|
|
|
814
887
|
module.exports = Agent
|