Last active
August 29, 2015 14:01
-
-
Save dmcaulay/8cf953dd075c70fa1e5b to your computer and use it in GitHub Desktop.
stats for node.js and winston
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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