Skip to content

Commit

Permalink
[api test] Added forever logs CLI commands and forever.tail() met…
Browse files Browse the repository at this point in the history
…hod with appropriate tests. Fixes #123, #93
  • Loading branch information
indexzero committed Oct 9, 2011
1 parent 3d23311 commit 0d6f85f
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 4 deletions.
4 changes: 4 additions & 0 deletions bin/forever
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var action, accepts = [
'clear',
'columns',
'list',
'logs',
'service-install',
'service-add',
'service-start',
Expand Down Expand Up @@ -44,6 +45,8 @@ var help = [
' config Lists all forever user configuration',
' set <key> <val> Sets the specified forever config <key>',
' clear <key> Clears the specified forever config <key>',
' logs Lists log files for all forever processes',
' logs <script|index> Tails the logs for <script|index>',
' columns add <col> Adds the specified column to the output in `forever list`',
' columns rm <col> Removed the specified column from the output in `forever list`',
' columns set <cols> Set all columns for the output in `forever list`',
Expand Down Expand Up @@ -85,6 +88,7 @@ function isSimpleAction() {
return [
'config',
'list',
'logs',
'stopall',
'cleanlogs',
'service-install',
Expand Down
3 changes: 3 additions & 0 deletions examples/log-on-interval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
setInterval(function () {
console.log('Logging at ' + Date.now());
}, 100);
60 changes: 56 additions & 4 deletions lib/forever.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,11 @@ forever.load = function (options) {
//
// Setup the incoming options with default options.
//
options = options || {};
options.root = options.root || forever.root;
options.pidPath = options.pidPath || path.join(options.root, 'pids');
options.sockPath = options.sockPath || path.join(options.root, 'sock');
options = options || {};
options.loglength = options.loglength || 100;
options.root = options.root || forever.root;
options.pidPath = options.pidPath || path.join(options.root, 'pids');
options.sockPath = options.sockPath || path.join(options.root, 'sock');

//
// If forever is initalized and the config directories are identical
Expand Down Expand Up @@ -194,6 +195,7 @@ forever.load = function (options) {
forever.config.set('root', options.root);
forever.config.set('pidPath', options.pidPath);
forever.config.set('sockPath', options.sockPath);
forever.config.set('loglength', options.loglength);
forever.config.set('columns', options.columns);

//
Expand Down Expand Up @@ -305,6 +307,7 @@ forever.start = function (script, options) {
// Starts a script with forever as a daemon
//
forever.startDaemon = function (script, options) {
options = options || {};
options.uid = options.uid || forever.randomString(24);
options.logFile = forever.logFilePath(options.logFile || options.uid + '.log');
options.pidFile = forever.pidFilePath(options.pidFile || options.uid + '.pid');
Expand Down Expand Up @@ -582,6 +585,55 @@ forever.list = function (format, callback) {
});
};

//
// ### function tail (target, length, callback)
// #### @target {string} Target script to list logs for
// #### @length {number} **Optional** Length of the logs to tail.
// #### @callback {function} Continuation to respond to when complete.
// Responds with the latest `length` logs for the specified `target` process
// managed by forever. If no `length` is supplied then `forever.config.get('loglength`)`
// is used.
//
forever.tail = function (target, length, callback) {
if (!callback && typeof length === 'function') {
callback = length;
length = 0;
}

length = length || forever.config.get('loglength');
if (!length) {
return callback(new Error('Cannot tail logs without a specified length'));
}

function tailProcess(proc, next) {
exec('tail -n ' + [length, proc.logFile].join(' '), function (err, stdout) {
if (err) {
return next(err);
}

proc.logs = stdout.split('\n');
proc.logs.pop();

return err ? next(err) : next(null, proc);
});
}

getAllProcesses(function (processes) {
if (!processes) {
return callback(new Error('Cannot find forever process: ' + target));
}

var procs = forever.findByIndex(target, processes)
|| forever.findByScript(target, processes);

async.mapSeries(procs, tailProcess, function (err, procs) {
return err
? callback(err)
: callback(null, procs);
});
});
};

//
// ### function format (format, procs)
// #### @format {Boolean} Value indicating if processes should be formatted
Expand Down
42 changes: 42 additions & 0 deletions lib/forever/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,48 @@ cli.clear = function (key) {
});
};

//
// ### function logs (target)
// #### @target {string} **Optional** Target script or index to list logs for
// Displays the logs using `tail` for the specified `target`. If no `target` is
// specified then it will display log files for all running forever processes.
//
cli.logs = function (index) {
//
// Helper function for listing all log files
//
function listFiles() {
var index = 0,
rows = [[' ', 'script', 'logfile']];

forever.list(false, function (err, processes) {
forever.log.info('Logs for running Forever processes');
rows = rows.concat(processes.map(function (proc) {
return ['[' + index++ + ']', proc.file.grey, proc.logFile.magenta];
}));

cliff.putRows('data', rows, ['white', 'grey', 'magenta']);
});
}

if (typeof index === 'undefined') {
return listFiles();
}

forever.tail(index, function (err, logs) {
if (err) {
return forever.log.error(err.message);
}

logs.forEach(function (proc) {
forever.log.info('Showing logs for ' + proc.file.magenta);
proc.logs.forEach(function (line) {
forever.log.data(line);
});
});
});
};

//
// ### function columns (action, value)
// #### @action {string} The subaction to execute
Expand Down
16 changes: 16 additions & 0 deletions test/fixtures/start-daemon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* start-daemon.js: Simple test fixture for spawning log-on-interval.js as a daemon
*
* (C) 2010 Nodejitsu Inc.
* MIT LICENCE
*
*/

var path = require('path'),
forever = require('../../lib/forever');

var monitor = forever.startDaemon(path.join(__dirname, '..', '..', 'examples', 'log-on-interval.js'));

monitor.on('start', function () {
forever.startServer(monitor);
});
50 changes: 50 additions & 0 deletions test/tail-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* forever-test.js: Tests for forever module
*
* (C) 2010 Nodejitsu Inc.
* MIT LICENCE
*
*/

var assert = require('assert'),
path = require('path'),
spawn = require('child_process').spawn,
vows = require('vows'),
forever = require('../lib/forever'),
helpers = require('./helpers');

vows.describe('forever/tail').addBatch({
"When using forever": {
"the tail() method": {
topic: function () {
var that = this;

that.child = spawn('node', [path.join(__dirname, 'fixtures', 'start-daemon.js')]);
setTimeout(function () {
forever.tail(0, that.callback);
}, 2000);
},
"should respond with logs for the script": function (err, procs) {
assert.isNull(err);
assert.isArray(procs);
procs.forEach(function (proc) {
assert.isArray(proc.logs);
assert.isTrue(!!proc.logs.length);
assert.isTrue(proc.logs.length > 10);
})
}
}
}
}).addBatch({
"When the tests are over": {
"stop all forever processes": {
topic: function () {
forever.stopAll().on('stopAll', this.callback.bind(null, null));
},
"should stop the correct number of procs": function (err, procs) {
assert.isArray(procs);
assert.length(procs, 1);
}
}
}
}).export(module);

0 comments on commit 0d6f85f

Please sign in to comment.