Skip to content

Commit 73885e8

Browse files
committed
Open-Sourcing indy!
1 parent 106a6e9 commit 73885e8

13 files changed

+534
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Vim
2+
*.swp
3+
*.swo
4+
5+
# Node
6+
node_modules
7+

.tern-project

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"libs": [
3+
],
4+
"loadEagerly": [
5+
],
6+
"plugins": {
7+
"node": {
8+
}
9+
}
10+
}

README.md

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Indy
2+
3+
Fancy having your own Dr. Henry Walton "Indiana" Jones, Jr? Now you can!
4+
5+
6+
```javascript
7+
8+
var Indy = require('indy');
9+
10+
var indy = new Indy();
11+
12+
// Defaults to our artefact custom event dispatcher.
13+
// Times eventloop delay, process info, memory info, etc.
14+
indy.whip( { /* dispatcher: new StatsD() */ } );
15+
16+
// Not sure why you would need it yet but, lets just see.
17+
indy.pause();
18+
indy.resume();
19+
20+
// Dumps a metric shit-ton of things, memory, core, yada yada ya.
21+
// Breaks things, blocks the eventloop etc. not fun.
22+
indy.extract(); // heart!
23+
24+
// We always forget this!
25+
indy.hat();
26+
27+
```
28+
29+
## Example output from the default dispatcher
30+
31+
```
32+
{
33+
"gccount": 0, // delta of gc counts between each capture
34+
"memrss": 18083840, // bytes
35+
"memfree": 721039360, // bytes
36+
"memtotal": 4076998656, // bytes
37+
"memheaptotal": 14519296, // bytes
38+
"memheapused": 5660016, // bytes
39+
"cpus": [
40+
{
41+
"model": "intel(r) core(tm) i5-3427u cpu @ 1.80ghz",
42+
"speed": 800, // Mhz
43+
"times": {
44+
"user": 11770900, // ms
45+
"nice": 1509700, // ms
46+
"sys": 4852500, // ms
47+
"idle": 44234800, // ms
48+
"irq": 700
49+
}
50+
},
51+
{
52+
"model": "intel(r) core(tm) i5-3427u cpu @ 1.80ghz",
53+
"speed": 800,
54+
"times": {
55+
"user": 9865300,
56+
"nice": 1292400,
57+
"sys": 4128400,
58+
"idle": 48376000,
59+
"irq": 0
60+
}
61+
},
62+
{
63+
"model": "intel(r) core(tm) i5-3427u cpu @ 1.80ghz",
64+
"speed": 800,
65+
"times": {
66+
"user": 11763300,
67+
"nice": 1567500,
68+
"sys": 5049500,
69+
"idle": 42676900,
70+
"irq": 0
71+
}
72+
},
73+
{
74+
"model": "intel(r) core(tm) i5-3427u cpu @ 1.80ghz",
75+
"speed": 1000,
76+
"times": {
77+
"user": 9685200,
78+
"nice": 1140400,
79+
"sys": 4383000,
80+
"idle": 45695100,
81+
"irq": 0
82+
}
83+
}
84+
],
85+
"loadavg5": 0.6865234375,
86+
"loadavg10": 1.10400390625,
87+
"loadavg15": 1.39208984375,
88+
"delay": 3.174327 //ms
89+
}
90+
```

index.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
var EventEmitter = require('events').EventEmitter;
2+
var util = require('util');
3+
var DefaultDispatcher = require('./lib/dispatcher');
4+
var async = require('async');
5+
6+
var delay = require('./lib/loop-delay');
7+
var memory = require('./lib/memory');
8+
var GC = require('./lib/gc');
9+
var cpu = require('./lib/cpu');
10+
11+
function Indy(options) {
12+
EventEmitter.call(this);
13+
14+
this.options = options || {};
15+
this.dispatcher = this.options.dispatcher || new DefaultDispatcher();
16+
this.interval = this.options.interval || 10000; // in ms
17+
this.intervalRef = undefined;
18+
this.gc = new GC();
19+
}
20+
21+
util.inherits(Indy, EventEmitter);
22+
23+
// Start watching for any events and publish them to our dispatcher.
24+
25+
Indy.prototype.whip = function () {
26+
this.gc.start();
27+
var self = this;
28+
var collect = function () {
29+
var metrics =
30+
{ delay: delay,
31+
gcCount: self.gc.delta.bind(self.gc),
32+
memRss: memory.rss,
33+
memFree: memory.free,
34+
memTotal: memory.total,
35+
memHeapTotal: memory.heapTotal,
36+
memHeapUsed: memory.heapUsed,
37+
cpus: cpu.cpus,
38+
loadavg5: cpu.loadavg5,
39+
loadavg10: cpu.loadavg10,
40+
loadavg15: cpu.loadavg15
41+
};
42+
43+
async.parallelLimit(metrics, 2, function (err, data) {
44+
if (err) {
45+
return;
46+
}
47+
self.dispatcher.dispatch(data);
48+
});
49+
};
50+
51+
self.intervalRef = setInterval(collect, self.interval);
52+
};
53+
54+
Indy.prototype.pause = function (done) {
55+
var self = this;
56+
if (!self.intervalRef) {
57+
return;
58+
}
59+
clearInterval(self.intervalRef);
60+
done();
61+
};
62+
63+
Indy.prototype.resume = function () {
64+
if (this.intervalRef) {
65+
return;
66+
}
67+
this.whip();
68+
};
69+
70+
module.exports = Indy;

