123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /*!
- * Cluster - stats
- * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
- * MIT Licensed
- */
- /**
- * Module dependencies.
- */
- var fs = require('fs')
- , Log = require('log')
- , repl = require('./repl')
- , utils = require('../utils')
- , os = require('os');
- /**
- * Enable stat tracking with the given `options`.
- *
- * Options:
- *
- * - `connections` enable connection statistics
- * - `requests` enable request statistics
- * - `lightRequests` enable light-weight request statistics
- *
- * Real-time applications should utilize `lightRequests` for reporting
- * when possible, although less data is available.
- *
- * TODO: UDP
- *
- * @param {Object} options
- * @return {Function}
- * @api public
- */
- module.exports = function(options){
- options = options || {};
- stats.enableInWorker = options.connections || options.requests;
- function stats(master){
- var server = master.server;
- master.connectionStats = options.connections;
- master.requestStats = options.requests;
- master.lightRequestStats = options.lightRequests;
- // worker stats
- if (master.isWorker) {
- var id = 0;
-
- // connections
- if (options.connections) {
- server.on('connection', function(sock){
- var data = { remoteAddress: sock.remoteAddress };
- master.call('reportStats', 'connection', data);
- sock.on('close', function(){
- master.call('reportStats', 'disconnection', data);
- });
- });
- }
- // light-weight requests
- if (options.lightRequests) {
- utils.unshiftListener(server, 'request', function(req, res){
- master.call('reportStats', 'light request', res.id = ++id);
- var end = res.end;
- res.end = function(str, encoding){
- res.end = end;
- res.end(str, encoding);
- master.call('reportStats', 'light request complete', res.id);
- };
- });
- }
- // requests
- if (options.requests) {
- utils.unshiftListener(server, 'request', function(req, res){
- var data = {
- remoteAddress: req.socket.remoteAddress
- , headers: req.headers
- , httpVersion: req.httpVersion
- , method: req.method
- , url: req.url
- , id: ++id
- };
- master.call('reportStats', 'request', data);
- var end = res.end;
- res.end = function(str, encoding){
- res.end = end;
- res.end(str, encoding);
- master.call('reportStats', 'request complete', data);
- };
- });
- }
- // master stats
- } else {
- master.stats = {
- start: new Date
- , restarts: 0
- , workersSpawned: 0
- , workersKilled: 0
- };
- // 0.4.x
- if (os) {
- master.stats.totalmem = os.totalmem();
- master.stats.freemem = os.freemem();
- }
- // worker stats
- master.reportStats = function(worker, type, data){
- master.emit('client ' + type, worker, data);
- switch (type) {
- case 'connection':
- worker.stats.connectionsTotal++;
- worker.stats.connectionsActive++;
- break;
- case 'disconnection':
- worker.stats.connectionsActive--;
- break;
- case 'light request':
- case 'request':
- worker.stats.requestsTotal++;
- }
- };
- // total workers spawned
- master.on('worker', function(worker){
- ++master.stats.workersSpawned;
- worker.stats = {
- start: new Date
- , connectionsTotal: 0
- , connectionsActive: 0
- , requestsTotal: 0
- };
- });
- // total worker deaths
- master.on('worker killed', function(worker){
- ++master.stats.workersKilled;
- });
- // restarting
- master.on('restarting', function(data){
- ++master.stats.restarts;
- data.stats = master.stats;
- });
- // restart
- master.on('restart', function(data){
- master.stats = data.stats;
- master.stats.start = new Date(master.stats.start);
- });
- }
- }
- return stats;
- };
- /**
- * REPL statistics command.
- */
- repl.define('stats', function(master, sock){
- var active = master.children.length
- , total = master.stats.workersSpawned
- , deaths = master.stats.workersKilled
- , restarts = master.stats.restarts;
- // master stats
- sock.title('Master');
- if (os) sock.row('os', os.type() + ' ' + os.release());
- sock.row('state', master.state);
- sock.row('started', master.stats.start.toUTCString());
- sock.row('uptime', utils.formatDateRange(new Date, master.stats.start));
- sock.row('restarts', restarts);
- sock.row('workers', active);
- sock.row('deaths', deaths);
- // resources
- if (os) {
- sock.title('Resources');
- sock.row('load average', os.loadavg().map(function(n){ return n.toFixed(2); }).join(' '));
- sock.row('cores utilized', active + ' / ' + os.cpus().length);
- var free = utils.formatBytes(master.stats.freemem);
- var total = utils.formatBytes(master.stats.totalmem);
- sock.row('memory at boot (free / total)', free + ' / ' + total);
- var free = utils.formatBytes(os.freemem());
- var total = utils.formatBytes(os.totalmem());
- sock.row('memory now (free / total)', free + ' / ' + total);
- }
- // worker stats
- sock.title('Workers');
- // connections
- if (master.connectionStats) {
- sock.row('connections total', sum(master.children, 'connectionsTotal'));
- sock.row('connections active', sum(master.children, 'connectionsActive'));
- }
- // requests
- if (master.requestStats) {
- sock.row('requests total', sum(master.children, 'requestsTotal'));
- }
- master.children.forEach(function(worker){
- var stats = ''
- , piped = [];
- // uptime
- stats += utils.formatDateRange(new Date, worker.stats.start);
- // connections
- if (master.connectionStats) {
- piped.push(worker.stats.connectionsActive);
- piped.push(worker.stats.connectionsTotal);
- }
- // requests
- if (master.requestStats) {
- piped.push(worker.stats.requestsTotal);
- }
- if (piped.length) {
- stats += ' ' + piped.join('\033[90m|\033[0m');
- }
- sock.row(worker.id, stats);
- });
- sock.write('\n');
- }, 'Display server statistics');
- /**
- * Return sum of each `prop` in `arr`.
- *
- * @param {Array} arr
- * @param {String} prop
- * @return {Number}
- * @api private
- */
- function sum(arr, prop){
- return arr.reduce(function(sum, obj){
- return sum + obj.stats[prop];
- }, 0);
- };
|