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/system-info.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var exec = require('child_process').exec
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var readProc = require('./utilization/common').readProc
|
|
5
|
+
var getBootId = require('./utilization/docker-info').getBootId
|
|
6
|
+
var utilization = require('./utilization')
|
|
6
7
|
var logger = require('./logger.js').child({component: 'system-info'})
|
|
7
8
|
var os = require('os')
|
|
8
9
|
var parseCpuInfo = require('./parse-proc-cpuinfo')
|
|
9
|
-
var parseDockerInfo = require('./parse-dockerinfo')
|
|
10
10
|
var parseMemInfo = require('./parse-proc-meminfo')
|
|
11
11
|
var platform = os.platform()
|
|
12
12
|
|
|
@@ -71,7 +71,9 @@ function fetchSystemInfo(agent, callback) {
|
|
|
71
71
|
var tasksDone = 0
|
|
72
72
|
var numTasks = 5
|
|
73
73
|
function finishedResponse() {
|
|
74
|
-
if (++tasksDone === numTasks)
|
|
74
|
+
if (++tasksDone === numTasks) {
|
|
75
|
+
callback(systemInfo)
|
|
76
|
+
}
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
module.exports._getProcessorStats(function getProcessCB(processorStats) {
|
|
@@ -88,16 +90,16 @@ function fetchSystemInfo(agent, callback) {
|
|
|
88
90
|
systemInfo.kernelVersion = kernelVersion
|
|
89
91
|
finishedResponse()
|
|
90
92
|
})
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
systemInfo.
|
|
94
|
-
id: containerId
|
|
95
|
-
}
|
|
93
|
+
utilization.getVendors(agent, function getVendorInfo(err, vendors) {
|
|
94
|
+
if (vendors) {
|
|
95
|
+
systemInfo.vendors = vendors
|
|
96
96
|
}
|
|
97
97
|
finishedResponse()
|
|
98
98
|
})
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
getBootId(agent, function reportBootId(err, bootId) {
|
|
100
|
+
if (bootId) {
|
|
101
|
+
systemInfo.bootId = bootId
|
|
102
|
+
}
|
|
101
103
|
finishedResponse()
|
|
102
104
|
})
|
|
103
105
|
}
|
|
@@ -136,11 +138,11 @@ module.exports._getProcessorStats = function getProcessorStats(callback) {
|
|
|
136
138
|
callback(processorStats)
|
|
137
139
|
})
|
|
138
140
|
} else if (platform.match(/linux/i)) {
|
|
139
|
-
readProc('/proc/cpuinfo', function parseProc(data) {
|
|
141
|
+
readProc('/proc/cpuinfo', function parseProc(err, data) {
|
|
140
142
|
callback(parseCpuInfo(data))
|
|
141
143
|
})
|
|
142
144
|
} else {
|
|
143
|
-
logger.debug('Unknown platform:
|
|
145
|
+
logger.debug('Unknown platform: %s; could not retrieve processor info', platform)
|
|
144
146
|
callback(processorStats)
|
|
145
147
|
}
|
|
146
148
|
}
|
|
@@ -156,11 +158,11 @@ module.exports._getMemoryStats = function getMemoryStats(callback) {
|
|
|
156
158
|
callback(parseInt(memory, 10) / (1024 * 1024))
|
|
157
159
|
})
|
|
158
160
|
} else if (platform.match(/linux/i)) {
|
|
159
|
-
readProc('/proc/meminfo', function parseProc(data) {
|
|
161
|
+
readProc('/proc/meminfo', function parseProc(err, data) {
|
|
160
162
|
callback(parseMemInfo(data))
|
|
161
163
|
})
|
|
162
164
|
} else {
|
|
163
|
-
logger.debug('Unknown platform:
|
|
165
|
+
logger.debug('Unknown platform: %s; could not retrieve memory info', platform)
|
|
164
166
|
callback(null)
|
|
165
167
|
}
|
|
166
168
|
}
|
|
@@ -175,27 +177,15 @@ function getKernelVersion(callback) {
|
|
|
175
177
|
callback(version)
|
|
176
178
|
})
|
|
177
179
|
} else if (platform.match(/linux/i)) {
|
|
178
|
-
readProc('/proc/version', function parseProc(data) {
|
|
180
|
+
readProc('/proc/version', function parseProc(err, data) {
|
|
179
181
|
callback(data)
|
|
180
182
|
})
|
|
181
183
|
} else {
|
|
182
|
-
logger.debug('Unknown platform
|
|
184
|
+
logger.debug('Unknown platform: %s; could not read kernel version', platform)
|
|
183
185
|
callback(null)
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
module.exports._getDockerContainerId = function getDockerContainerId(agent, callback) {
|
|
188
|
-
if (!platform.match(/linux/i)) {
|
|
189
|
-
logger.debug('Platform is not a flavor of linux, omitting docker info')
|
|
190
|
-
callback(null)
|
|
191
|
-
} else {
|
|
192
|
-
readProc('/proc/self/cgroup', function getCGroup(data) {
|
|
193
|
-
if (!data) callback(null)
|
|
194
|
-
else callback(parseDockerInfo(agent, data))
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
189
|
function getSysctlValue(names, callback) {
|
|
200
190
|
if (!names) return callback(null)
|
|
201
191
|
var returned = false
|
|
@@ -206,7 +196,7 @@ function getSysctlValue(names, callback) {
|
|
|
206
196
|
function respond(err, stdout, stderr) {
|
|
207
197
|
if (returned) return
|
|
208
198
|
if (err) {
|
|
209
|
-
logger.debug('Error when trying to run: sysctl -n
|
|
199
|
+
logger.debug('Error when trying to run: sysctl -n %s: %s', name, err.message)
|
|
210
200
|
callback(null)
|
|
211
201
|
returned = true
|
|
212
202
|
} else if (!stderr) {
|
|
@@ -214,20 +204,9 @@ function getSysctlValue(names, callback) {
|
|
|
214
204
|
returned = true
|
|
215
205
|
}
|
|
216
206
|
if (++ran === names.length && !returned) {
|
|
217
|
-
logger.debug('No sysctl info found for names: '
|
|
207
|
+
logger.debug('No sysctl info found for names: %j', names)
|
|
218
208
|
callback(null)
|
|
219
209
|
}
|
|
220
210
|
}
|
|
221
211
|
})
|
|
222
212
|
}
|
|
223
|
-
|
|
224
|
-
function readProc(path, callback) {
|
|
225
|
-
fs.readFile(path, function readProcFile(err, data) {
|
|
226
|
-
if (err) {
|
|
227
|
-
logger.error('Error when trying to read ' + path, err)
|
|
228
|
-
callback(null)
|
|
229
|
-
} else {
|
|
230
|
-
callback(data.toString())
|
|
231
|
-
}
|
|
232
|
-
})
|
|
233
|
-
}
|
|
@@ -13,10 +13,21 @@ module.exports.stub = {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A light representation of a transaction instance, returned by calling
|
|
18
|
+
* {@link API#getTransaction}.
|
|
19
|
+
*
|
|
20
|
+
* @constructor
|
|
21
|
+
*/
|
|
16
22
|
function TransactionHandle(transaction) {
|
|
17
23
|
this._transaction = transaction
|
|
18
24
|
}
|
|
19
25
|
|
|
26
|
+
/**
|
|
27
|
+
* End the transaction.
|
|
28
|
+
*
|
|
29
|
+
* @param {Function} callback
|
|
30
|
+
*/
|
|
20
31
|
TransactionHandle.prototype.end = function handleEnd(callback) {
|
|
21
32
|
if (!this._transaction.name) {
|
|
22
33
|
this._transaction.finalizeName(null) // Use existing partial name.
|
|
@@ -27,6 +38,9 @@ TransactionHandle.prototype.end = function handleEnd(callback) {
|
|
|
27
38
|
this._transaction.end(callback)
|
|
28
39
|
}
|
|
29
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Mark the transaction to be ignored.
|
|
43
|
+
*/
|
|
30
44
|
TransactionHandle.prototype.ignore = function handleIgnore() {
|
|
31
45
|
this._transaction.setForceIgnore(true)
|
|
32
46
|
}
|
package/lib/transaction/index.js
CHANGED
|
@@ -53,7 +53,8 @@ function Transaction(agent) {
|
|
|
53
53
|
agent.metricNameNormalizer
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
-
agent.activeTransactions
|
|
56
|
+
++agent.activeTransactions
|
|
57
|
+
++agent.transactionCreatedInHarvest
|
|
57
58
|
|
|
58
59
|
this.numSegments = 0
|
|
59
60
|
|
|
@@ -144,8 +145,9 @@ Transaction.prototype.end = function end(done) {
|
|
|
144
145
|
)
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
this.agent.recordSupportability('Transactions/Segments', this.numSegments)
|
|
148
149
|
|
|
150
|
+
var transaction = this
|
|
149
151
|
transaction.trace.end()
|
|
150
152
|
process.nextTick(function nextTickedEnd() {
|
|
151
153
|
// recorders must be run before the trace is collected
|
|
@@ -404,6 +406,12 @@ function finalizeNameFromUri(requestURL, statusCode) {
|
|
|
404
406
|
* the current partial name is used.
|
|
405
407
|
*/
|
|
406
408
|
Transaction.prototype.finalizeName = function finalizeName(name) {
|
|
409
|
+
// If no name is given, and this is a web transaction with a url, then
|
|
410
|
+
// finalize the name using the stored url.
|
|
411
|
+
if (name == null && this.type === 'web' && this.url) {
|
|
412
|
+
return this.finalizeNameFromUri(this.url, this.statusCode)
|
|
413
|
+
}
|
|
414
|
+
|
|
407
415
|
this._partialName = name || this._partialName
|
|
408
416
|
|
|
409
417
|
var fullName = TYPE_METRICS[this.type] + '/' + this._partialName
|
|
@@ -100,6 +100,7 @@ function transactionProxy(handler) {
|
|
|
100
100
|
transaction: {id: segment.transaction.id, name: segment.transaction.getName()},
|
|
101
101
|
segment: segment.name
|
|
102
102
|
}, 'Active transaction when creating non-nested transaction')
|
|
103
|
+
tracer.agent.recordSupportability('Transactions/Nested')
|
|
103
104
|
return handler.apply(this, arguments)
|
|
104
105
|
}
|
|
105
106
|
var transaction = new Transaction(tracer.agent)
|
package/lib/util/hashes.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto')
|
|
4
|
+
var semver = require('semver')
|
|
4
5
|
|
|
5
6
|
// TODO: Just use Buffer.from once Node <5.10.0 is deprecated.
|
|
6
|
-
var makeBuffer = Buffer.from
|
|
7
|
+
var makeBuffer = Buffer.from
|
|
8
|
+
|
|
9
|
+
if (semver.satisfies(process.version, '<5.10.0')) {
|
|
10
|
+
makeBuffer = bufferFactory
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function bufferFactory(v, enc) {
|
|
7
14
|
return new Buffer(v, enc)
|
|
8
15
|
}
|
|
9
16
|
|
package/lib/util/logger.js
CHANGED
|
@@ -129,12 +129,12 @@ Logger.prototype.child = function child(extra) {
|
|
|
129
129
|
var parent = this
|
|
130
130
|
childLogger.options = parent.options
|
|
131
131
|
|
|
132
|
-
childLogger.write = function write(level, args,
|
|
133
|
-
|
|
132
|
+
childLogger.write = function write(level, args, _extra) {
|
|
133
|
+
_extra = getPropertiesToLog(_extra)
|
|
134
134
|
var selfExtra = util._extend({}, this.extra)
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
return parent.write(level, args,
|
|
136
|
+
_extra = util._extend(selfExtra, _extra)
|
|
137
|
+
return parent.write(level, args, _extra)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
childLogger.setEnabled = Logger.prototype.setEnabled
|
|
@@ -13,7 +13,7 @@ var hex = /0x[0-9a-f]+/
|
|
|
13
13
|
var boolean = /true|false|null/
|
|
14
14
|
var number = /\b-?(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?/
|
|
15
15
|
|
|
16
|
-
var dialects = {}
|
|
16
|
+
var dialects = obfuscate.dialects = {}
|
|
17
17
|
|
|
18
18
|
dialects.mysql = [
|
|
19
19
|
replacer(join(
|
|
@@ -28,7 +28,7 @@ dialects.postgres = [
|
|
|
28
28
|
[dollarQuote, singleQuote, comment, multilineComment, uuid, boolean, number],
|
|
29
29
|
'gi'
|
|
30
30
|
)),
|
|
31
|
-
unmatchedPairs(/'
|
|
31
|
+
unmatchedPairs(/'|\/\*|\*\/|(?:\$(?!\?))/)
|
|
32
32
|
]
|
|
33
33
|
|
|
34
34
|
dialects.cassandra = [
|
|
@@ -72,13 +72,19 @@ function toPart(expressions) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function replacer(regex) {
|
|
75
|
-
|
|
75
|
+
function replace(sql) {
|
|
76
76
|
return sql.replace(regex, '?')
|
|
77
77
|
}
|
|
78
|
+
replace.regex = regex
|
|
79
|
+
|
|
80
|
+
return replace
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
function unmatchedPairs(regex) {
|
|
81
|
-
|
|
84
|
+
function check(sql) {
|
|
82
85
|
return regex.test(sql) ? '?' : sql
|
|
83
86
|
}
|
|
87
|
+
check.regex = regex
|
|
88
|
+
|
|
89
|
+
return check
|
|
84
90
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var logger = require('../logger.js').child({component: 'aws-info'})
|
|
4
|
+
var common = require('./common')
|
|
5
|
+
var NAMES = require('../metrics/names.js')
|
|
6
|
+
|
|
7
|
+
module.exports = fetchAWSInfo
|
|
8
|
+
module.exports.clearCache = function clearAWSCache() {
|
|
9
|
+
results = null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var results = null
|
|
13
|
+
|
|
14
|
+
function fetchAWSInfo(agent, callback) {
|
|
15
|
+
if (!agent.config.utilization || !agent.config.utilization.detect_aws) {
|
|
16
|
+
return setImmediate(callback, null)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (results) {
|
|
20
|
+
return setImmediate(callback, null, results)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var instanceHost = '169.254.169.254'
|
|
24
|
+
var apiVersion = '2016-09-02'
|
|
25
|
+
var endpoint = 'dynamic/instance-identity/document'
|
|
26
|
+
var url = 'http://' + instanceHost + '/' + apiVersion + '/' + endpoint
|
|
27
|
+
common.request(url, agent, function getMetadata(err, data) {
|
|
28
|
+
if (err) {
|
|
29
|
+
return callback(err)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
data = JSON.parse(data)
|
|
34
|
+
} catch (e) {
|
|
35
|
+
logger.debug(e, 'Failed to parse AWS metadata.')
|
|
36
|
+
data = null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
results = common.getKeys(data, ['availabilityZone', 'instanceId', 'instanceType'])
|
|
40
|
+
if (results == null) {
|
|
41
|
+
logger.debug('AWS metadata was invalid.')
|
|
42
|
+
agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.AWS_ERROR).incrementCallCount()
|
|
43
|
+
}
|
|
44
|
+
callback(null, results)
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var common = require('./common')
|
|
4
|
+
var logger = require('../logger.js').child({component: 'azure-info'})
|
|
5
|
+
var NAMES = require('../metrics/names.js')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = fetchAzureInfo
|
|
9
|
+
module.exports.clearCache = function clearAzureCache() {
|
|
10
|
+
results = null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
var results = null
|
|
14
|
+
|
|
15
|
+
function fetchAzureInfo(agent, callback) {
|
|
16
|
+
if (!agent.config.utilization || !agent.config.utilization.detect_azure) {
|
|
17
|
+
return setImmediate(callback, null, null)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (results) {
|
|
21
|
+
return setImmediate(callback, null, results)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var instanceHost = '169.254.169.254'
|
|
25
|
+
var apiVersion = '2017-03-01'
|
|
26
|
+
var endpoint = '/metadata/instance/compute'
|
|
27
|
+
common.request({
|
|
28
|
+
host: instanceHost,
|
|
29
|
+
path: endpoint + '?api-version=' + apiVersion,
|
|
30
|
+
headers: {Metadata: 'true'}
|
|
31
|
+
}, agent, function getMetadata(err, data) {
|
|
32
|
+
if (err) {
|
|
33
|
+
return callback(err)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Hopefully the data is parsable as JSON.
|
|
37
|
+
try {
|
|
38
|
+
data = JSON.parse(data)
|
|
39
|
+
} catch (e) {
|
|
40
|
+
logger.debug(e, 'Failed to parse Azure metadata.')
|
|
41
|
+
data = null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get out just the keys we care about.
|
|
45
|
+
results = common.getKeys(data, ['location', 'name', 'vmId', 'vmSize'])
|
|
46
|
+
if (results == null) {
|
|
47
|
+
logger.debug('Azure metadata was invalid.')
|
|
48
|
+
agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.AZURE_ERROR).incrementCallCount()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Call back!
|
|
52
|
+
callback(null, results)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var concat = require('concat-stream')
|
|
4
|
+
var http = require('http')
|
|
5
|
+
var logger = require('../logger').child({component: 'utilization-request'})
|
|
6
|
+
var fs = require('fs')
|
|
7
|
+
var properties = require('../util/properties')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.checkValueString = checkValueString
|
|
11
|
+
function checkValueString(str) {
|
|
12
|
+
if (!str || !str.length || Buffer.byteLength(str) > 255) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
var len = str.length
|
|
17
|
+
var validCharacters = /[0-9a-zA-Z_ ./-]/
|
|
18
|
+
for (var i = 0; i < len; ++i) {
|
|
19
|
+
if (str.charCodeAt(i) < 128 && !validCharacters.test(str[i])) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.getKeys = function getKeys(data, keys) {
|
|
27
|
+
if (!data) {
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var results = {}
|
|
32
|
+
for (var i = 0; i < keys.length; ++i) {
|
|
33
|
+
var key = keys[i]
|
|
34
|
+
if (!properties.hasOwn(data, key) || !data[key]) {
|
|
35
|
+
logger.debug('Key %s missing from metadata', key)
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
var value = data[key]
|
|
39
|
+
if (typeof value === 'number') {
|
|
40
|
+
value = value.toString()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If any value is invalid, the whole thing must be trashed.
|
|
44
|
+
if (!checkValueString(value)) {
|
|
45
|
+
logger.debug('Invalid metadata value found: %s -> %s', key, value)
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
results[key] = value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return results
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exports.request = function request(opts, agent, cb) {
|
|
55
|
+
var req = http.get(opts, function awsRequest(res) {
|
|
56
|
+
res.pipe(concat(respond))
|
|
57
|
+
function respond(data) {
|
|
58
|
+
agent.removeListener('errored', abortRequest)
|
|
59
|
+
agent.removeListener('stopped', abortRequest)
|
|
60
|
+
|
|
61
|
+
if (res.statusCode !== 200) {
|
|
62
|
+
logger.debug(
|
|
63
|
+
'Got %d %s from metadata request %j',
|
|
64
|
+
res.statusCode, res.statusMessage || '<n/a>', opts
|
|
65
|
+
)
|
|
66
|
+
return cb(new Error('Request for metadata failed.'))
|
|
67
|
+
} else if (!data) {
|
|
68
|
+
logger.debug('Got no response data?')
|
|
69
|
+
return cb(new Error('No reponse data received.'))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cb(null, data.toString('utf8'))
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
req.setTimeout(1000, function requestTimeout() {
|
|
77
|
+
logger.debug('Request for metadata %j timed out', opts)
|
|
78
|
+
cb(new Error('Timeout'))
|
|
79
|
+
})
|
|
80
|
+
req.on('error', function requestError(err) {
|
|
81
|
+
logger.debug('Message for metadata %j: %s', opts, err.message)
|
|
82
|
+
cb(err)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
agent.once('errored', abortRequest)
|
|
86
|
+
agent.once('stopped', abortRequest)
|
|
87
|
+
|
|
88
|
+
function abortRequest() {
|
|
89
|
+
logger.debug('Aborting request for metadata at %j', opts)
|
|
90
|
+
req.abort()
|
|
91
|
+
agent.removeListener('errored', abortRequest)
|
|
92
|
+
agent.removeListener('stopped', abortRequest)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
exports.readProc = readProc
|
|
97
|
+
function readProc(path, callback) {
|
|
98
|
+
fs.readFile(path, function readProcFile(err, data) {
|
|
99
|
+
if (err) {
|
|
100
|
+
logger.error(err, 'Error when trying to read %s', path)
|
|
101
|
+
callback(err, null)
|
|
102
|
+
} else {
|
|
103
|
+
callback(null, data.toString())
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var logger = require('../logger').child({component: 'docker-info'})
|
|
4
|
+
var common = require('./common')
|
|
5
|
+
var NAMES = require('../metrics/names')
|
|
6
|
+
var os = require('os')
|
|
7
|
+
|
|
8
|
+
module.exports.getVendorInfo = fetchDockerVendorInfo
|
|
9
|
+
module.exports.clearVendorCache = function clearDockerVendorCache() {
|
|
10
|
+
vendorInfo = null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports.getBootId = function getBootId(agent, callback) {
|
|
14
|
+
if (!/linux/i.test(os.platform())) {
|
|
15
|
+
logger.debug('Platform is not a flavor of linux, omitting boot info')
|
|
16
|
+
return setImmediate(callback, null, null)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
common.readProc('/proc/sys/kernel/random/boot_id', function readProcBootId(err, data) {
|
|
20
|
+
if (!data) {
|
|
21
|
+
bootIdError()
|
|
22
|
+
return callback(null, null)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
data = data.trim()
|
|
26
|
+
var asciiData = (new Buffer(data, 'ascii')).toString()
|
|
27
|
+
|
|
28
|
+
if (data !== asciiData) {
|
|
29
|
+
bootIdError()
|
|
30
|
+
return callback(null, null)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (data.length !== 36) {
|
|
34
|
+
bootIdError()
|
|
35
|
+
if (data.length > 128) {
|
|
36
|
+
data = data.substr(0, 128)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return callback(null, data)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
function bootIdError() {
|
|
44
|
+
agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.BOOT_ID_ERROR)
|
|
45
|
+
.incrementCallCount()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var vendorInfo = null
|
|
50
|
+
|
|
51
|
+
function fetchDockerVendorInfo(agent, callback) {
|
|
52
|
+
if (!agent.config.utilization || !agent.config.utilization.detect_docker) {
|
|
53
|
+
return callback(null, null)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (vendorInfo) {
|
|
57
|
+
return callback(null, vendorInfo)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!os.platform().match(/linux/i)) {
|
|
61
|
+
logger.debug('Platform is not a flavor of linux, omitting docker info')
|
|
62
|
+
return callback(null)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
common.readProc('/proc/self/cgroup', function getCGroup(err, data) {
|
|
66
|
+
if (!data) {
|
|
67
|
+
return callback(null)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var id = null
|
|
71
|
+
findCGroups(data, 'cpu', function forEachCpuGroup(cpuGroup) {
|
|
72
|
+
var match = /(?:^|[^0-9a-f])([0-9a-f]{64})(?:[^0-9a-f]|$)/.exec(cpuGroup)
|
|
73
|
+
if (match) {
|
|
74
|
+
id = match[1]
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return true
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if (id) {
|
|
82
|
+
vendorInfo = {id: id}
|
|
83
|
+
callback(null, vendorInfo)
|
|
84
|
+
} else {
|
|
85
|
+
logger.debug('No matching cpu group found.')
|
|
86
|
+
callback(null, null)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function findCGroups(info, cgroup, eachCb) {
|
|
92
|
+
var target = new RegExp('^\\d+:[^:]*?\\b' + cgroup + '\\b[^:]*:')
|
|
93
|
+
var lines = info.split('\n')
|
|
94
|
+
for (var i = 0; i < lines.length; ++i) {
|
|
95
|
+
var line = lines[i]
|
|
96
|
+
if (target.test(line) && !eachCb(line.split(':')[2])) {
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var logger = require('../logger.js').child({component: 'gcp-info'})
|
|
4
|
+
var common = require('./common')
|
|
5
|
+
var NAMES = require('../metrics/names.js')
|
|
6
|
+
|
|
7
|
+
module.exports = fetchGCPInfo
|
|
8
|
+
module.exports.clearCache = function clearGCPCache() {
|
|
9
|
+
resultDict = null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var resultDict
|
|
13
|
+
|
|
14
|
+
function fetchGCPInfo(agent, callback) {
|
|
15
|
+
if (!agent.config.utilization || !agent.config.utilization.detect_gcp) {
|
|
16
|
+
return setImmediate(callback, null)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (resultDict) {
|
|
20
|
+
return setImmediate(callback, null, resultDict)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
common.request({
|
|
24
|
+
host: 'metadata.google.internal',
|
|
25
|
+
path: '/computeMetadata/v1/instance/?recursive=true',
|
|
26
|
+
headers: {
|
|
27
|
+
'Metadata-Flavor': 'Google'
|
|
28
|
+
}
|
|
29
|
+
}, agent, function getMetadata(err, data) {
|
|
30
|
+
if (err) {
|
|
31
|
+
return callback(err)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
data = JSON.parse(data)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
logger.debug(e, 'Failed to parse GCP metadata.')
|
|
38
|
+
data = null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var results = common.getKeys(data, ['id', 'machineType', 'name', 'zone'])
|
|
42
|
+
if (results == null) {
|
|
43
|
+
logger.debug('GCP metadata was invalid.')
|
|
44
|
+
agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.GCP_ERROR).incrementCallCount()
|
|
45
|
+
} else {
|
|
46
|
+
// normalize
|
|
47
|
+
results.machineType =
|
|
48
|
+
results.machineType.substr(results.machineType.lastIndexOf('/') + 1)
|
|
49
|
+
results.zone =
|
|
50
|
+
results.zone.substr(results.zone.lastIndexOf('/') + 1)
|
|
51
|
+
|
|
52
|
+
resultDict = results
|
|
53
|
+
}
|
|
54
|
+
callback(null, results)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports.getVendors = getVendors
|
|
4
|
+
function getVendors(agent, callback) {
|
|
5
|
+
var vendorMethods = {
|
|
6
|
+
aws: require('./aws-info'),
|
|
7
|
+
pcf: require('./pcf-info'),
|
|
8
|
+
azure: require('./azure-info'),
|
|
9
|
+
gcp: require('./gcp-info'),
|
|
10
|
+
docker: require('./docker-info').getVendorInfo
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
var done = 0
|
|
14
|
+
var vendors = null
|
|
15
|
+
var vendorNames = Object.keys(vendorMethods)
|
|
16
|
+
vendorNames.forEach(function getVendorInfo(vendor) {
|
|
17
|
+
vendorMethods[vendor](agent, function getInfo(err, result) {
|
|
18
|
+
if (result) {
|
|
19
|
+
vendors = vendors || {}
|
|
20
|
+
vendors[vendor] = result
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (++done === vendorNames.length) {
|
|
24
|
+
callback(null, vendors)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
}
|