Skip to content

Commit fc22117

Browse files
committed
fix(metrics): load metics instead of cpu
1 parent 5f781b4 commit fc22117

4 files changed

Lines changed: 123 additions & 16 deletions

File tree

lib/agent/metrics/apm/index.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var BYTES_TO_MEGABYTES = 1024 * 1024
66

77
function ApmMetrics (options) {
88
var _this = this
9+
this.cpuCount = os.cpus().length
910
this.pid = process.pid
1011
this.collectorApi = options.collectorApi
1112
this.config = options.config
@@ -47,7 +48,7 @@ ApmMetrics.prototype.sendMetrics = function () {
4748
var databag = {
4849
timestamp: (new Date()).toISOString(),
4950
memory: this.getMemory(),
50-
cpu: this.getCpu()
51+
cpu: this.getLoad()
5152
}
5253

5354
if (eventloop.stats) {
@@ -72,9 +73,14 @@ ApmMetrics.prototype.getMemory = function () {
7273
}
7374
}
7475

75-
ApmMetrics.prototype.getCpu = function () {
76+
ApmMetrics.prototype.getLoad = function () {
77+
if (this.config.isRunningInVm) {
78+
return {
79+
utilization: null
80+
}
81+
}
7682
return {
77-
utilization: Math.floor(os.loadavg()[0])
83+
utilization: Math.floor(os.loadavg()[0] * 100 / this.cpuCount)
7884
}
7985
}
8086

lib/agent/metrics/apm/index.spec.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('The ApmMetrics module', function () {
99
var activeRequests = 3
1010
var activeHandlers = 5
1111
var ISOString = 'date-string'
12-
var loadAvg = 3.3
12+
var loadAvg = 3
1313
var memoryUsed = 24000000
1414
var memoryTotal = 12000000
1515
var memoryRss = 15000000
@@ -56,6 +56,8 @@ describe('The ApmMetrics module', function () {
5656
return [loadAvg]
5757
})
5858

59+
this.sandbox.stub(apmMetrics, 'cpuCount', 2)
60+
5961
apmMetrics.sendMetrics()
6062

6163
expect(collectorApi.sendApmMetrics).to.be.calledWith({
@@ -70,7 +72,7 @@ describe('The ApmMetrics module', function () {
7072
stats: _eventLoopStats
7173
},
7274
cpu: {
73-
utilization: Math.floor(loadAvg)
75+
utilization: 150
7476
},
7577
gc: {
7678
time: 0,
@@ -80,4 +82,22 @@ describe('The ApmMetrics module', function () {
8082
timestamp: ISOString
8183
})
8284
})
85+
86+
it('skips utilization if running in VM', function () {
87+
var collectorApi = {
88+
sendApmMetrics: this.sandbox.spy()
89+
}
90+
91+
var apmMetrics = ApmMetrics.create({
92+
collectorApi: collectorApi,
93+
config: {
94+
collectInterval: 1,
95+
isRunningInVm: true
96+
}
97+
})
98+
99+
expect(apmMetrics.getLoad()).to.eql({
100+
utilization: null
101+
})
102+
})
83103
})

lib/utils/configReader.js

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var url = require('url')
66
var debug = require('debug')('risingstack/trace')
77
var defaults = require('lodash.defaults')
88
var format = require('util').format
9+
var fs = require('fs')
910

1011
function ConfigReader (config) {
1112
this.parameterConfig = config || { }
@@ -81,21 +82,70 @@ ConfigReader.prototype._readConfigFile = function (file) {
8182

8283
ConfigReader.prototype._getFileConfig = function (file) {
8384
if (file) {
85+
try {
86+
fs.statSync(file)
87+
} catch (ex) {
88+
debug('Configuration file not found')
89+
return { }
90+
}
91+
8492
try {
8593
return this._readConfigFile(path.resolve(file))
8694
} catch (ex) {
87-
if (ex.code === 'MODULE_NOT_FOUND') {
88-
debug('Configuration file not found')
89-
return { }
90-
} else {
91-
throw new Error('Invalid trace.config.js configuration file')
92-
}
95+
throw new Error('Invalid trace.config.js configuration file')
9396
}
9497
} else /* no file path given */ {
9598
return { }
9699
}
97100
}
98101

102+
ConfigReader.prototype._getVmConfig = function (name) {
103+
var vmConfig = {}
104+
var cGroupContent = this.readProcFile('/self/cgroup')
105+
var isRunningInVm
106+
107+
if (cGroupContent) {
108+
isRunningInVm = this.isRunningInVm(cGroupContent)
109+
}
110+
111+
vmConfig.isRunningInVm = isRunningInVm
112+
113+
return vmConfig
114+
}
115+
116+
ConfigReader.prototype.isRunningInVm = function (cGroupFile) {
117+
var cGroupLines = cGroupFile.split('\n')
118+
var patterns = [
119+
/^\/docker/,
120+
/^\/lxc/
121+
]
122+
var parts
123+
var i
124+
var j
125+
for (i = 0; i < cGroupLines.length; i++) {
126+
parts = cGroupLines[i].split(':')
127+
if (parts.length !== 3) {
128+
continue
129+
}
130+
for (j = 0; j < patterns.length; j++) {
131+
if (parts[2].match(patterns[i])) {
132+
return true
133+
}
134+
}
135+
}
136+
return false
137+
}
138+
139+
ConfigReader.prototype.readProcFile = function (name) {
140+
var procFileContent
141+
try {
142+
procFileContent = fs.readFileSync('/proc' + name, 'utf-8')
143+
} catch (ex) {
144+
debug('Cannot read procfile', name, ex)
145+
}
146+
return procFileContent
147+
}
148+
99149
ConfigReader.prototype.checkApiToken = function (token) {
100150
var bearerTokenParts
101151

@@ -123,10 +173,13 @@ ConfigReader.prototype.getConfig = function () {
123173

124174
var fileConfig = this._getFileConfig(configFilePath)
125175

176+
var vmConfig = this._getVmConfig()
177+
126178
config = defaults(
127179
config,
128180
fileConfig,
129181
cloudFoundryConfig,
182+
vmConfig,
130183
defaultConfig
131184
)
132185

lib/utils/configReader.spec.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
var expect = require('chai').expect
22

3+
var fs = require('fs')
4+
35
var ConfigReader = require('./configReader')
46

57
describe('Config Reader module', function () {
@@ -187,6 +189,25 @@ describe('Config Reader module', function () {
187189
delete process.env.VCAP_SERVICES
188190
})
189191

192+
it('loads VM specific config (LXC/Docker)', function () {
193+
this.sandbox.stub(fs, 'readFileSync', function () {
194+
return '11:memory:/docker'
195+
})
196+
197+
var configReader = ConfigReader.create({
198+
serviceName: 'test',
199+
reporter: 'dummy',
200+
collectorApiUrl: 'http://c.a.b',
201+
apiKey: testApiToken
202+
})
203+
204+
this.sandbox.stub(configReader, '_getDefaultConfig', function () {
205+
return { configPath: 'default' }
206+
})
207+
208+
expect(configReader.getConfig().isRunningInVm).to.eql(true)
209+
})
210+
190211
it('can find config file by default config', function () {
191212
var configReader = ConfigReader.create({
192213
serviceName: 'test',
@@ -255,16 +276,11 @@ describe('Config Reader module', function () {
255276

256277
it('throws readable error on loading invalid config file', function () {
257278
var configReader = ConfigReader.create({ serviceName: 'test', reporter: 'dummy', configPath: 'test' })
258-
var readConfigFileStub = this.sandbox.stub(configReader, '_readConfigFile', function () {
259-
// simulates an error
260-
throw new Error()
261-
})
262279

263280
try {
264281
configReader.getConfig()
265282
} catch (ex) {
266283
expect(ex).to.be.eql(new Error('Invalid trace.config.js configuration file'))
267-
expect(readConfigFileStub).to.have.been.calledOnce
268284
return
269285
}
270286

@@ -287,6 +303,18 @@ describe('Config Reader module', function () {
287303
this.sandbox.stub(configReader, '_readConfigFile', function () {
288304
return { ignoreHeaders: wellformed }
289305
})
306+
307+
this.sandbox.stub(configReader, '_getDefaultConfig', function () {
308+
return {
309+
test: 'default',
310+
collectorApiUrl: 'http://c.a.b'
311+
}
312+
})
313+
314+
this.sandbox.stub(fs, 'statSync', function () {
315+
return { }
316+
})
317+
290318
try {
291319
var config = configReader.getConfig()
292320
expect(config.ignoreHeaders).to.eql(wellformed)

0 commit comments

Comments
 (0)