lib/bench.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function Bench() {
2+
if (!(this instanceof Bench)) {
3+
return new Bench();
4+
}
5+
this.ts = 0;
6+
7+
this.start();
8+
}
9+
10+
Bench.prototype = {};
11+
12+
Bench.prototype.start = function () {
13+
this.ts = process.hrtime();
14+
};
15+
16+
Bench.prototype.elapsed = function () {
17+
var ts = process.hrtime(this.ts);
18+
// Secs into ms + ns into ms
19+
return (ts[0] * 1e3) + (ts[1] / 1e6);
20+
};
21+
22+
module.exports = Bench;

lib/cpu.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var os = require('os');
2+
var wrap = require('./util').wrap;
3+
4+
module.exports =
5+
{ cpus: wrap(os.cpus()),
6+
loadavg5: wrap(os.loadavg()[0]),
7+
loadavg10: wrap(os.loadavg()[1]),
8+
loadavg15: wrap(os.loadavg()[2])
9+
};

lib/dispatcher.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var EventEmitter = require('events').EventEmitter;
2+
var util = require('util');
3+
4+
function DefaultDispatcher(options) {
5+
if (!(this instanceof DefaultDispatcher)) {
6+
return new DefaultDispatcher(options);
7+
}
8+
EventEmitter.call(this);
9+
10+
this.options = options || {};
11+
}
12+
13+
util.inherits(DefaultDispatcher, EventEmitter);
14+
15+
DefaultDispatcher.prototype.dispatch = function (obj) {
16+
process.stdout.write(JSON.stringify(obj, true, 2));
17+
};
18+
19+
module.exports = DefaultDispatcher;

lib/gc.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
var EventEmitter = require('events').EventEmitter;
2+
var v8 = new EventEmitter();
3+
4+
try {
5+
v8 = require('tracing').v8;
6+
if (!process.env.NODE_ENV) {
7+
v8.setMaxListeners(0);
8+
}
9+
} catch (e) {
10+
console.log('Failed to load v8, not collecting GC information');
11+
}
12+
13+
// TODO: Need to decide on if we should do some internal buffering of the
14+
// object, or just send the last gc event since the last probe.
15+
// Most likely the latter as under high loads we don't want artefact or indy
16+
// to exacerbate an issue.
17+
function V8Gc() {
18+
if (!(this instanceof V8Gc)) {
19+
return new V8Gc();
20+
}
21+
this.count = 0;
22+
this.started = false;
23+
this.start();
24+
}
25+
26+
V8Gc.prototype.start = function () {
27+
if(this.started) {
28+
return false;
29+
}
30+
this.started = true;
31+
32+
var self = this;
33+
// need to ensure we don't over use this.
34+
// possible major performance bottle-neck.
35+
v8.on('gc', function onGC(before, after) {
36+
// we need to be super defensive here against changing v8 versions.
37+
// and breaking api changes to that subsystem.
38+
39+
self.count++;
40+
// undefined behaviour on the first gc.
41+
// bail out early.
42+
if (!before || !after) {
43+
return false;
44+
}
45+
46+
// increment gc counter.
47+
// notify what gc occured.
48+
// send gc sizes.
49+
50+
// This is very handy to check that a large gc has occured
51+
// and has caused a significant load on the CPU.
52+
53+
});
54+
55+
};
56+
57+
V8Gc.prototype.reset = function () {
58+
this.count = 0;
59+
};
60+
61+
V8Gc.prototype.delta = function (cb) {
62+
var delta = this.count;
63+
this.count = 0;
64+
return cb(null, delta);
65+
};
66+
67+
module.exports = V8Gc;

lib/loop-delay.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var Bench = require('./bench');
2+
3+
module.exports = function loopDelay(cb) {
4+
var bench = new Bench();
5+
setImmediate(function measure() {
6+
return cb(null, bench.elapsed());
7+
});
8+
};

lib/memory.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
var wrap = require('./util').wrap;
2+
var os = require('os');
3+
4+
// System
5+
exports.rss = wrap(process.memoryUsage().rss);
6+
exports.total = wrap(os.totalmem());
7+
exports.free = wrap(os.freemem());
8+
9+
// V8 Memory
10+
exports.heapTotal = wrap(process.memoryUsage().heapTotal);
11+
exports.heapUsed = wrap(process.memoryUsage().heapUsed);
12+

lib/util.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
exports.wrap = function wrap(result) {
2+
return function wrappedFn(cb) {
3+
return cb(null, result);
4+
};
5+
};

package.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "indy",
3+
"version": "1.0.0",
4+
"description": "discover all the artifacts!",
5+
"main": "lib/indy.js",
6+
"scripts": {
7+
"test": "./node_modules/.bin/mocha test"
8+
},
9+
"author": "Tom Gallacher <[email protected]>",
10+
"license": "Artistic-2.0",
11+
"dependencies": {
12+
"async": "^0.9.0"
13+
},
14+
"devDependencies": {
15+
"mocha": "^1.20.1"
16+
}
17+
}

0 commit comments

Comments
 (0)