Skip to content

Commit

Permalink
added and cli options for streaming log output, updated README.md and…
Browse files Browse the repository at this point in the history
… tests to reflect changes
  • Loading branch information
jlank authored and indexzero committed Apr 21, 2013
1 parent 94f61f5 commit 1e4b2f6
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 51 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ A simple CLI tool for ensuring that a given script runs continuously (i.e. forev
$ [sudo] npm install forever -g
```

**Note:** If you are using forever _programatically_ you should install [forever-monitor][0].
**Note:** If you are using forever _programatically_ you should install [forever-monitor][0].

``` bash
$ cd /path/to/your/project
$ [sudo] npm install forever-monitor
```

## Usage
There are two distinct ways to use forever: through the command line interface, or by requiring the forever module in your own code. **Note:** If you are using forever _programatically_ you should install [forever-monitor][0].
There are two distinct ways to use forever: through the command line interface, or by requiring the forever module in your own code. **Note:** If you are using forever _programatically_ you should install [forever-monitor][0].

### Using forever from the command line
You can use forever to run any kind of script continuously (whether it is written in node.js or not). The usage options are simple:

```
$ forever --help
usage: forever [options] [action] SCRIPT [script-options]
usage: forever [action] [options] SCRIPT [script-options]
Monitors the script specified in the current process or as a daemon
Expand Down Expand Up @@ -52,6 +52,8 @@ You can use forever to run any kind of script continuously (whether it is writte
-p PATH Base path for all forever related files (pid files, etc.)
-c COMMAND COMMAND to execute (defaults to node)
-a, --append Append logs
-f, --fifo Stream logs to stdout
-n, --number Number of log lines to print
--pidFile The pid file
--sourceDir The source directory for which SCRIPT is relative to
--minUptime Minimum uptime (millis) for a script to not be considered "spinning"
Expand Down Expand Up @@ -112,8 +114,11 @@ Stops all forever scripts currently running. This method returns an EventEmitter
### forever.list (format, callback)
Returns a list of metadata objects about each process that is being run using forever. This method is synchronous and will return the list of metadata as such. Only processes which have invoked `forever.startServer()` will be available from `forever.list()`

### forever.tail (target, [length,] callback)
Responds with the logs from the target script(s) from `tail`. If `length` is provided it is used as the `-n` parameter to `tail`.
### forever.tail (target, options, callback)
Responds with the logs from the target script(s) from `tail`. There are two important options:

* `length` (numeric): is is used as the `-n` parameter to `tail`.
* `stream` (boolean): is is used as the `-f` parameter to `tail`.

### forever.cleanUp ()
Cleans up any extraneous forever *.pid files that are on the target system. This method returns an EventEmitter that raises the 'cleanUp' event when complete.
Expand All @@ -129,8 +134,8 @@ Removes all log files from the root forever directory that do not belong to curr

#### License: MIT
#### Author: [Charlie Robbins](http://github.com/indexzero)
#### Contributors: [Fedor Indutny](http://github.com/indutny), [James Halliday](http://substack.net/), [Charlie McConnell](http://github.com/avianflu), [Maciej Malecki](http://github.com/mmalecki)
#### Contributors: [Fedor Indutny](http://github.com/indutny), [James Halliday](http://substack.net/), [Charlie McConnell](http://github.com/avianflu), [Maciej Malecki](http://github.com/mmalecki), [John Lancaster](http://jlank.com)

[0]: http://github.com/nodejitsu/forever-monitor
[1]: http://github.com/nodejitsu/forever-monitor/tree/master/examples
[2]: https://github.com/nodejitsu/forever/blob/master/lib/forever/cli.js
[2]: https://github.com/nodejitsu/forever/blob/master/lib/forever/cli.js
80 changes: 58 additions & 22 deletions lib/forever.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ forever.load = function (options) {
//
options = options || {};
options.loglength = options.loglength || 100;
options.logstream = options.logstream || false;
options.root = options.root || forever.root;
options.pidPath = options.pidPath || path.join(options.root, 'pids');
options.sockPath = options.sockPath || path.join(options.root, 'sock');
Expand Down Expand Up @@ -260,6 +261,7 @@ forever.load = function (options) {
forever.config.set('pidPath', options.pidPath);
forever.config.set('sockPath', options.sockPath);
forever.config.set('loglength', options.loglength);
forever.config.set('logstream', options.logstream);
forever.config.set('columns', options.columns);

//
Expand Down Expand Up @@ -514,33 +516,71 @@ 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.
// #### @options {length|stream} **Optional** Length of the logs to tail, boolean stream
// #### @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;
forever.tail = function (target, options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options.length = 0;
options.stream = false;
}

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

function tailProcess(proc, next) {
exec('tail -n ' + [length, proc.logFile].join(' '), function (err, stdout) {
if (err) {
return next(err);
}
var that = this,
blanks = function (e, i, a) { return e !== '' },
title = function (e, i, a) { return e.match(/^==>/) },
args = ['-n', length],
logs;

if (stream) args.unshift('-f');

function tailProcess(procs, next) {
var map = {},
count = 0;

proc.logs = stdout.split('\n');
proc.logs.pop();
procs.forEach(function (proc) {
args.push(proc.logFile);
map[proc.logFile] = { pid: proc.pid, file: proc.file };
count++;
});

return err ? next(err) : next(null, proc);
tail = spawn('tail', args, {
stdio: [null, 'pipe', 'pipe'],
});

tail.stdio[1].setEncoding('utf8');
tail.stdio[2].setEncoding('utf8');

tail.stdio[1].on('data', function (data) {
chunk = data.split('\n\n');
chunk.forEach(function (logs) {
var logs = logs.split('\n').filter(blanks),
file = logs.filter(title),
lines,
proc;

file.length
? proc = map[file[0].split(' ')[1]]
: proc = map[procs[0].logFile];

count === 1
? lines = logs
: lines = logs.slice(1);

lines.forEach(function (line) {
callback(null, { file: proc.file, pid: proc.pid, line: line });
});
});
});

tail.stdio[2].on('data', function (err) {
return callback(err);
});
}

Expand All @@ -556,11 +596,7 @@ forever.tail = function (target, length, callback) {
return callback(new Error('No logs available for process: ' + target));
}

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

Expand Down
19 changes: 12 additions & 7 deletions lib/forever/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var help = [
' -p PATH Base path for all forever related files (pid files, etc.)',
' -c COMMAND COMMAND to execute (defaults to node)',
' -a, --append Append logs',
' -f, --fifo Stream logs to stdout',
' -n, --number Number of log lines to print',
' --pidFile The pid file',
' --sourceDir The source directory for which SCRIPT is relative to',
' --minUptime Minimum uptime (millis) for a script to not be considered "spinning"',
Expand Down Expand Up @@ -92,6 +94,8 @@ var argvOptions = cli.argvOptions = {
'errFile': {alias: 'e'},
'logFile': {alias: 'l'},
'append': {alias: 'a', boolean: true},
'fifo': {alias: 'f', boolean: false},
'number': {alias: 'n'},
'max': {alias: 'm'},
'outFile': {alias: 'o'},
'path': {alias: 'p'},
Expand Down Expand Up @@ -395,17 +399,18 @@ app.cmd('clear :key', cli.clear = function (key) {
// Displays the logs using `tail` for the specified `target`.
//
app.cmd('logs :index', cli.logs = function (index) {
forever.tail(index, function (err, logs) {
var options = {
stream: app.argv.fifo,
length: app.argv.number
};

forever.tail(index, options, function (err, log) {
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);
});
});
forever.log.data(log.file.magenta + ':' + log.pid + ' - ' + log.line);

});
});

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
"timespan": "2.0.1",
"watch": "0.5.1",
"utile": "0.1.2",
"winston": "0.6.2",
"event-stream": "3.0.2"
"winston": "0.6.2"
},
"devDependencies": {
"broadway": "0.2.x",
Expand Down
19 changes: 6 additions & 13 deletions test/core/tail-stopall-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,18 @@ vows.describe('forever/core/tail').addBatch({
"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);
forever.tail(0, { length: 1 }, 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);
// Temporary hack, remove after warnings are gone.
proc.logs = proc.logs.slice(1);
proc.logs.forEach(function (line) {
assert.match(line, /^Logging at/);
});
});
assert.equal(typeof procs, 'object');
assert.ok(procs.file);
assert.ok(procs.pid);
assert.ok(procs.line);
}
}
}
Expand Down

0 comments on commit 1e4b2f6

Please sign in to comment.