newrelic 5.13.0 → 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/NEWS.md +49 -0
- package/api.js +3 -2
- package/lib/agent.js +148 -315
- package/lib/aggregators/base-aggregator.js +3 -12
- package/lib/collector/api.js +2 -1
- package/lib/config/default.js +7 -0
- package/lib/config/env.js +6 -0
- package/lib/config/index.js +5 -5
- package/lib/injector.js +161 -0
- package/lib/shimmer.js +116 -0
- package/lib/spans/span-event-aggregator.js +1 -1
- package/lib/transaction/index.js +0 -1
- package/lib/utilization/common.js +11 -4
- package/package.json +8 -6
- package/lib/event-aggregator.js +0 -94
- package/lib/harvest.js +0 -718
package/lib/collector/api.js
CHANGED
|
@@ -469,7 +469,8 @@ CollectorAPI.prototype.restart = function restart(callback) {
|
|
|
469
469
|
CollectorAPI.prototype._runLifecycle = function _runLifecycle(method, body, callback) {
|
|
470
470
|
if (!this.isConnected()) {
|
|
471
471
|
logger.warn('Not connected to New Relic. Not calling.', method.name)
|
|
472
|
-
|
|
472
|
+
const error = new Error('Not connected to collector.', null, null)
|
|
473
|
+
return setImmediate(callback, error)
|
|
473
474
|
}
|
|
474
475
|
|
|
475
476
|
const api = this
|
package/lib/config/default.js
CHANGED
|
@@ -179,6 +179,13 @@ exports.config = () => ({
|
|
|
179
179
|
filter_cache_limit: 1000
|
|
180
180
|
},
|
|
181
181
|
|
|
182
|
+
diagnostics: {
|
|
183
|
+
code_injector: {
|
|
184
|
+
enabled: false,
|
|
185
|
+
internal_file_pattern: /nodejs_agent\/(?:(?!test))|\/node_modules\/(?:@)?newrelic/
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
182
189
|
logging: {
|
|
183
190
|
/**
|
|
184
191
|
* Verbosity of the module's logging. This module uses bunyan
|
package/lib/config/env.js
CHANGED
|
@@ -51,6 +51,11 @@ const ENV_MAPPING = {
|
|
|
51
51
|
include: 'NEW_RELIC_TRANSACTION_EVENTS_ATTRIBUTES_INCLUDE'
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
diagnostics: {
|
|
55
|
+
code_injector: {
|
|
56
|
+
enabled: 'NEW_RELIC_DIAGNOSTICS_CODE_INJECTOR_ENABLED'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
54
59
|
transaction_tracer: {
|
|
55
60
|
attributes: {
|
|
56
61
|
enabled: 'NEW_RELIC_TRANSACTION_TRACER_ATTRIBUTES_ENABLED',
|
|
@@ -186,6 +191,7 @@ const BOOLEAN_VARS = new Set([
|
|
|
186
191
|
'NEW_RELIC_TRACER_ENABLED',
|
|
187
192
|
'NEW_RELIC_TRANSACTION_TRACER_ATTRIBUTES_ENABLED',
|
|
188
193
|
'NEW_RELIC_TRANSACTION_EVENTS_ATTRIBUTES_ENABLED',
|
|
194
|
+
'NEW_RELIC_DIAGNOSTICS_CODE_INJECTOR_ENABLED',
|
|
189
195
|
'NEW_RELIC_TRANSACTION_SEGMENTS_ATTRIBUTES_ENABLED',
|
|
190
196
|
'NEW_RELIC_DEBUG_METRICS',
|
|
191
197
|
'NEW_RELIC_DEBUG_TRACER',
|
package/lib/config/index.js
CHANGED
|
@@ -135,9 +135,6 @@ function Config(config) {
|
|
|
135
135
|
this.sampling_target_period_in_seconds = 60
|
|
136
136
|
this.max_payload_size_in_bytes = DEFAULT_MAX_PAYLOAD_SIZE_IN_BYTES
|
|
137
137
|
|
|
138
|
-
// how frequently harvester runs
|
|
139
|
-
this.data_report_period = 60
|
|
140
|
-
|
|
141
138
|
// this value is arbitrary
|
|
142
139
|
this.max_trace_segments = 900
|
|
143
140
|
|
|
@@ -338,7 +335,6 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
338
335
|
|
|
339
336
|
case 'apdex_t':
|
|
340
337
|
case 'web_transactions_apdex':
|
|
341
|
-
case 'data_report_period':
|
|
342
338
|
this._updateIfChanged(params, key)
|
|
343
339
|
break
|
|
344
340
|
case 'event_harvest_config':
|
|
@@ -1029,7 +1025,7 @@ Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrar
|
|
|
1029
1025
|
return
|
|
1030
1026
|
}
|
|
1031
1027
|
|
|
1032
|
-
if (typeof node === 'object' && !Array.isArray(node)) {
|
|
1028
|
+
if (typeof node === 'object' && !(node instanceof RegExp) && !Array.isArray(node)) {
|
|
1033
1029
|
// is top level and can have arbitrary keys
|
|
1034
1030
|
var allowArbitrary = internal === this || HAS_ARBITRARY_KEYS.has(key)
|
|
1035
1031
|
this._fromPassed(node, internal[key], allowArbitrary)
|
|
@@ -1207,6 +1203,10 @@ Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
|
|
|
1207
1203
|
}
|
|
1208
1204
|
// default trusted_account_key to account_id
|
|
1209
1205
|
this.trusted_account_key = this.trusted_account_key || this.account_id
|
|
1206
|
+
|
|
1207
|
+
// Not required in serverless mode but must default to Unknown to function.
|
|
1208
|
+
this.primary_application_id = this.primary_application_id || 'Unknown'
|
|
1209
|
+
|
|
1210
1210
|
return
|
|
1211
1211
|
}
|
|
1212
1212
|
const DT_KEYS = ['account_id', 'primary_application_id', 'trusted_account_key']
|
package/lib/injector.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// to avoid parsing the esprima code base we require it here
|
|
4
|
+
// TODO: excise this with better internal file filtering
|
|
5
|
+
const esprima = require('esprima')
|
|
6
|
+
const ecg = require('escodegen')
|
|
7
|
+
const fileNameToken = {
|
|
8
|
+
type: 'Literal',
|
|
9
|
+
value: null
|
|
10
|
+
}
|
|
11
|
+
const lineNumberTokenTemplate = {
|
|
12
|
+
type: 'Literal',
|
|
13
|
+
value: null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class Mutator {
|
|
17
|
+
constructor(appliesTo, mutate) {
|
|
18
|
+
this.appliesTo = appliesTo
|
|
19
|
+
this.mutate = mutate
|
|
20
|
+
this.tokens = []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
add(token) {
|
|
24
|
+
this.tokens.push(token)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
apply() {
|
|
28
|
+
this.tokens.forEach(this.mutate)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const functionTypes = new Set([
|
|
33
|
+
'ArrowFunctionExpression',
|
|
34
|
+
'FunctionExpression',
|
|
35
|
+
'AsyncFunction'
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
const equalityCheckOps = new Set([
|
|
39
|
+
'==',
|
|
40
|
+
'===',
|
|
41
|
+
'!=',
|
|
42
|
+
'!==',
|
|
43
|
+
'instanceof'
|
|
44
|
+
])
|
|
45
|
+
|
|
46
|
+
const variableTypes = new Set([
|
|
47
|
+
'MemberExpression',
|
|
48
|
+
'Identifier'
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
const callTypes = new Set([
|
|
52
|
+
'CallExpression',
|
|
53
|
+
'NewExpression'
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
const wrapTemplate = esprima.parse('__NR_wrap()').body[0].expression
|
|
57
|
+
const unwrapTemplate = esprima.parse('__NR_unwrap()').body[0].expression
|
|
58
|
+
|
|
59
|
+
const mutators = [
|
|
60
|
+
new Mutator(function wrapAssignmentPredicate(token) {
|
|
61
|
+
return token.type === 'AssignmentExpression' &&
|
|
62
|
+
token.operator === '=' &&
|
|
63
|
+
functionTypes.has(token.right.type)
|
|
64
|
+
}, function injectWrapAssignment(token) {
|
|
65
|
+
token.right = wrapToken(token.right)
|
|
66
|
+
}),
|
|
67
|
+
new Mutator(function wrapArgPredicate(token) {
|
|
68
|
+
return callTypes.has(token.type)
|
|
69
|
+
}, function injectWrapArg(token) {
|
|
70
|
+
token.arguments = token.arguments.map(wrapToken)
|
|
71
|
+
}),
|
|
72
|
+
new Mutator(function unwrapPredicate(token) {
|
|
73
|
+
return token.type === 'BinaryExpression' && equalityCheckOps.has(token.operator)
|
|
74
|
+
}, function injectUnwrap(token) {
|
|
75
|
+
token.left = unwrapToken(token.left)
|
|
76
|
+
token.right = unwrapToken(token.right)
|
|
77
|
+
})
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
function wrapToken(argToken) {
|
|
81
|
+
const type = argToken.type
|
|
82
|
+
if (
|
|
83
|
+
!functionTypes.has(type) &&
|
|
84
|
+
!callTypes.has(type) &&
|
|
85
|
+
!variableTypes.has(type) ||
|
|
86
|
+
!argToken.loc
|
|
87
|
+
) {
|
|
88
|
+
return argToken
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const wrapped = Object.assign({}, wrapTemplate)
|
|
92
|
+
const lineNumberToken = Object.assign({}, lineNumberTokenTemplate)
|
|
93
|
+
lineNumberToken.value = argToken.loc.start.line
|
|
94
|
+
wrapped.arguments = [argToken, lineNumberToken, Object.assign({}, fileNameToken)]
|
|
95
|
+
return wrapped
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function unwrapToken(argToken) {
|
|
99
|
+
if (!variableTypes.has(argToken.type)) {
|
|
100
|
+
return argToken
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const wrapped = Object.assign({}, unwrapTemplate)
|
|
104
|
+
wrapped.arguments = [argToken]
|
|
105
|
+
return wrapped
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function inject(sourceCode, file) {
|
|
109
|
+
// wrap the incoming file code to make it more palatable for esprima.
|
|
110
|
+
// node likewise wraps the contents of the file in a function, so this
|
|
111
|
+
// replicates the behavior (e.g. allows for global returns)
|
|
112
|
+
sourceCode = 'function main() {' + sourceCode + '}'
|
|
113
|
+
const sourceRootBody = esprima.parse(sourceCode, {loc: true}).body[0].body.body
|
|
114
|
+
|
|
115
|
+
const toRelax = [].concat(sourceRootBody)
|
|
116
|
+
|
|
117
|
+
while (toRelax.length) {
|
|
118
|
+
const currentToken = toRelax.pop()
|
|
119
|
+
|
|
120
|
+
mutators.forEach(m => {
|
|
121
|
+
if (m.appliesTo(currentToken)) {
|
|
122
|
+
m.add(currentToken)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
for (let key of Object.keys(currentToken)) {
|
|
127
|
+
if (key === 'loc') continue
|
|
128
|
+
const value = currentToken[key]
|
|
129
|
+
if (value && value instanceof Object) {
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
for (let t of value) {
|
|
132
|
+
if (t) {
|
|
133
|
+
toRelax.push(t)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
toRelax.push(value)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// TODO: make this less janky
|
|
144
|
+
fileNameToken.value = file
|
|
145
|
+
mutators.forEach(m => m.apply())
|
|
146
|
+
fileNameToken.value = null
|
|
147
|
+
// create a new base level token that contains all the statements we
|
|
148
|
+
// want to pass back to node
|
|
149
|
+
const printed = ecg.generate({
|
|
150
|
+
type: 'Program',
|
|
151
|
+
body: sourceRootBody,
|
|
152
|
+
sourceType: 'script'
|
|
153
|
+
}, {
|
|
154
|
+
format: {
|
|
155
|
+
semicolons: false
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
return printed
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { inject }
|
package/lib/shimmer.js
CHANGED
|
@@ -6,6 +6,7 @@ var logger = require('./logger').child({component: 'shimmer'})
|
|
|
6
6
|
var INSTRUMENTATIONS = require('./instrumentations')()
|
|
7
7
|
var properties = require('./util/properties')
|
|
8
8
|
var shims = require('./shim')
|
|
9
|
+
const { inject } = require('./injector')
|
|
9
10
|
|
|
10
11
|
var MODULE_TYPE = shims.constants.MODULE_TYPE
|
|
11
12
|
|
|
@@ -342,6 +343,121 @@ var shimmer = module.exports = {
|
|
|
342
343
|
var Module = require('module')
|
|
343
344
|
var filepathMap = {}
|
|
344
345
|
|
|
346
|
+
// TODO: how do we unpatch this?
|
|
347
|
+
const diagConf = agent.config.diagnostics.code_injector
|
|
348
|
+
if (diagConf.enabled) {
|
|
349
|
+
// XXX: this is required to handle unwrapping event listener removal
|
|
350
|
+
const EEProto = require('events').EventEmitter.prototype
|
|
351
|
+
shimmer.wrapMethod(
|
|
352
|
+
EEProto,
|
|
353
|
+
'EventEmitter.prototype',
|
|
354
|
+
['addListener', 'on', 'once', 'off', 'prependListener', 'prependOnceListener'],
|
|
355
|
+
function wrapAddListener(addListener) {
|
|
356
|
+
return function wrappedAddListener(ev, listener) {
|
|
357
|
+
return addListener.call(this, ev, __NR_unwrap(listener))
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
shimmer.wrapMethod(
|
|
362
|
+
EEProto,
|
|
363
|
+
'EventEmitter.prototype',
|
|
364
|
+
'removeListener',
|
|
365
|
+
function wrapRemoveListener(removeListener) {
|
|
366
|
+
return function wrappedRemoveListener(ev, listener) {
|
|
367
|
+
return removeListener.call(this, ev, __NR_unwrap(listener))
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
const internalCodePattern = diagConf.internal_file_pattern
|
|
372
|
+
const proto = Module.prototype
|
|
373
|
+
shimmer.wrapMethod(
|
|
374
|
+
proto,
|
|
375
|
+
'Module.prototype',
|
|
376
|
+
'_compile',
|
|
377
|
+
function wrapCompile(_compile) {
|
|
378
|
+
return function wrappedCompile(code, file) {
|
|
379
|
+
let injected
|
|
380
|
+
try {
|
|
381
|
+
injected = internalCodePattern.test(file) ? code : inject(code, file)
|
|
382
|
+
return _compile.call(this, injected, file)
|
|
383
|
+
} catch (e) {
|
|
384
|
+
logger.debug('Unable to parse file:', file, e)
|
|
385
|
+
return _compile.call(this, code, file)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
// TODO: use shimmer methods
|
|
392
|
+
global.tracer = agent.tracer
|
|
393
|
+
global.__NR_wrap = __NR_wrap
|
|
394
|
+
function __NR_wrap(f, lineNum, fileName) {
|
|
395
|
+
const scheduledSegment = agent.tracer.getSegment()
|
|
396
|
+
if (
|
|
397
|
+
typeof f !== 'function' ||
|
|
398
|
+
!scheduledSegment ||
|
|
399
|
+
!scheduledSegment.transaction.isActive()
|
|
400
|
+
) {
|
|
401
|
+
return f
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
const scheduledTransaction = scheduledSegment.transaction
|
|
406
|
+
return new Proxy(f, {
|
|
407
|
+
get: function getTrap(target, prop) {
|
|
408
|
+
// Allow for look up of the target
|
|
409
|
+
if (prop === '__NR_new_original') {
|
|
410
|
+
return target
|
|
411
|
+
}
|
|
412
|
+
return target[prop]
|
|
413
|
+
},
|
|
414
|
+
construct: function constructTrap(Target, proxyArgs) {
|
|
415
|
+
const currentSegment = agent.tracer.getSegment()
|
|
416
|
+
if (
|
|
417
|
+
scheduledTransaction.isActive() &&
|
|
418
|
+
(
|
|
419
|
+
!currentSegment ||
|
|
420
|
+
currentSegment.transaction.id !== scheduledTransaction.id
|
|
421
|
+
)
|
|
422
|
+
) {
|
|
423
|
+
logger.info(
|
|
424
|
+
`lost state in ${fileName} (line ${lineNum});`,
|
|
425
|
+
`expected to be in transaction ${scheduledTransaction.id},`,
|
|
426
|
+
`instead landed in ${currentSegment && currentSegment.transaction.id}`
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
return new Target(...proxyArgs)
|
|
430
|
+
},
|
|
431
|
+
apply: function wrappedApply(target, thisArg, args) {
|
|
432
|
+
const currentSegment = agent.tracer.getSegment()
|
|
433
|
+
if (
|
|
434
|
+
scheduledTransaction.isActive() &&
|
|
435
|
+
(
|
|
436
|
+
!currentSegment ||
|
|
437
|
+
currentSegment.transaction.id !== scheduledTransaction.id
|
|
438
|
+
)
|
|
439
|
+
) {
|
|
440
|
+
logger.info(
|
|
441
|
+
`lost state in ${fileName} (line ${lineNum});`,
|
|
442
|
+
`expected to be in transaction ${scheduledTransaction.id},`,
|
|
443
|
+
`instead landed in ${currentSegment && currentSegment.transaction.id}`
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
return target.apply(thisArg, args)
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
global.__NR_unwrap = __NR_unwrap
|
|
452
|
+
function __NR_unwrap(f) {
|
|
453
|
+
if (typeof f !== 'function' || !f || !f.__NR_new_original) {
|
|
454
|
+
return f
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return f.__NR_new_original
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
345
461
|
shimmer.wrapMethod(Module, 'Module', '_resolveFilename', function wrapRes(resolve) {
|
|
346
462
|
return function wrappedResolveFilename(file) {
|
|
347
463
|
// This is triggered by the load call, so record the path that has been seen so
|
package/lib/transaction/index.js
CHANGED
|
@@ -66,7 +66,8 @@ exports.request = function request(opts, agent, cb) {
|
|
|
66
66
|
function respond(data) {
|
|
67
67
|
agent.removeListener('errored', abortRequest)
|
|
68
68
|
agent.removeListener('stopped', abortRequest)
|
|
69
|
-
|
|
69
|
+
agent.removeListener('disconnected', abortRequest)
|
|
70
|
+
|
|
70
71
|
if (res.statusCode !== 200) {
|
|
71
72
|
logger.debug(
|
|
72
73
|
'Got %d %s from metadata request %j',
|
|
@@ -83,22 +84,28 @@ exports.request = function request(opts, agent, cb) {
|
|
|
83
84
|
})
|
|
84
85
|
|
|
85
86
|
req.setTimeout(1000, function requestTimeout() {
|
|
86
|
-
|
|
87
|
-
cb(new Error('Timeout'))
|
|
87
|
+
req.abort()
|
|
88
88
|
})
|
|
89
|
+
|
|
89
90
|
req.on('error', function requestError(err) {
|
|
91
|
+
if (err.code === 'ECONNRESET') {
|
|
92
|
+
logger.debug('Request for metadata %j timed out', opts)
|
|
93
|
+
return cb(err)
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
logger.debug('Message for metadata %j: %s', opts, err.message)
|
|
91
97
|
cb(err)
|
|
92
98
|
})
|
|
93
|
-
|
|
94
99
|
agent.once('errored', abortRequest)
|
|
95
100
|
agent.once('stopped', abortRequest)
|
|
101
|
+
agent.once('disconnected', abortRequest)
|
|
96
102
|
|
|
97
103
|
function abortRequest() {
|
|
98
104
|
logger.debug('Aborting request for metadata at %j', opts)
|
|
99
105
|
req.abort()
|
|
100
106
|
agent.removeListener('errored', abortRequest)
|
|
101
107
|
agent.removeListener('stopped', abortRequest)
|
|
108
|
+
agent.removeListener('disconnected', abortRequest)
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.1",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"contributors": [
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"clean": "./bin/clean.sh",
|
|
109
109
|
"docker-env": "./bin/docker-env-vars.sh",
|
|
110
110
|
"docs": "npm ci && jsdoc -c ./jsdoc-conf.json --private -r .",
|
|
111
|
-
"integration": "npm run prepare-test && npm run sub-install && time tap --no-esm test/integration
|
|
111
|
+
"integration": "npm run prepare-test && npm run sub-install && time tap --no-esm test/integration/**/**/*.tap.js --timeout=120 --no-coverage",
|
|
112
112
|
"prepare-test": "npm ci && npm run ca-gen && npm run ssl && npm run docker-env",
|
|
113
113
|
"lint": "eslint ./*.js lib test",
|
|
114
114
|
"public-docs": "npm ci && jsdoc -c ./jsdoc-conf.json --tutorials examples/shim api.js lib/shim/ lib/transaction/handle.js && cp examples/shim/*.png out/",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"ssl": "./bin/ssl.sh",
|
|
120
120
|
"sub-install": "node test/bin/install_sub_deps",
|
|
121
121
|
"test": "npm run integration && npm run unit",
|
|
122
|
-
"unit": "rm -f newrelic_agent.log && mocha -r nock -c test/unit --recursive",
|
|
122
|
+
"unit": "rm -f newrelic_agent.log && mocha -r nock -c test/unit --recursive --exit",
|
|
123
123
|
"update-cross-agent-tests": "./bin/update-cats.sh",
|
|
124
124
|
"versioned-tests": "./bin/run-versioned-tests.sh",
|
|
125
125
|
"versioned": "npm run prepare-test && time ./bin/run-versioned-tests.sh"
|
|
@@ -133,7 +133,9 @@
|
|
|
133
133
|
"@tyriar/fibonacci-heap": "^2.0.7",
|
|
134
134
|
"async": "^2.1.4",
|
|
135
135
|
"concat-stream": "^2.0.0",
|
|
136
|
-
"
|
|
136
|
+
"escodegen": "^1.11.1",
|
|
137
|
+
"esprima": "^4.0.1",
|
|
138
|
+
"https-proxy-agent": "^3.0.0",
|
|
137
139
|
"json-stringify-safe": "^5.0.0",
|
|
138
140
|
"readable-stream": "^3.1.1",
|
|
139
141
|
"semver": "^5.3.0"
|
|
@@ -158,7 +160,7 @@
|
|
|
158
160
|
"lodash": "^4.17.14",
|
|
159
161
|
"memcached": ">=0.2.8",
|
|
160
162
|
"minami": "^1.1.1",
|
|
161
|
-
"mocha": "^
|
|
163
|
+
"mocha": "^6.2.1",
|
|
162
164
|
"mongodb": "^2",
|
|
163
165
|
"mysql": "*",
|
|
164
166
|
"nock": "9.1.9",
|
|
@@ -170,7 +172,7 @@
|
|
|
170
172
|
"rimraf": "^2.6.3",
|
|
171
173
|
"should": "*",
|
|
172
174
|
"sinon": "^4.5.0",
|
|
173
|
-
"tap": "^
|
|
175
|
+
"tap": "^14.6.9",
|
|
174
176
|
"temp": "^0.8.1",
|
|
175
177
|
"through": "^2.3.6",
|
|
176
178
|
"when": "*"
|
package/lib/event-aggregator.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const logger = require('./logger').child({component: 'event_aggregator'})
|
|
4
|
-
const PriorityQueue = require('./priority-queue')
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Aggregates events up to a certain limit.
|
|
8
|
-
*
|
|
9
|
-
* @private
|
|
10
|
-
* @class
|
|
11
|
-
*/
|
|
12
|
-
class EventAggregator {
|
|
13
|
-
constructor(limit) {
|
|
14
|
-
this._events = new PriorityQueue(limit)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
get limit() {
|
|
18
|
-
return this._events.limit
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
set limit(limit) {
|
|
22
|
-
this._events.setLimit(limit)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
get seen() {
|
|
26
|
-
return this._events.seen
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get length() {
|
|
30
|
-
return this._events.length
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get overflow() {
|
|
34
|
-
return this._events.overflow()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
*/
|
|
40
|
-
getQueue() {
|
|
41
|
-
return this._events
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Fetches all the span events aggregated.
|
|
46
|
-
*
|
|
47
|
-
* @return {array.<Event>} An array of span events from the aggregator.
|
|
48
|
-
*/
|
|
49
|
-
getEvents() {
|
|
50
|
-
return this._events.toArray()
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Resets the contents of the aggregator and all counters.
|
|
55
|
-
*
|
|
56
|
-
* @return {PriorityQueue} The old collection of aggregated events.
|
|
57
|
-
*/
|
|
58
|
-
clearEvents() {
|
|
59
|
-
const oldEvents = this._events
|
|
60
|
-
this._events = new PriorityQueue(this._events.limit)
|
|
61
|
-
return oldEvents
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
addEvent(event, priority) {
|
|
65
|
-
return this._events.add(event, priority)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Merges a set of events back into the aggregator.
|
|
70
|
-
*
|
|
71
|
-
* This should only be used after a failed harvest with the `PriorityQueue`
|
|
72
|
-
* returned from `EventAggregator#clearEvents`.
|
|
73
|
-
*
|
|
74
|
-
* @param {?PriorityQueue} events - The collection of events to re-merge.
|
|
75
|
-
*/
|
|
76
|
-
mergeEvents(events) {
|
|
77
|
-
if (!events) {
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// We calculate the number that will be merged for the log, but we try to
|
|
82
|
-
// add every event because we want the ones with the highest priority, not
|
|
83
|
-
// the first `n` events.
|
|
84
|
-
const toMerge = Math.min(events.length, this.limit - this.length)
|
|
85
|
-
logger.warn(
|
|
86
|
-
'Merging %d of %d events into %s for next harvest',
|
|
87
|
-
toMerge, events.length, this.constructor.name
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
this._events.merge(events)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = EventAggregator
|