newrelic 4.10.0 → 4.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/.eslintrc +2 -0
- package/.npmignore +4 -0
- package/NEWS.md +89 -0
- package/api.js +88 -47
- package/bin/travis-setup.sh +14 -6
- package/index.js +12 -10
- package/lib/adaptive-sampler.js +28 -5
- package/lib/agent.js +199 -77
- package/lib/collector/api.js +228 -178
- package/lib/collector/facts.js +29 -9
- package/lib/collector/parse-response.js +24 -76
- package/lib/collector/remote-method.js +102 -72
- package/lib/collector/response.js +61 -0
- package/lib/collector/serverless.js +205 -0
- package/lib/config/attribute-filter.js +3 -1
- package/lib/config/default.js +14 -3
- package/lib/config/env.js +11 -3
- package/lib/config/index.js +103 -56
- package/lib/db/tracer.js +7 -0
- package/lib/errors/aggregator.js +9 -2
- package/lib/errors/index.js +6 -6
- package/lib/feature_flags.js +2 -0
- package/lib/harvest.js +526 -444
- package/lib/header-attributes.js +152 -0
- package/lib/instrumentation/amqplib.js +4 -3
- package/lib/instrumentation/aws-sdk.js +25 -0
- package/lib/instrumentation/core/http-outbound.js +12 -4
- package/lib/instrumentation/core/http.js +20 -126
- package/lib/instrumentation/restify.js +23 -4
- package/lib/instrumentations.js +1 -0
- package/lib/metrics/names.js +7 -6
- package/lib/metrics/normalizer.js +1 -2
- package/lib/metrics/recorders/http.js +4 -4
- package/lib/serverless/api-gateway.js +62 -0
- package/lib/serverless/aws-lambda.js +273 -0
- package/lib/serverless/config.js +0 -0
- package/lib/shim/constants.js +13 -1
- package/lib/shim/shim.js +16 -10
- package/lib/shim/specs/index.js +2 -2
- package/lib/shim/webframework-shim.js +7 -3
- package/lib/shimmer.js +15 -8
- package/lib/spans/aggregator.js +2 -2
- package/lib/system-info.js +15 -14
- package/lib/timer.js +7 -3
- package/lib/transaction/index.js +27 -5
- package/lib/transaction/trace/attributes.js +1 -4
- package/lib/util/label-parser.js +1 -1
- package/package-lock.json +6572 -0
- package/package.json +6 -13
- package/stub_api.js +6 -0
- package/lib/collector/constants.js +0 -10
- package/lib/metrics/recorders/cassandra.js +0 -40
- package/lib/metrics/recorders/express.js +0 -15
- package/lib/metrics/recorders/memcached.js +0 -48
- package/lib/metrics/recorders/redis.js +0 -48
package/lib/agent.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const AdaptiveSampler = require('./adaptive-sampler')
|
|
4
4
|
const CollectorAPI = require('./collector/api')
|
|
5
|
+
const ServerlessCollector = require('./collector/serverless')
|
|
5
6
|
const DESTINATIONS = require('./config/attribute-filter').DESTINATIONS
|
|
6
7
|
const ErrorAggregator = require('./errors/aggregator')
|
|
7
8
|
const EventEmitter = require('events').EventEmitter
|
|
@@ -22,9 +23,11 @@ const TxSegmentNormalizer = require('./metrics/normalizer/tx_segment')
|
|
|
22
23
|
const uninstrumented = require('./uninstrumented')
|
|
23
24
|
const util = require('util')
|
|
24
25
|
|
|
26
|
+
const AGENT_RUN_BEHAVIOR = require('./collector/response').AGENT_RUN_BEHAVIOR
|
|
25
27
|
const STATES = [
|
|
26
28
|
'stopped', // start state
|
|
27
|
-
'starting', //
|
|
29
|
+
'starting', // starting agent
|
|
30
|
+
'connecting', // handshaking with NR
|
|
28
31
|
'connected', // connected to collector
|
|
29
32
|
'disconnected', // disconnected from collector
|
|
30
33
|
'started', // up and running
|
|
@@ -34,6 +37,7 @@ const STATES = [
|
|
|
34
37
|
|
|
35
38
|
// just to make clear what's going on
|
|
36
39
|
const TO_MILLIS = 1e3
|
|
40
|
+
const SERVERLESS_SAMPLING_LIMIT = Infinity
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* There's a lot of stuff in this constructor, due to Agent acting as the
|
|
@@ -53,7 +57,12 @@ function Agent(config) {
|
|
|
53
57
|
this.config = config
|
|
54
58
|
this.environment = require('./environment')
|
|
55
59
|
this.version = this.config.version
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
if (config.serverless_mode.enabled) {
|
|
62
|
+
this.collector = new ServerlessCollector(this)
|
|
63
|
+
} else {
|
|
64
|
+
this.collector = new CollectorAPI(this)
|
|
65
|
+
}
|
|
57
66
|
|
|
58
67
|
// Reset the agent to add all the sub-objects it needs. These object are the
|
|
59
68
|
// ones that get re-created if the agent is told to restart from the collector.
|
|
@@ -74,6 +83,8 @@ function Agent(config) {
|
|
|
74
83
|
this.tracer = new Tracer(this)
|
|
75
84
|
this.traces = new TraceAggregator(this.config)
|
|
76
85
|
this.transactionSampler = new AdaptiveSampler({
|
|
86
|
+
agent: this,
|
|
87
|
+
serverless: config.serverless_mode.enabled,
|
|
77
88
|
period: config.sampling_target_period_in_seconds * 1000,
|
|
78
89
|
target: config.sampling_target
|
|
79
90
|
})
|
|
@@ -112,7 +123,7 @@ util.inherits(Agent, EventEmitter)
|
|
|
112
123
|
Agent.prototype.start = function start(callback) {
|
|
113
124
|
if (!callback) throw new TypeError('callback required!')
|
|
114
125
|
|
|
115
|
-
|
|
126
|
+
const agent = this
|
|
116
127
|
|
|
117
128
|
this.setState('starting')
|
|
118
129
|
|
|
@@ -124,50 +135,75 @@ Agent.prototype.start = function start(callback) {
|
|
|
124
135
|
return process.nextTick(callback)
|
|
125
136
|
}
|
|
126
137
|
|
|
127
|
-
|
|
138
|
+
sampler.start(agent)
|
|
139
|
+
|
|
140
|
+
if (this.config.serverless_mode.enabled) {
|
|
141
|
+
return this._serverlessModeStart(callback)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!this.config.license_key) {
|
|
128
145
|
logger.error('A valid account license key cannot be found. ' +
|
|
129
146
|
'Has a license key been specified in the agent configuration ' +
|
|
130
147
|
'file or via the NEW_RELIC_LICENSE_KEY environment variable?')
|
|
131
148
|
|
|
132
149
|
this.setState('errored')
|
|
150
|
+
sampler.stop()
|
|
133
151
|
return process.nextTick(function onNextTick() {
|
|
134
152
|
callback(new Error('Not starting without license key!'))
|
|
135
153
|
})
|
|
136
154
|
}
|
|
137
155
|
|
|
138
|
-
sampler.start(agent)
|
|
139
|
-
|
|
140
156
|
logger.info('Starting New Relic for Node.js connection process.')
|
|
141
157
|
|
|
142
|
-
this.collector.connect(function onConnect(error,
|
|
143
|
-
if (error) {
|
|
158
|
+
this.collector.connect(function onConnect(error, response) {
|
|
159
|
+
if (error || response.shouldShutdownRun()) {
|
|
144
160
|
agent.setState('errored')
|
|
145
161
|
sampler.stop()
|
|
146
|
-
|
|
162
|
+
callback(
|
|
163
|
+
error || new Error('Failed to connect to collector'),
|
|
164
|
+
response && response.payload
|
|
165
|
+
)
|
|
166
|
+
return
|
|
147
167
|
}
|
|
148
168
|
|
|
149
|
-
if (agent.collector.isConnected()
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// the last step in the Agent startup process
|
|
155
|
-
setTimeout(function afterTimeout() {
|
|
156
|
-
agent.harvest(function onHarvest(harvestError) {
|
|
157
|
-
agent._startHarvester(agent.config.data_report_period)
|
|
158
|
-
|
|
159
|
-
agent.setState('started')
|
|
160
|
-
callback(harvestError, config)
|
|
161
|
-
})
|
|
162
|
-
}, 1000)
|
|
163
|
-
} else {
|
|
164
|
-
process.nextTick(function onNextTick() {
|
|
169
|
+
if (agent.collector.isConnected()) {
|
|
170
|
+
agent.setState('started')
|
|
171
|
+
const config = response.payload
|
|
172
|
+
if (agent.config.no_immediate_harvest) {
|
|
173
|
+
agent._scheduleHarvester(agent.config.data_report_period)
|
|
165
174
|
callback(null, config)
|
|
166
|
-
}
|
|
175
|
+
} else {
|
|
176
|
+
// Harvest immediately for quicker data display, but after at least 1
|
|
177
|
+
// second or the collector will throw away the data.
|
|
178
|
+
//
|
|
179
|
+
// NOTE: this setTimeout is deliberately NOT unref'd due to it being
|
|
180
|
+
// the last step in the Agent startup process
|
|
181
|
+
setTimeout(function afterTimeout() {
|
|
182
|
+
agent.harvest(function onHarvest(harvestError) {
|
|
183
|
+
callback(harvestError, config)
|
|
184
|
+
})
|
|
185
|
+
}, 1000)
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
callback(new Error('Collector did not connect and did not error'))
|
|
167
189
|
}
|
|
168
190
|
})
|
|
169
191
|
}
|
|
170
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Bypasses standard collector connection by immediately invoking the startup
|
|
195
|
+
* callback, after gathering local environment details.
|
|
196
|
+
*
|
|
197
|
+
* @param {Function} callback
|
|
198
|
+
*/
|
|
199
|
+
Agent.prototype._serverlessModeStart = function _serverlessModeStart(callback) {
|
|
200
|
+
logger.info(
|
|
201
|
+
'New Relic for Node.js starting in serverless mode -- skipping connection process.'
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
setImmediate(() => callback(null, this.config))
|
|
205
|
+
}
|
|
206
|
+
|
|
171
207
|
/**
|
|
172
208
|
* Any memory claimed by the agent will be retained after stopping.
|
|
173
209
|
*
|
|
@@ -178,7 +214,7 @@ Agent.prototype.start = function start(callback) {
|
|
|
178
214
|
Agent.prototype.stop = function stop(callback) {
|
|
179
215
|
if (!callback) throw new TypeError('callback required!')
|
|
180
216
|
|
|
181
|
-
|
|
217
|
+
const agent = this
|
|
182
218
|
|
|
183
219
|
this.setState('stopping')
|
|
184
220
|
this._stopHarvester()
|
|
@@ -233,7 +269,13 @@ Agent.prototype._resetEvents = function resetEvents() {
|
|
|
233
269
|
if (!this.events) {
|
|
234
270
|
this.events = new PriorityQueue()
|
|
235
271
|
}
|
|
236
|
-
|
|
272
|
+
|
|
273
|
+
this.events.setLimit(
|
|
274
|
+
this.config.serverless_mode.enabled
|
|
275
|
+
? SERVERLESS_SAMPLING_LIMIT
|
|
276
|
+
: this.config.transaction_events.max_samples_per_minute
|
|
277
|
+
)
|
|
278
|
+
|
|
237
279
|
if (!this.customEvents) {
|
|
238
280
|
this.customEvents = new PriorityQueue()
|
|
239
281
|
}
|
|
@@ -249,7 +291,12 @@ Agent.prototype._resetCustomEvents = function resetCustomEvents(forceReset) {
|
|
|
249
291
|
if (!this.customEvents || forceReset) {
|
|
250
292
|
this.customEvents = new PriorityQueue()
|
|
251
293
|
}
|
|
252
|
-
|
|
294
|
+
|
|
295
|
+
this.customEvents.setLimit(
|
|
296
|
+
this.config.serverless_mode.enabled
|
|
297
|
+
? SERVERLESS_SAMPLING_LIMIT
|
|
298
|
+
: this.config.custom_insights_events.max_samples_stored
|
|
299
|
+
)
|
|
253
300
|
}
|
|
254
301
|
|
|
255
302
|
/**
|
|
@@ -264,7 +311,11 @@ Agent.prototype.reset = function reset() {
|
|
|
264
311
|
this._resetErrors()
|
|
265
312
|
|
|
266
313
|
// Open tracing.
|
|
267
|
-
this.spans = new SpanAggregator(
|
|
314
|
+
this.spans = new SpanAggregator(
|
|
315
|
+
this.config.serverless_mode.enabled
|
|
316
|
+
? SERVERLESS_SAMPLING_LIMIT
|
|
317
|
+
: null
|
|
318
|
+
)
|
|
268
319
|
|
|
269
320
|
// Metrics.
|
|
270
321
|
this.mapper = new MetricMapper()
|
|
@@ -305,31 +356,54 @@ Agent.prototype.harvest = function harvest(callback) {
|
|
|
305
356
|
}
|
|
306
357
|
|
|
307
358
|
// We have a connection, create a new harvest.
|
|
359
|
+
this.emit('harvestStarted')
|
|
308
360
|
this._lastHarvest = new Harvest(this)
|
|
361
|
+
this._lastHarvest.prepare(Harvest.ALL_ENDPOINTS)
|
|
309
362
|
|
|
310
363
|
// Reset all our collections. The harvest has all the data it needs at this point.
|
|
311
|
-
|
|
312
|
-
// up. Then the Harvest class can just iterate over all aggregations without
|
|
313
|
-
// having to know bespoke reset information.
|
|
314
|
-
this.metrics = new Metrics(
|
|
315
|
-
this.config.apdex_t,
|
|
316
|
-
this.mapper,
|
|
317
|
-
this.metricNameNormalizer
|
|
318
|
-
)
|
|
319
|
-
this.events = new PriorityQueue(
|
|
320
|
-
this.config.transaction_events.max_samples_per_minute
|
|
321
|
-
)
|
|
322
|
-
this.customEvents = new PriorityQueue(
|
|
323
|
-
this.config.custom_insights_events.max_samples_stored
|
|
324
|
-
)
|
|
325
|
-
this.errors.clearEvents()
|
|
326
|
-
this.errors.clearErrors()
|
|
327
|
-
this.traces.reset()
|
|
328
|
-
this.queries = new QueryTracer(this.config)
|
|
329
|
-
this.spans.clearEvents()
|
|
364
|
+
this._resetHarvestables()
|
|
330
365
|
|
|
331
366
|
// Send the harvest!
|
|
332
|
-
this.
|
|
367
|
+
const collector = this.collector
|
|
368
|
+
const agent = this
|
|
369
|
+
this._lastHarvest.send(function afterHarvest(err, agentRunAction) {
|
|
370
|
+
// The serverless collector will never tell us anything interesting to do,
|
|
371
|
+
// but has an awkward, final step that the normal collector does not.
|
|
372
|
+
if (collector instanceof ServerlessCollector) {
|
|
373
|
+
return collector.flushPayload(function afterFlush(flushError) {
|
|
374
|
+
agent.emit('harvestFinished')
|
|
375
|
+
const serverlessError = flushError || err
|
|
376
|
+
callback(serverlessError)
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Do we need to do anything to the agent run?
|
|
381
|
+
if (agentRunAction === AGENT_RUN_BEHAVIOR.SHUTDOWN) {
|
|
382
|
+
agent.emit('harvestFinished')
|
|
383
|
+
agent.stop(function afterStop(stopError) {
|
|
384
|
+
const shutdownError = stopError || err
|
|
385
|
+
callback(shutdownError)
|
|
386
|
+
})
|
|
387
|
+
} else if (agentRunAction === AGENT_RUN_BEHAVIOR.RESTART) {
|
|
388
|
+
collector.restart(function afterRestart(restartError) {
|
|
389
|
+
// TODO: What if preconnect/connect respond with shutdown here?
|
|
390
|
+
if (restartError) {
|
|
391
|
+
logger.warn('Failed to restart agent run after harvest')
|
|
392
|
+
callback(restartError)
|
|
393
|
+
} else {
|
|
394
|
+
_finish(err)
|
|
395
|
+
}
|
|
396
|
+
})
|
|
397
|
+
} else {
|
|
398
|
+
_finish(err)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function _finish(error) {
|
|
402
|
+
agent.emit('harvestFinished')
|
|
403
|
+
agent._scheduleHarvester(agent.config.data_report_period)
|
|
404
|
+
callback(error)
|
|
405
|
+
}
|
|
406
|
+
})
|
|
333
407
|
}
|
|
334
408
|
|
|
335
409
|
Agent.prototype._generateHarvestMetrics = function _generateHarvestMetrics() {
|
|
@@ -358,6 +432,32 @@ Agent.prototype._generateHarvestMetrics = function _generateHarvestMetrics() {
|
|
|
358
432
|
this.transactionsCreatedInHarvest = 0
|
|
359
433
|
}
|
|
360
434
|
|
|
435
|
+
Agent.prototype._resetHarvestables = function _resetHarvestables() {
|
|
436
|
+
// TODO: Make each aggregator able to compose its own payload and clean itself
|
|
437
|
+
// up. Then the Harvest class can just iterate over all aggregations without
|
|
438
|
+
// having to know bespoke reset information.
|
|
439
|
+
this.metrics = new Metrics(
|
|
440
|
+
this.config.apdex_t,
|
|
441
|
+
this.mapper,
|
|
442
|
+
this.metricNameNormalizer
|
|
443
|
+
)
|
|
444
|
+
this.events = new PriorityQueue(
|
|
445
|
+
this.config.serverless_mode.enabled
|
|
446
|
+
? SERVERLESS_SAMPLING_LIMIT
|
|
447
|
+
: this.config.transaction_events.max_samples_per_minute
|
|
448
|
+
)
|
|
449
|
+
this.customEvents = new PriorityQueue(
|
|
450
|
+
this.config.serverless_mode.enabled
|
|
451
|
+
? SERVERLESS_SAMPLING_LIMIT
|
|
452
|
+
: this.config.custom_insights_events.max_samples_stored
|
|
453
|
+
)
|
|
454
|
+
this.errors.clearEvents()
|
|
455
|
+
this.errors.clearErrors()
|
|
456
|
+
this.traces.reset()
|
|
457
|
+
this.queries = new QueryTracer(this.config)
|
|
458
|
+
this.spans.clearEvents()
|
|
459
|
+
}
|
|
460
|
+
|
|
361
461
|
/**
|
|
362
462
|
* Public interface for passing configuration data from the collector
|
|
363
463
|
* on to the configuration, in an effort to keep them at least somewhat
|
|
@@ -407,7 +507,7 @@ Agent.prototype._apdexTChange = function _apdexTChange(apdexT) {
|
|
|
407
507
|
Agent.prototype._harvesterIntervalChange = _harvesterIntervalChange
|
|
408
508
|
|
|
409
509
|
function _harvesterIntervalChange(interval, callback) {
|
|
410
|
-
|
|
510
|
+
const agent = this
|
|
411
511
|
|
|
412
512
|
// only change the setup if the harvester is currently running
|
|
413
513
|
if (this.harvesterHandle) {
|
|
@@ -428,14 +528,17 @@ function _harvesterIntervalChange(interval, callback) {
|
|
|
428
528
|
*/
|
|
429
529
|
Agent.prototype._restartHarvester = function _restartHarvester(harvestSeconds) {
|
|
430
530
|
this._stopHarvester()
|
|
431
|
-
this.
|
|
531
|
+
this._scheduleHarvester(harvestSeconds)
|
|
432
532
|
}
|
|
433
533
|
|
|
434
534
|
/**
|
|
435
535
|
* Safely stop the harvest cycle timer.
|
|
436
536
|
*/
|
|
437
537
|
Agent.prototype._stopHarvester = function _stopHarvester() {
|
|
438
|
-
if (this.harvesterHandle)
|
|
538
|
+
if (this.harvesterHandle) {
|
|
539
|
+
clearTimeout(this.harvesterHandle)
|
|
540
|
+
}
|
|
541
|
+
this._lastHarvest = null
|
|
439
542
|
this.harvesterHandle = null
|
|
440
543
|
}
|
|
441
544
|
|
|
@@ -444,22 +547,28 @@ Agent.prototype._stopHarvester = function _stopHarvester() {
|
|
|
444
547
|
* cycle won't keep an application from exiting if nothing else is
|
|
445
548
|
* happening to keep it up.
|
|
446
549
|
*
|
|
447
|
-
* @param {number} harvestSeconds How many seconds between harvests.
|
|
550
|
+
* @param {number} harvestSeconds - How many seconds between harvests.
|
|
448
551
|
*/
|
|
449
|
-
Agent.prototype.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
552
|
+
Agent.prototype._scheduleHarvester = function _scheduleHarvester(harvestSeconds) {
|
|
553
|
+
const agent = this
|
|
554
|
+
let harvestDelay = harvestSeconds * TO_MILLIS
|
|
555
|
+
|
|
556
|
+
// If there was a previous harvest, we want to schedule the next one based on
|
|
557
|
+
// its start time.
|
|
558
|
+
if (this._lastHarvest && this._lastHarvest.startTime) {
|
|
559
|
+
const timeSinceHarvest = Date.now() - this._lastHarvest.startTime
|
|
560
|
+
harvestDelay = Math.max(0, harvestDelay - timeSinceHarvest)
|
|
456
561
|
}
|
|
457
562
|
|
|
458
|
-
function
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
563
|
+
this.harvesterHandle = setTimeout(function doHarvest() {
|
|
564
|
+
// Agent#harvest handles scheduling the next harvest and properly reacting to
|
|
565
|
+
// any errors or commands. All we need to do is note any errors it spits out.
|
|
566
|
+
agent.harvest(function harvestError(error) {
|
|
567
|
+
if (error) {
|
|
568
|
+
logger.warn(error, 'Error on submission to New Relic.')
|
|
569
|
+
}
|
|
570
|
+
})
|
|
571
|
+
}, harvestDelay)
|
|
463
572
|
this.harvesterHandle.unref()
|
|
464
573
|
}
|
|
465
574
|
|
|
@@ -487,17 +596,17 @@ Agent.prototype._configChange = function _configChange() {
|
|
|
487
596
|
Agent.prototype._addIntrinsicAttrsFromTransaction = _addIntrinsicAttrsFromTransaction
|
|
488
597
|
|
|
489
598
|
function _addIntrinsicAttrsFromTransaction(transaction) {
|
|
490
|
-
|
|
599
|
+
const intrinsicAttributes = {
|
|
491
600
|
webDuration: transaction.timer.getDurationInMillis() / 1000,
|
|
492
601
|
timestamp: transaction.timer.start,
|
|
493
602
|
name: transaction.getFullName(),
|
|
494
603
|
duration: transaction.timer.getDurationInMillis() / 1000,
|
|
495
|
-
totalTime: transaction.trace.getTotalTimeDurationInMillis(),
|
|
604
|
+
totalTime: transaction.trace.getTotalTimeDurationInMillis() / 1000,
|
|
496
605
|
type: 'Transaction',
|
|
497
606
|
error: transaction.hasErrors()
|
|
498
607
|
}
|
|
499
608
|
|
|
500
|
-
|
|
609
|
+
let metric = transaction.metrics.getMetric(NAMES.QUEUETIME)
|
|
501
610
|
if (metric) {
|
|
502
611
|
intrinsicAttributes.queueDuration = metric.total
|
|
503
612
|
}
|
|
@@ -582,11 +691,11 @@ function calculateApdexZone(duration, apdexT) {
|
|
|
582
691
|
Agent.prototype._addEventFromTransaction = function _addEventFromTransaction(tx) {
|
|
583
692
|
if (!this.config.transaction_events.enabled) return
|
|
584
693
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
694
|
+
const intrinsicAttributes = this._addIntrinsicAttrsFromTransaction(tx)
|
|
695
|
+
const userAttributes = tx.trace.custom.get(DESTINATIONS.TRANS_EVENT)
|
|
696
|
+
const agentAttributes = tx.trace.attributes.get(DESTINATIONS.TRANS_EVENT)
|
|
588
697
|
|
|
589
|
-
|
|
698
|
+
const event = [
|
|
590
699
|
intrinsicAttributes,
|
|
591
700
|
userAttributes,
|
|
592
701
|
agentAttributes
|
|
@@ -615,7 +724,7 @@ Agent.prototype._transactionFinished = function _transactionFinished(transaction
|
|
|
615
724
|
this.errors.onTransactionFinished(transaction, this.metrics)
|
|
616
725
|
this.traces.add(transaction)
|
|
617
726
|
|
|
618
|
-
|
|
727
|
+
const trace = transaction.trace
|
|
619
728
|
trace.intrinsics = transaction.getIntrinsicAttributes()
|
|
620
729
|
|
|
621
730
|
this._addEventFromTransaction(transaction)
|
|
@@ -628,6 +737,20 @@ Agent.prototype._transactionFinished = function _transactionFinished(transaction
|
|
|
628
737
|
--this.activeTransactions
|
|
629
738
|
this.totalActiveSegments -= transaction.numSegments
|
|
630
739
|
this.segmentsClearedInHarvest += transaction.numSegments
|
|
740
|
+
|
|
741
|
+
if (this.config.serverless_mode.enabled) {
|
|
742
|
+
this.harvest(function onServerlessHarvest(err) {
|
|
743
|
+
if (err) {
|
|
744
|
+
logger.error('Serverless mode harvest error', err)
|
|
745
|
+
}
|
|
746
|
+
})
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
Agent.prototype.setLambdaArn = function setLambdaArn(arn) {
|
|
751
|
+
if (this.collector instanceof ServerlessCollector) {
|
|
752
|
+
this.collector.setLambdaArn(arn)
|
|
753
|
+
}
|
|
631
754
|
}
|
|
632
755
|
|
|
633
756
|
/**
|
|
@@ -640,7 +763,7 @@ Agent.prototype.getTransaction = function getTransaction() {
|
|
|
640
763
|
}
|
|
641
764
|
|
|
642
765
|
Agent.prototype.recordSupportability = function recordSupportability(name, value) {
|
|
643
|
-
|
|
766
|
+
const metric = this.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.PREFIX + name)
|
|
644
767
|
if (value != null) {
|
|
645
768
|
metric.recordValue(value)
|
|
646
769
|
} else {
|
|
@@ -649,9 +772,8 @@ Agent.prototype.recordSupportability = function recordSupportability(name, value
|
|
|
649
772
|
}
|
|
650
773
|
|
|
651
774
|
Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
|
|
652
|
-
|
|
775
|
+
const self = this
|
|
653
776
|
this.config.on('apdex_t', this._apdexTChange.bind(this))
|
|
654
|
-
this.config.on('data_report_period', this._harvesterIntervalChange.bind(this))
|
|
655
777
|
this.config.on('agent_enabled', this._enabledChange.bind(this))
|
|
656
778
|
this.config.on('change', this._configChange.bind(this))
|
|
657
779
|
this.config.on('metric_name_rules', function updateMetricNameNormalizer() {
|