Permalink
Browse files

[api test] Added `forever logs` CLI commands and `forever.tail()` met…

…hod with appropriate tests. Fixes #123, #93
  • Loading branch information...
indexzero committed Oct 9, 2011
1 parent 3d23311 commit 0d6f85fb2f243d6f1a647504dafabe91807ac5bd
Showing with 171 additions and 4 deletions.
  1. +4 −0 bin/forever
  2. +3 −0 examples/log-on-interval.js
  3. +56 −4 lib/forever.js
  4. +42 −0 lib/forever/cli.js
  5. +16 −0 test/fixtures/start-daemon.js
  6. +50 −0 test/tail-test.js
View
@@ -12,6 +12,7 @@ var action, accepts = [
'clear',
'columns',
'list',
+ 'logs',
'service-install',
'service-add',
'service-start',
@@ -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`',
@@ -85,6 +88,7 @@ function isSimpleAction() {
return [
'config',
'list',
+ 'logs',
'stopall',
'cleanlogs',
'service-install',
@@ -0,0 +1,3 @@
+setInterval(function () {
+ console.log('Logging at ' + Date.now());
+}, 100);
View
@@ -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
@@ -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);
//
@@ -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');
@@ -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
View
@@ -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
@@ -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);
+});
View
@@ -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.