Skip to content

Instantly share code, notes, and snippets.

@dmcaulay
Last active August 29, 2015 14:01
Show Gist options
  • Save dmcaulay/8cf953dd075c70fa1e5b to your computer and use it in GitHub Desktop.
Save dmcaulay/8cf953dd075c70fa1e5b to your computer and use it in GitHub Desktop.
stats for node.js and winston
var os = require("os");
// winston config
var winston = require('winston');
winston.clear();
config.label = os.hostname() + ":" + process.pid;
winston.setLevels(config.levels); // make sure you have a stats level
winston.addColors(config.colors); // you need to add a stats color
winston.add(winston.transports.Console, config);
// standalone object
var stats = require('./stats').add('db');
// add 1 to the read stat
stats.addStat('read');
// add 1 to the write stat
stats.addStat('write');
// class
var stats = require('./stats');
var Db = function() {
stats.add('db', this);
// configure stats in constructor
// you can simply pass the stat name or pass options
this.wrap('write', {method: 'delete', name: 'delete:stat', timing: true})
};
Db.prototype.read = function() {
// add it manually
this.addStat('read');
};
Db.prototype.write = function(columns, values, callback) {
callback();
};
Db.prototype.remove = function(query, callback) {
callback();
};
var winston = require('winston');
var stats = {};
var addStat = function(name) {
if (!stats[this.statsId]) stats[this.statsId] = {};
if (!this.stats) this.stats = stats[this.statsId];
if (!this.stats[name]) {
this.stats[name] = 1;
} else {
this.stats[name] = this.stats[name] + 1;
}
};
var wrap = function() {
var statsList = Array.prototype.slice.call(arguments);
statsList.forEach(function(stat) {
// stats properties
var name = stat.name || stat;
var method = stat.method || stat;
var time = stat.time;
this[method] = function() {
var callback = args.pop();
var start = new Date();
var end = function() { return (new Date() - start) + 'ms'; };
var self = this;
args.push(function() {
if (time) winston.stats(end(), name);
self.addStat(name);
callback.apply(self, arguments);
});
};
});
};
var logStats = function() {
winston.stats(JSON.stringify(stats));
Object.keys(stats).forEach(function(statsId) { reset(stats[statsId]) });
};
var reset = function(stats) {
Object.keys(stats).forEach(function(key) { stats[key] = 0; });
};
setInterval(logStats, 5000);
module.exports = {
add: function(statsId, context) {
context = context || {};
context.statsId = statsId;
context.addStat = addStat;
context.wrap = wrap;
return context;
}
};
// read the stats from the log
var log = require('single-line-log').stdout;
var numeral = require('numeral');
var _ = require('underscore');
var prev = "";
var stats = {};
var logPid = process.argv.length > 2;
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(data) {
var lines = (prev + data).split('\n');
lines.forEach(function(line) {
if (/stats/.test(line)) {
var hostMatch = line.match(/rabbit-hole\d+:\d+/);
if (!hostMatch) return;
var host = hostMatch[0].split(':');
var pid = host[1];
host = host[0];
var hostStats = {};
if (stats[host]) hostStats = stats[host];
stats[host] = hostStats;
try {
var offset = hostMatch.index + hostMatch[0].length + 1;
hostStats[pid] = JSON.parse(line.slice(offset));
} catch(e) {
prev = line;
return;
}
}
});
var totals = {};
var totalTypes = {};
var totalHosts = {};
// calculate totals
Object.keys(stats).forEach(function(host) {
var hostStats = stats[host];
Object.keys(hostStats).forEach(function(pid) {
var pidStats = hostStats[pid];
Object.keys(pidStats).forEach(function(type) {
var typeStats = pidStats[type];
Object.keys(typeStats).forEach(function(name) {
if (!/^pub/.test(name)) {
if (!totalTypes[type]) totalTypes[type] = 0;
totalTypes[type] = totalTypes[type] + typeStats[name];
}
if (!totals[type]) totals[type] = {};
if (!totals[type][name]) totals[type][name] = 0;
totals[type][name] = totals[type][name] + typeStats[name];
if (!totalHosts[host]) totalHosts[host] = {};
if (!totalHosts[host][type]) totalHosts[host][type] = {};
if (!totalHosts[host][type][name]) totalHosts[host][type][name] = 0;
totalHosts[host][type][name] = totalHosts[host][type][name] + typeStats[name];
});
});
});
});
// TODO output to web clients
// output to console
var messages = [];
Object.keys(totals).forEach(function(type) {
var numString = numeral(totalTypes[type]/5).format('0,0.0');
messages.push(type + '\t' + numString);
Object.keys(totals[type]).forEach(function(name) {
if (type == 'cassandra' && totals[type][name] == 0) return;
var numString = numeral(totals[type][name]/5).format('0,0.0');
messages.push(' ' + numString + '/s \t' + name.substr(0, 65));
Object.keys(totalHosts).forEach(function(host) {
if (!totalHosts[host][type] || totalHosts[host][type][name] === undefined) return;
var numString = numeral(totalHosts[host][type][name]/5).format('0,0.0');
var hostMessage = ' ' + numString + '/s\t ' + host;
messages.push(hostMessage);
if (logPid) {
var pidMessage = ' ('
Object.keys(stats[host]).forEach(function(pid, index) {
var pidStats = stats[host][pid];
if (!pidStats[type] || pidStats[type][name] === undefined) return;
var numString = numeral(pidStats[type][name]/5).format('0,0.0');
if (index != 0) pidMessage += ',';
pidMessage += numString + '/s';
});
pidMessage += ')';
messages.push(pidMessage);
}
});
});
});
log(messages.join('\n'));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment