newrelic 2.0.0 → 2.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/.eslintrc +1 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- package/NEWS.md +75 -0
- package/README.md +24 -12
- package/api.js +74 -29
- package/index.js +6 -2
- package/lib/agent.js +15 -3
- package/lib/collector/api.js +11 -9
- package/lib/collector/facts.js +9 -9
- package/lib/config.default.js +24 -2
- package/lib/config.js +70 -7
- package/lib/db/parse-sql.js +3 -0
- package/lib/db/tracer.js +3 -3
- package/lib/feature_flags.js +1 -0
- package/lib/instrumentation/core/async_hooks.js +92 -0
- package/lib/instrumentation/core/child_process.js +35 -0
- package/lib/instrumentation/core/globals.js +4 -23
- package/lib/instrumentation/core/http.js +1 -1
- package/lib/instrumentation/hapi.js +67 -33
- package/lib/instrumentation/ioredis.js +1 -6
- package/lib/metrics/names.js +7 -2
- package/lib/shim/datastore-shim.js +13 -1
- package/lib/shim/shim.js +46 -8
- package/lib/shim/webframework-shim.js +4 -2
- package/lib/stats/index.js +1 -1
- package/lib/system-info.js +21 -42
- package/lib/transaction/handle.js +14 -0
- package/lib/transaction/index.js +10 -2
- package/lib/transaction/tracer/index.js +1 -0
- package/lib/util/hashes.js +8 -1
- package/lib/util/logger.js +4 -4
- package/lib/util/sql/obfuscate.js +10 -4
- package/lib/utilization/aws-info.js +46 -0
- package/lib/utilization/azure-info.js +54 -0
- package/lib/utilization/common.js +106 -0
- package/lib/utilization/docker-info.js +100 -0
- package/lib/utilization/gcp-info.js +56 -0
- package/lib/utilization/index.js +28 -0
- package/lib/utilization/pcf-info.js +38 -0
- package/newrelic.js +1 -2
- package/package.json +2 -1
- package/stub_api.js +26 -9
- package/lib/aws-info.js +0 -100
- package/lib/parse-dockerinfo.js +0 -57
package/lib/config.js
CHANGED
|
@@ -12,6 +12,7 @@ var exists = fs.existsSync || path.existsSync
|
|
|
12
12
|
var safeJSON = require('./util/safe-json')
|
|
13
13
|
var stringifySync = safeJSON.stringifySync
|
|
14
14
|
var parse = safeJSON.parse
|
|
15
|
+
var semver = require('semver')
|
|
15
16
|
var os = require('os')
|
|
16
17
|
var logger
|
|
17
18
|
|
|
@@ -77,6 +78,9 @@ var ENV_MAPPING = {
|
|
|
77
78
|
},
|
|
78
79
|
utilization: {
|
|
79
80
|
detect_aws: "NEW_RELIC_UTILIZATION_DETECT_AWS",
|
|
81
|
+
detect_pcf: "NEW_RELIC_UTILIZATION_DETECT_PCF",
|
|
82
|
+
detect_azure: "NEW_RELIC_UTILIZATION_DETECT_AZURE",
|
|
83
|
+
detect_gcp: "NEW_RELIC_UTILIZATION_DETECT_GCP",
|
|
80
84
|
detect_docker: "NEW_RELIC_UTILIZATION_DETECT_DOCKER",
|
|
81
85
|
logical_processors: "NEW_RELIC_UTILIZATION_LOGICAL_PROCESSORS",
|
|
82
86
|
total_ram_mib: "NEW_RELIC_UTILIZATION_TOTAL_RAM_MIB",
|
|
@@ -724,6 +728,19 @@ Config.prototype.logUnknown = function logUnknown(json, key) {
|
|
|
724
728
|
)
|
|
725
729
|
}
|
|
726
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Return the availability of async_hook for use by the agent.
|
|
733
|
+
*/
|
|
734
|
+
Config.prototype.checkAsyncHookStatus = function checkAsyncHookStatus() {
|
|
735
|
+
return (
|
|
736
|
+
(
|
|
737
|
+
semver.satisfies(process.version, '>=8') ||
|
|
738
|
+
semver.prerelease(process.version)
|
|
739
|
+
) &&
|
|
740
|
+
this.feature_flag.await_support
|
|
741
|
+
)
|
|
742
|
+
}
|
|
743
|
+
|
|
727
744
|
/**
|
|
728
745
|
* Gets the user set host display name. If not provided, it returns the default value.
|
|
729
746
|
*
|
|
@@ -945,13 +962,59 @@ Config.prototype._fromEnvironment = function _fromEnvironment(metadata, data) {
|
|
|
945
962
|
* based on special properties of configuration values should go here as well.
|
|
946
963
|
*/
|
|
947
964
|
Config.prototype._canonicalize = function _canonicalize() {
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
965
|
+
function parseCodes(codes) {
|
|
966
|
+
// range does not support negative values
|
|
967
|
+
function parseRange(range, parsed) {
|
|
968
|
+
var split = range.split('-')
|
|
969
|
+
if (split.length !== 2) {
|
|
970
|
+
logger.warn('Failed to parse range %s', range)
|
|
971
|
+
return parsed
|
|
972
|
+
}
|
|
973
|
+
if (split[0] === '') { // catch negative code. ex. -7
|
|
974
|
+
return parsed.push(parseInt(range, 10))
|
|
975
|
+
}
|
|
976
|
+
var lower = parseInt(split[0], 10)
|
|
977
|
+
var upper = parseInt(split[1], 10)
|
|
978
|
+
if (Number.isNaN(lower) || Number.isNaN(upper)) {
|
|
979
|
+
logger.warn('Range must contain two numbers %s', range)
|
|
980
|
+
return parsed
|
|
981
|
+
}
|
|
982
|
+
if (lower > upper) {
|
|
983
|
+
logger.warn('Range must start with lower bound %s', range)
|
|
984
|
+
} else if (lower < 0 || upper > 1000) {
|
|
985
|
+
logger.warn('Range must be between 0 and 1000 %s', range)
|
|
986
|
+
} else { // success
|
|
987
|
+
for (var i = lower; i <= upper; i++) {
|
|
988
|
+
parsed.push(i)
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return parsed
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
var parsedCodes = []
|
|
995
|
+
for (var i = 0; i < codes.length; i++) {
|
|
996
|
+
var code = codes[i]
|
|
997
|
+
var parsedCode
|
|
998
|
+
if (typeof code === 'string' && code.indexOf('-') !== -1) {
|
|
999
|
+
parseRange(code, parsedCodes)
|
|
1000
|
+
} else {
|
|
1001
|
+
parsedCode = parseInt(code, 10)
|
|
1002
|
+
if (!Number.isNaN(parsedCode)) {
|
|
1003
|
+
parsedCodes.push(parsedCode)
|
|
1004
|
+
} else {
|
|
1005
|
+
logger.warn('Failed to parse status code %s', code)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return parsedCodes
|
|
953
1010
|
}
|
|
954
1011
|
|
|
1012
|
+
var statusCodes = this.error_collector && this.error_collector.ignore_status_codes
|
|
1013
|
+
if (statusCodes) {
|
|
1014
|
+
this.error_collector.ignore_status_codes = parseCodes(statusCodes)
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
|
|
955
1018
|
var logAliases = {
|
|
956
1019
|
'verbose': 'trace',
|
|
957
1020
|
'debugging': 'debug',
|
|
@@ -1017,10 +1080,10 @@ Config.prototype.measureInternal = function measureInternal(suffix, duration) {
|
|
|
1017
1080
|
Config.prototype.validateFlags = function validateFlags() {
|
|
1018
1081
|
Object.keys(this.feature_flag).forEach(function cb_forEach(key) {
|
|
1019
1082
|
if (feature_flag.released.indexOf(key) > -1) {
|
|
1020
|
-
logger.warn('Feature flag
|
|
1083
|
+
logger.warn('Feature flag %s has been released', key)
|
|
1021
1084
|
}
|
|
1022
1085
|
if (feature_flag.unreleased.indexOf(key) > -1) {
|
|
1023
|
-
logger.warn('Feature flag
|
|
1086
|
+
logger.warn('Feature flag %s has been deprecated', key)
|
|
1024
1087
|
}
|
|
1025
1088
|
})
|
|
1026
1089
|
}
|
package/lib/db/parse-sql.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// TODO: remove in favor of /lib/db/query-parsers/sql.js
|
|
4
|
+
// This module is currently used only in the Oracle instrumentation
|
|
5
|
+
|
|
3
6
|
var logger = require('../logger').child({component: 'parse_sql'})
|
|
4
7
|
var StatementMatcher = require('./statement-matcher')
|
|
5
8
|
var ParsedStatement = require('./parsed-statement')
|
package/lib/db/tracer.js
CHANGED
|
@@ -56,15 +56,15 @@ QueryTracer.prototype.addQuery = function addQuery(segment, type, query, trace)
|
|
|
56
56
|
|
|
57
57
|
switch (this.config.transaction_tracer.record_sql) {
|
|
58
58
|
case 'raw':
|
|
59
|
-
logger.
|
|
59
|
+
logger.trace('recording raw sql')
|
|
60
60
|
segment.parameters.sql = slowQuery.query
|
|
61
61
|
break
|
|
62
62
|
case 'obfuscated':
|
|
63
|
-
logger.
|
|
63
|
+
logger.trace('recording obfuscated sql')
|
|
64
64
|
segment.parameters.sql_obfuscated = slowQuery.obfuscated
|
|
65
65
|
break
|
|
66
66
|
default:
|
|
67
|
-
logger.
|
|
67
|
+
logger.trace(
|
|
68
68
|
'not collecting slow-query because transaction_tracer.record_sql was set to %s',
|
|
69
69
|
this.config.transaction_tracer.record_sql
|
|
70
70
|
)
|
package/lib/feature_flags.js
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var logger = require('../../logger').child({component: 'async_hooks'})
|
|
4
|
+
var promInit = require('../promise')
|
|
5
|
+
var semver = require('semver')
|
|
6
|
+
|
|
7
|
+
module.exports = initialize
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The spec for the native `Promise` class.
|
|
11
|
+
*/
|
|
12
|
+
var STATIC_PROMISE_METHODS = ['accept', 'all', 'defer', 'race', 'reject', 'resolve']
|
|
13
|
+
var NATIVE_PROMISE_SPEC = {
|
|
14
|
+
name: 'global',
|
|
15
|
+
constructor: 'Promise',
|
|
16
|
+
executor: true,
|
|
17
|
+
$proto: {
|
|
18
|
+
then: ['then', 'chain'],
|
|
19
|
+
catch: ['catch']
|
|
20
|
+
},
|
|
21
|
+
$static: {
|
|
22
|
+
$copy: STATIC_PROMISE_METHODS,
|
|
23
|
+
cast: STATIC_PROMISE_METHODS
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function initialize(agent) {
|
|
28
|
+
var enableHooks = agent.config.checkAsyncHookStatus()
|
|
29
|
+
if (enableHooks && tryAsyncHooks(agent)) {
|
|
30
|
+
logger.debug('Using async_hooks.')
|
|
31
|
+
} else if (semver.satisfies(process.version, '>=0.12')) {
|
|
32
|
+
logger.debug('Using promise instrumentation.')
|
|
33
|
+
promInit(agent, global, NATIVE_PROMISE_SPEC)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tryAsyncHooks(agent) {
|
|
38
|
+
var asyncHooks = null
|
|
39
|
+
try {
|
|
40
|
+
asyncHooks = require('async_hooks')
|
|
41
|
+
} catch (e) {
|
|
42
|
+
logger.info(e, 'Not using async_hooks module.')
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// this map is reused to track the segment that was active when
|
|
47
|
+
// the before callback is called to be replaced in the after callback
|
|
48
|
+
var segmentMap = new Map()
|
|
49
|
+
module.exports._segmentMap = segmentMap
|
|
50
|
+
|
|
51
|
+
asyncHooks.createHook({
|
|
52
|
+
init: function initHook(id, type) {
|
|
53
|
+
var transaction = agent.getTransaction()
|
|
54
|
+
|
|
55
|
+
if (!transaction || type !== 'PROMISE') {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
before: function beforeHook(id) {
|
|
63
|
+
var hookSegment = segmentMap.get(id)
|
|
64
|
+
|
|
65
|
+
if (!hookSegment) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
70
|
+
agent.tracer.segment = hookSegment
|
|
71
|
+
},
|
|
72
|
+
after: function afterHook(id) {
|
|
73
|
+
var hookSegment = segmentMap.get(id)
|
|
74
|
+
|
|
75
|
+
// hookSegment is the segment that was active before the promise
|
|
76
|
+
// executed. If the promise is executing before a segment has been
|
|
77
|
+
// restored, hookSegment will be null and should be restored. Thus
|
|
78
|
+
// undefined is the only invalid value here.
|
|
79
|
+
if (hookSegment === undefined) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
84
|
+
agent.tracer.segment = hookSegment
|
|
85
|
+
},
|
|
86
|
+
destroy: function destHook(id) {
|
|
87
|
+
segmentMap.delete(id)
|
|
88
|
+
}
|
|
89
|
+
}).enable()
|
|
90
|
+
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var wrap = require('../../shimmer').wrapMethod
|
|
4
|
+
var isWrapped = require('../../shimmer').isWrapped
|
|
4
5
|
|
|
5
6
|
module.exports = initialize
|
|
6
7
|
|
|
@@ -12,4 +13,38 @@ function initialize(agent, childProcess) {
|
|
|
12
13
|
function wrapMethod(fn, method) {
|
|
13
14
|
return agent.tracer.wrapFunctionLast('child_process.' + method, null, fn)
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
var childProcessProto = childProcess && childProcess.ChildProcess
|
|
18
|
+
// ChildProcess is exposed on Node 4 and higher
|
|
19
|
+
if (childProcessProto) {
|
|
20
|
+
wrapChildProcessCls(childProcess.ChildProcess)
|
|
21
|
+
} else {
|
|
22
|
+
wrap(childProcess, 'childProcess', ['fork', 'spawn'], wrapSpawn)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function wrapSpawn(fn) {
|
|
26
|
+
return function wrapped() {
|
|
27
|
+
var child = fn.apply(this, arguments)
|
|
28
|
+
if (child && child.constructor && child.constructor.prototype) {
|
|
29
|
+
wrapChildProcessCls(child.constructor)
|
|
30
|
+
}
|
|
31
|
+
return child
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrapChildProcessCls(childProcessCls) {
|
|
36
|
+
if (!childProcessCls || !childProcessCls.prototype ||
|
|
37
|
+
isWrapped(childProcessCls.prototype.on)) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
wrap(
|
|
42
|
+
childProcessCls.prototype,
|
|
43
|
+
'childProcess.ChildProcess.prototype',
|
|
44
|
+
'on',
|
|
45
|
+
function wrapEmit(fn) {
|
|
46
|
+
return agent.tracer.wrapFunctionNoSegment(fn, ['on', 'addListener'])
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
}
|
|
15
50
|
}
|
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
var asyncHookInstrumentation = require('./async_hooks')
|
|
3
4
|
var events = require('events')
|
|
4
5
|
var wrap = require('../../shimmer').wrapMethod
|
|
5
|
-
var promInit = require('../promise')
|
|
6
|
-
var semver = require('semver')
|
|
7
6
|
|
|
8
7
|
module.exports = initialize
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
* The spec for the native `Promise` class.
|
|
12
|
-
*/
|
|
13
|
-
var STATIC_PROMISE_METHODS = ['accept', 'all', 'defer', 'race', 'reject', 'resolve']
|
|
14
|
-
var NATIVE_PROMISE_SPEC = {
|
|
15
|
-
name: 'global',
|
|
16
|
-
constructor: 'Promise',
|
|
17
|
-
executor: true,
|
|
18
|
-
$proto: {
|
|
19
|
-
then: ['then', 'chain'],
|
|
20
|
-
catch: ['catch']
|
|
21
|
-
},
|
|
22
|
-
$static: {
|
|
23
|
-
$copy: STATIC_PROMISE_METHODS,
|
|
24
|
-
cast: STATIC_PROMISE_METHODS
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
9
|
|
|
28
10
|
function initialize(agent) {
|
|
29
11
|
// `_fatalException` is an undocumented feature of domains, introduced in
|
|
@@ -63,10 +45,9 @@ function initialize(agent) {
|
|
|
63
45
|
}
|
|
64
46
|
)
|
|
65
47
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
48
|
+
// This will initialize the most optimal native-promise instrumentation
|
|
49
|
+
// that we have available.
|
|
50
|
+
asyncHookInstrumentation(agent)
|
|
70
51
|
}
|
|
71
52
|
|
|
72
53
|
function listenerCount(emitter, evnt) {
|
|
@@ -131,7 +131,7 @@ function wrapEmitWithTransaction(agent, emit) {
|
|
|
131
131
|
var start = parseFloat(qtime)
|
|
132
132
|
|
|
133
133
|
if (isNaN(start)) {
|
|
134
|
-
logger.warn('Queue time header parsed as NaN ('
|
|
134
|
+
logger.warn('Queue time header parsed as NaN (%s)', qtime)
|
|
135
135
|
} else {
|
|
136
136
|
// nano seconds
|
|
137
137
|
if (start > 1e18) start = start / 1e6
|
|
@@ -23,6 +23,33 @@ module.exports = function initialize(agent, hapi, moduleName, shim) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function wrapServer(shim, Server) {
|
|
26
|
+
// wrap server.handler function that registers a new handler type
|
|
27
|
+
// the second argument is expected to be a function that generates the handler function
|
|
28
|
+
shim.wrap(Server.prototype, 'handler', function wrapHandler(shim, original) {
|
|
29
|
+
return function wrappedHandler() {
|
|
30
|
+
var args = shim.argsToArray.apply(shim, arguments)
|
|
31
|
+
|
|
32
|
+
var handlerGenerator = args[1]
|
|
33
|
+
if (typeof handlerGenerator === 'function') {
|
|
34
|
+
args[1] = wrapGenerator(handlerGenerator)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return original.apply(this, args)
|
|
38
|
+
|
|
39
|
+
function wrapGenerator(generator) {
|
|
40
|
+
return function wrappedGenerator() {
|
|
41
|
+
var generatorArgs = shim.argsToArray.apply(shim, arguments)
|
|
42
|
+
var handler = generator.apply(this, generatorArgs)
|
|
43
|
+
if (typeof handler === 'function') {
|
|
44
|
+
var route = generatorArgs[0]
|
|
45
|
+
return wrapRouteHandler(shim, handler, route && route.path)
|
|
46
|
+
}
|
|
47
|
+
return handler
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
26
53
|
shim.wrap(Server.prototype, 'route', function wrapRoute(shim, original) {
|
|
27
54
|
return function wrappedRoute() {
|
|
28
55
|
var args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -35,44 +62,19 @@ function wrapServer(shim, Server) {
|
|
|
35
62
|
var route = args[0]
|
|
36
63
|
// handler function could be on the route object, or on a nested config object
|
|
37
64
|
if (route.config) {
|
|
38
|
-
|
|
65
|
+
_wrapRouteHandler(shim, route.config, route.path)
|
|
39
66
|
} else {
|
|
40
|
-
|
|
67
|
+
_wrapRouteHandler(shim, route)
|
|
41
68
|
}
|
|
42
69
|
|
|
43
70
|
return original.apply(this, args)
|
|
44
71
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
shim.recordRender(reply, 'view')
|
|
52
|
-
}
|
|
53
|
-
return original.apply(this, arguments)
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return shim.recordMiddleware(wrappedHandler, {
|
|
58
|
-
route: path || route.path,
|
|
59
|
-
req: function getReq(shim, fn, fnName, args) {
|
|
60
|
-
var request = args[0]
|
|
61
|
-
if (request && request.raw) {
|
|
62
|
-
return request.raw.req
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
next: function wrapNext(shim, fn, fnName, args, wrap) {
|
|
66
|
-
var reply = args[1]
|
|
67
|
-
if (!shim.isFunction(reply)) return
|
|
68
|
-
wrap(reply, 'response', true)
|
|
69
|
-
},
|
|
70
|
-
params: function getParams(shim, fn, fnName, args, req) {
|
|
71
|
-
var req = args[0]
|
|
72
|
-
if (!req) return
|
|
73
|
-
return req.params
|
|
74
|
-
}
|
|
75
|
-
})
|
|
72
|
+
function _wrapRouteHandler(shim, container, path) {
|
|
73
|
+
if (typeof container.handler !== 'function') {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
shim.wrap(container, 'handler', function wrapHandler(shim, handler) {
|
|
77
|
+
return wrapRouteHandler(shim, handler, path || route.path)
|
|
76
78
|
})
|
|
77
79
|
}
|
|
78
80
|
}
|
|
@@ -109,3 +111,35 @@ function wrapCreateServer(shim, hapi) {
|
|
|
109
111
|
}
|
|
110
112
|
})
|
|
111
113
|
}
|
|
114
|
+
|
|
115
|
+
function wrapRouteHandler(shim, handler, path) {
|
|
116
|
+
var wrappedHandler = shim.wrap(handler, function wrapHandler(shim, original) {
|
|
117
|
+
return function wrapped() {
|
|
118
|
+
var reply = arguments[1]
|
|
119
|
+
if (reply) {
|
|
120
|
+
shim.recordRender(reply, 'view')
|
|
121
|
+
}
|
|
122
|
+
return original.apply(this, arguments)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return shim.recordMiddleware(wrappedHandler, {
|
|
127
|
+
route: path,
|
|
128
|
+
req: function getReq(shim, fn, fnName, args) {
|
|
129
|
+
var request = args[0]
|
|
130
|
+
if (request && request.raw) {
|
|
131
|
+
return request.raw.req
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
next: function wrapNext(shim, fn, fnName, args, wrap) {
|
|
135
|
+
var reply = args[1]
|
|
136
|
+
if (!shim.isFunction(reply)) return
|
|
137
|
+
wrap(reply, 'response', true)
|
|
138
|
+
},
|
|
139
|
+
params: function getParams(shim, fn, fnName, args, req) {
|
|
140
|
+
var req = args[0]
|
|
141
|
+
if (!req) return
|
|
142
|
+
return req.params
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
@@ -33,12 +33,7 @@ module.exports = function initialize(agent, redis, moduleName, shim) {
|
|
|
33
33
|
return {
|
|
34
34
|
name: (command.name || 'unknown'),
|
|
35
35
|
parameters: parameters,
|
|
36
|
-
|
|
37
|
-
// record duration when promise resolves
|
|
38
|
-
command.promise.finally(function cb_resolved() {
|
|
39
|
-
segment.touch()
|
|
40
|
-
})
|
|
41
|
-
}
|
|
36
|
+
promise: true
|
|
42
37
|
}
|
|
43
38
|
}
|
|
44
39
|
}
|
package/lib/metrics/names.js
CHANGED
|
@@ -150,12 +150,17 @@ var SUPPORTABILITY = {
|
|
|
150
150
|
EVENTS: 'Supportability/Events',
|
|
151
151
|
API: 'Supportability/API',
|
|
152
152
|
UTILIZATION: 'Supportability/utilization',
|
|
153
|
-
DEPENDENCIES: 'Supportability/InstalledDependencies'
|
|
153
|
+
DEPENDENCIES: 'Supportability/InstalledDependencies',
|
|
154
|
+
NODEJS: 'Supportability/Nodejs'
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
var UTILIZATION = {
|
|
157
158
|
AWS_ERROR: SUPPORTABILITY.UTILIZATION + '/aws/error',
|
|
158
|
-
|
|
159
|
+
PCF_ERROR: SUPPORTABILITY.UTILIZATION + '/pcf/error',
|
|
160
|
+
AZURE_ERROR: SUPPORTABILITY.UTILIZATION + '/azure/error',
|
|
161
|
+
GCP_ERROR: SUPPORTABILITY.UTILIZATION + '/gcp/error',
|
|
162
|
+
DOCKER_ERROR: SUPPORTABILITY.UTILIZATION + '/docker/error',
|
|
163
|
+
BOOT_ID_ERROR: SUPPORTABILITY.UTILIZATION + '/boot_id/error'
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
|
|
@@ -523,10 +523,22 @@ function recordBatchQuery(nodule, properties, querySpec) {
|
|
|
523
523
|
*/
|
|
524
524
|
function parseQuery(query, nodule) {
|
|
525
525
|
var parsed = this.queryParser.call(nodule, query)
|
|
526
|
+
|
|
527
|
+
var collection = parsed.collection
|
|
528
|
+
// strip enclosing special characters from collection (table) name
|
|
529
|
+
if (typeof collection === 'string' && collection.length > 2) {
|
|
530
|
+
if (/^[\[{'"`]/.test(collection)) {
|
|
531
|
+
collection = collection.substr(1)
|
|
532
|
+
}
|
|
533
|
+
if (/[\]}'"`]$/.test(collection)) {
|
|
534
|
+
collection = collection.substr(0, collection.length - 1)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
526
538
|
return new ParsedStatement(
|
|
527
539
|
this._metrics.PREFIX,
|
|
528
540
|
parsed.operation,
|
|
529
|
-
|
|
541
|
+
collection,
|
|
530
542
|
parsed.query
|
|
531
543
|
)
|
|
532
544
|
}
|
package/lib/shim/shim.js
CHANGED
|
@@ -127,6 +127,7 @@ Shim.prototype.record = record
|
|
|
127
127
|
Shim.prototype.isWrapped = isWrapped
|
|
128
128
|
Shim.prototype.unwrap = unwrap
|
|
129
129
|
Shim.prototype.getSegment = getSegment
|
|
130
|
+
Shim.prototype.getActiveSegment = getActiveSegment
|
|
130
131
|
Shim.prototype.storeSegment = storeSegment
|
|
131
132
|
Shim.prototype.bindCallbackSegment = bindCallbackSegment
|
|
132
133
|
Shim.prototype.applySegment = applySegment
|
|
@@ -875,8 +876,8 @@ function record(nodule, properties, recordNamer) {
|
|
|
875
876
|
shim.logger.trace('Wrapping "%s" with metric recording.', name)
|
|
876
877
|
|
|
877
878
|
return function wrapper() {
|
|
878
|
-
// See if we're in
|
|
879
|
-
var parent = shim.
|
|
879
|
+
// See if we're in an active transaction.
|
|
880
|
+
var parent = shim.getActiveSegment()
|
|
880
881
|
if (!parent) {
|
|
881
882
|
shim.logger.debug('Not recording function %s, not in a transaction.', name)
|
|
882
883
|
return fn.apply(this, arguments)
|
|
@@ -911,8 +912,11 @@ function record(nodule, properties, recordNamer) {
|
|
|
911
912
|
})
|
|
912
913
|
|
|
913
914
|
// Apply the function, and (if it returned a stream) bind that too.
|
|
915
|
+
// If you are looking here Geoff, the reason there is no check for
|
|
916
|
+
// `segment` is because it should be guaranteed by the parent and active
|
|
917
|
+
// transaction check at the beginning of this function. :)
|
|
914
918
|
var ret = _applyRecorderSegment(segment, this, args, segDesc)
|
|
915
|
-
if (
|
|
919
|
+
if (ret) {
|
|
916
920
|
if (segDesc.stream) {
|
|
917
921
|
shim.logger.trace('Binding return value as stream.')
|
|
918
922
|
_bindStream(shim, ret, segment, {
|
|
@@ -1034,12 +1038,20 @@ function bindSegment(nodule, property, segment, full) {
|
|
|
1034
1038
|
|
|
1035
1039
|
// Determine our arguments.
|
|
1036
1040
|
if (this.isObject(property) && !this.isArray(property)) {
|
|
1037
|
-
// bindSegment(func, segment, full)
|
|
1041
|
+
// bindSegment(func, segment [, full])
|
|
1038
1042
|
full = segment
|
|
1039
1043
|
segment = property
|
|
1040
1044
|
property = null
|
|
1041
1045
|
}
|
|
1042
1046
|
|
|
1047
|
+
// This protects against the `bindSegment(func, null, true)` case, where the
|
|
1048
|
+
// segment is `null`, and thus `true` (the full param) is detected as the
|
|
1049
|
+
// segment.
|
|
1050
|
+
if (segment != null && !this.isObject(segment)) {
|
|
1051
|
+
this.logger.debug({segment: segment}, 'Segment is not a segment, not binding.')
|
|
1052
|
+
return nodule
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1043
1055
|
var wrapped = this.wrap(nodule, property, function wrapFunc(shim, func) {
|
|
1044
1056
|
if (!shim.isFunction(func)) {
|
|
1045
1057
|
return func
|
|
@@ -1133,8 +1145,8 @@ function bindCallbackSegment(args, cbIdx, parentSegment) {
|
|
|
1133
1145
|
}
|
|
1134
1146
|
|
|
1135
1147
|
/**
|
|
1136
|
-
* Retrieves the segment associated with the given object, or the
|
|
1137
|
-
*
|
|
1148
|
+
* Retrieves the segment associated with the given object, or the current
|
|
1149
|
+
* segment if no object is given.
|
|
1138
1150
|
*
|
|
1139
1151
|
* - `getSegment([obj])`
|
|
1140
1152
|
*
|
|
@@ -1143,8 +1155,8 @@ function bindCallbackSegment(args, cbIdx, parentSegment) {
|
|
|
1143
1155
|
* @param {*} [obj] - The object to retrieve a segment from.
|
|
1144
1156
|
*
|
|
1145
1157
|
* @return {?TraceSegment} The trace segment associated with the given object or
|
|
1146
|
-
* the
|
|
1147
|
-
*
|
|
1158
|
+
* the current segment if no object is provided or no segment is associated
|
|
1159
|
+
* with the object.
|
|
1148
1160
|
*/
|
|
1149
1161
|
function getSegment(obj) {
|
|
1150
1162
|
if (obj && obj.__NR_segment) {
|
|
@@ -1153,6 +1165,32 @@ function getSegment(obj) {
|
|
|
1153
1165
|
return this.tracer.getSegment()
|
|
1154
1166
|
}
|
|
1155
1167
|
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Retrieves the segment associated with the given object, or the currently
|
|
1171
|
+
* active segment if no object is given.
|
|
1172
|
+
*
|
|
1173
|
+
* - `getActiveSegment([obj])`
|
|
1174
|
+
*
|
|
1175
|
+
* An active segment is one whose transaction is still active (e.g. has not
|
|
1176
|
+
* ended yet).
|
|
1177
|
+
*
|
|
1178
|
+
* @memberof Shim.prototype
|
|
1179
|
+
*
|
|
1180
|
+
* @param {*} [obj] - The object to retrieve a segment from.
|
|
1181
|
+
*
|
|
1182
|
+
* @return {?TraceSegment} The trace segment associated with the given object or
|
|
1183
|
+
* the currently active segment if no object is provided or no segment is
|
|
1184
|
+
* associated with the object.
|
|
1185
|
+
*/
|
|
1186
|
+
function getActiveSegment(obj) {
|
|
1187
|
+
var segment = this.getSegment(obj)
|
|
1188
|
+
if (segment && segment.transaction && segment.transaction.isActive()) {
|
|
1189
|
+
return segment
|
|
1190
|
+
}
|
|
1191
|
+
return null
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1156
1194
|
/**
|
|
1157
1195
|
* Associates a segment with the given object.
|
|
1158
1196
|
*
|
|
@@ -652,8 +652,10 @@ function setErrorPredicate(pred) {
|
|
|
652
652
|
*/
|
|
653
653
|
function captureUrlParams(params) {
|
|
654
654
|
var segment = this.getSegment()
|
|
655
|
-
|
|
656
|
-
|
|
655
|
+
if (segment) {
|
|
656
|
+
var config = this.agent.config
|
|
657
|
+
urltils.copyParameters(config, params, segment.parameters)
|
|
658
|
+
}
|
|
657
659
|
}
|
|
658
660
|
|
|
659
661
|
// -------------------------------------------------------------------------- //
|
package/lib/stats/index.js
CHANGED
|
@@ -41,7 +41,7 @@ Stats.prototype.recordValue = function recordValue(totalTime, exclusiveTime) {
|
|
|
41
41
|
this.max = Math.max(totalTime, this.max)
|
|
42
42
|
|
|
43
43
|
this.sumOfSquares += (totalTime * totalTime)
|
|
44
|
-
this.callCount
|
|
44
|
+
++this.callCount
|
|
45
45
|
this.total += totalTime
|
|
46
46
|
this.totalExclusive += exclusiveTime
|
|
47
47
|
}
|