diff --git a/docs/docco.css b/docs/docco.css index 96bb30a9..bd541343 100644 --- a/docs/docco.css +++ b/docs/docco.css @@ -123,7 +123,7 @@ table td { } pre, tt, code { font-size: 12px; line-height: 18px; - font-family: Monaco, Consolas, "Lucida Console", monospace; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; margin: 0; padding: 0; } diff --git a/docs/forever.html b/docs/forever.html index e855edb5..b555a25e 100644 --- a/docs/forever.html +++ b/docs/forever.html @@ -1,7 +1,7 @@ - forever.js
Jump To …

forever.js

/*
+      forever.js           

forever.js

/*
  * forever.js: Top level include for the forever module
  *
- * (C) 2010 and Charlie Robbins
+ * (C) 2010 Charlie Robbins
  * MIT LICENCE
  *
  */
@@ -15,13 +15,16 @@
     events = require('events'),
     exec = require('child_process').exec,
     timespan = require('timespan'),
+    nconf = require('nconf'),
     daemon = require('daemon');
 
-var forever = exports, config;

Export Components / Settings

+var forever = exports;

Export Components / Settings

Export version and important Prototypes from lib/forever/*

forever.version     = [0, 4, 0];
 forever.initialized = false;
-forever.root        = path.join('/tmp', 'forever');
+forever.root        = path.join(process.env.HOME, '.forever');
+forever.config      = new nconf.stores.File({ file: path.join(forever.root, 'config.json') });
+forever.cli         = require('forever/cli');
 forever.Forever     = forever.Monitor = require('forever/monitor').Monitor; 

function load (options, [callback])

@options {Object} Options to load into the forever module

@@ -30,13 +33,21 @@

@options {Object} Options to load into the forever module

options.root = options.root || forever.root, options.pidPath = options.pidPath || path.join(options.root, 'pids');

If forever is initalized and the config directories are identical -simply return without creating directories

  if (forever.initialized && forever.config.root === options.root && 
-    forever.config.pidPath === options.pidPath) {
+simply return without creating directories

  if (forever.initialized && forever.config.get('root') === options.root && 
+    forever.config.get('pidPath') === options.pidPath) {
     return;
   }
   
-  forever.config  = config = options;
-  

Syncronously create the root directory + forever.config = new nconf.stores.File({ file: path.join(options.root, 'config.json') }); +

Try to load the forever config.json from +the specified location.

  try {
+    forever.config.loadSync();
+  }
+  catch (ex) { }
+  
+  forever.config.set('root', options.root);
+  forever.config.set('pidPath', options.pidPath);
+  

Syncronously create the root directory and the pid directory for forever. Although there is an additional overhead here of the sync action. It simplifies the setup of forever dramatically.

  function tryCreate (dir) {
@@ -44,27 +55,48 @@ 

@options {Object} Options to load into the forever module

catch (ex) { } } - tryCreate(config.root); - tryCreate(config.pidPath); + tryCreate(forever.config.get('root')); + tryCreate(forever.config.get('pidPath')); +

Attempt to save the new config.json for forever

  try {
+    forever.config.saveSync();
+  }
+  catch (ex) { }
+  
   forever.initialized = true;
-};

Ensure forever will always be loaded the first time it is required.

forever.load();

function stat (logFile, script, callback)

+};

Ensure forever will always be loaded the first time it is required.

forever.load();

function stat (logFile, script, callback)

@logFile {string} Path to the log file for this script

+

@logAppend {boolean} Optional. True Prevent failure if the log file exists.

+

@script {string} Path to the target script.

@callback {function} Continuation to pass control back to

Ensures that the logFile doesn't exist and that the target script does exist before executing callback.

forever.stat = function (logFile, script, callback) {
-  fs.stat(logFile, function (err, stats) {
-    if (!err) return callback(new Error('log file ' + logFile + ' exists.'));
-    fs.stat(script, function (err, stats) {
-      if (err) return callback(new Error('script ' + script + ' does not exist.'));
-      callback(null);
+  var logAppend,
+      realCallback = callback;
+      
+  if (arguments.length === 4) {
+    logAppend = callback;
+    realCallback = arguments[3];
+  }
+
+  fs.stat(script, function (err, stats) {
+    if (err) return realCallback(new Error('script ' + script + ' does not exist.'));
+
+    if (logAppend) {
+      realCallback(null);
+      return;
+    }
+
+    fs.stat(logFile, function (err, stats) {
+      if (!err) return realCallback(new Error('log file ' + logFile + ' exists.'));
+      realCallback(null);
     });
   });
-};

function start (script, options)

+};

function start (script, options)

@script {string} Location of the script to run.

@@ -72,31 +104,32 @@

@options {Object} Configuration for forever instance.

Starts a script with forever

forever.start = function (script, options) {
   return new forever.Monitor(script, options).start();
-};

function startDaemon (script, options)

+};

function startDaemon (script, options)

@script {string} Location of the script to run.

@options {Object} Configuration for forever instance.

Starts a script with forever as a daemon

forever.startDaemon = function (script, options) {
-  options.logFile = path.join(config.root, options.logFile || 'forever.log');
-  options.pidFile = path.join(config.pidPath, options.pidFile);
+  options.logFile = forever.logFilePath(options.logFile);
+  options.pidFile = forever.pidFilePath(options.pidFile);
   var runner = new forever.Monitor(script, options);
-
-  daemon.daemonize(options.logFile, options.pidFile, function (err, pid) {
+  
+  fs.open(options.logFile, options.appendLog ? 'a+' : 'w+', function (err, fd) {
     if (err) return runner.emit('error', err);
-    

Remark: This should work, but the fd gets screwed up + + var pid = daemon.start(fd); + daemon.lock(options.pidFile);

Remark: This should work, but the fd gets screwed up with the daemon process.

process.on('exit', function () { fs.unlinkSync(options.pidFile); -});

    
-    process.pid = pid;
+});

    process.pid = pid;
     runner.start();
   });
   
   return runner;
-};

function stop (target, [format])

+};

function stop (target, [format])

@target {string} Index or script name to stop

@@ -113,8 +146,11 @@

@format {boolean} Indicated if we should CLI format the returned output.

if (procs && procs.length > 0) { procs.forEach(function (proc) { - process.kill(proc.foreverPid); - process.kill(proc.pid); + try { + process.kill(proc.foreverPid); + process.kill(proc.pid); + } + catch (ex) { } }); process.nextTick(function () { @@ -128,7 +164,7 @@

@format {boolean} Indicated if we should CLI format the returned output.

} return emitter; -};

function restart (target, format)

+};

function restart (target, format)

@target {string} Index or script name to restart

@@ -141,13 +177,13 @@

@format {boolean} Indicated if we should CLI format the returned output.

runner.on('stop', function (procs) { if (procs && procs.length > 0) { - async.forEach(procs, function (proc, next) {

We need to spawn a new process running the forever CLI + async.forEach(procs, function (proc, next) {

We need to spawn a new process running the forever CLI here because we want each process to daemonize separately without the main process running forever restart myscript.js daemonizing itself.

        var restartCommand = [
           'forever', 
           'start',
-          '-d', proc.sourceDir,

Remark: Is using a new logfile good behavior? + '-d', proc.sourceDir,

Remark: Is using a new logfile good behavior? '-l', path.basename(proc.logFile),

          proc.file,
           proc.options.join(' ')
         ].join(' ');
@@ -163,12 +199,12 @@ 

@format {boolean} Indicated if we should CLI format the returned output.

emitter.emit('error', new Error('Cannot find forever process: ' + target)); } }); -

Bubble up the error to the appropriate EventEmitter instance.

  runner.on('error', function (err) {
+  

Bubble up the error to the appropriate EventEmitter instance.

  runner.on('error', function (err) {
     emitter.emit('error', err);
   });
   
   return emitter;
-};

function findByIndex (index, processes)

+};

function findByIndex (index, processes)

@index {string} Index of the process to find.

@@ -176,7 +212,7 @@

@processes {Array} Set of processes to find in.

Finds the process with the specified index.

forever.findByIndex = function (index, processes) {
   return processes && [processes[parseInt(index)]];
-};

function findByScript (script, processes)

+};

function findByScript (script, processes)

@script {string} The name of the script to find.

@@ -184,7 +220,7 @@

@processes {Array} Set of processes to find in.

Finds the process with the specified script name.

forever.findByScript = function (script, processes) {
   return processes.filter(function (p) { return p.file === script });
-};

function stopAll (format)

+};

function stopAll (format)

@format {boolean} Value indicating if we should format output

@@ -202,7 +238,10 @@

@format {boolean} Value indicating if we should format output

cPids = pids.map(function (pid) { return pid.pid }); fPids.concat(cPids).forEach(function (pid) { - process.kill(pid); + try { + process.kill(pid); + } + catch (ex) { } }); process.nextTick(function () { @@ -216,28 +255,32 @@

@format {boolean} Value indicating if we should format output

} return emitter; -};

function list (format)

+};

function list (format, procs)

@format {boolean} If set, will return a formatted string of data

+

@procs {Array} Set of processes to list format.

+

Returns the list of all process data managed by forever.

forever.list = function (format, procs) {
-  var formatted = [], procs = procs || getAllProcesses();
+  var formatted = [];
+  
+  procs = procs || getAllProcesses();
   if (!procs) return null;
   
   if (format) {
-    var index = 0, maxLen = 0;

Iterate over the procs to see which has the longest options string

    procs.forEach(function (proc) {
+    var index = 0, maxLen = 0;

Iterate over the procs to see which has the longest options string

    procs.forEach(function (proc) {
       proc.length = [proc.file].concat(proc.options).join(' ').length;
       if (proc.length > maxLen) maxLen = proc.length;
     });
     
-    procs.forEach(function (proc) {

Create padding string to keep output aligned

      var padding = new Array(maxLen - proc.length + 1).join(' ');
+    procs.forEach(function (proc) {

Create padding string to keep output aligned

      var padding = new Array(maxLen - proc.length + 1).join(' ');
       formatted.push(formatProcess(proc, index, padding));
       index++;
     });
   }
   
   return format ? formatted.join('\n') : procs;
-};

function cleanUp ()

+};

function cleanUp ()

Utility function for removing excess pid and config, and log files used by forever.

forever.cleanUp = function (cleanLogs) {
@@ -253,10 +296,10 @@ 

@format {boolean} If set, will return a formatted string of data

checkProcess(proc.foreverPid, function (manager) { if (!child && !manager || proc.dead) { if (proc.pidFile) { - fs.unlink(proc.pidFile, function () {

Ignore errors

              });
+              fs.unlink(proc.pidFile, function () {

Ignore errors

              });
             }
             
-            fs.unlink(path.join(config.pidPath, proc.foreverPid + '.fvr'), function () {

Ignore errors

            });
+            fs.unlink(path.join(forever.config.get('pidPath'), proc.uid + '.fvr'), function () {

Ignore errors

            });
             
             if (cleanLogs) {
               fs.unlink(proc.logFile, function () { /* Ignore Errors */ });
@@ -278,21 +321,22 @@ 

@format {boolean} If set, will return a formatted string of data

} return emitter; -};

function cleanLogsSync (processes)

+};

function cleanLogsSync (processes)

@processes {Array} The set of all forever processes

Removes all log files from the root forever directory that do not belong to current running forever processes.

forever.cleanLogsSync = function (processes) {
-  var files = fs.readdirSync(config.root),
-      runningLogs = processes && processes.map(function (p) { return p.logFile.split('/').pop() });
+  var files = fs.readdirSync(forever.config.get('root')),
+      running = processes && processes.filter(function (p) { return p && p.logFile }),
+      runningLogs = running && running.map(function (p) { return p.logFile.split('/').pop() });
   
   files.forEach(function (file) {
     if (/\.log$/.test(file) && (!runningLogs || runningLogs.indexOf(file) === -1)) {
-      fs.unlinkSync(path.join(config.root, file));
+      fs.unlinkSync(path.join(forever.config.get('root'), file));
     }
   });
-};

function randomString (bits)

+};

function randomString (bits)

@bits {Number} Bit-length of the base64 string to return.

@@ -301,13 +345,33 @@

@bits {Number} Bit-length of the base64 string to return.

length ⌈bits/6⌉ of characters from the base64 alphabet.

forever.randomString = function (bits) {
   var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+', 
       rand, i, ret = '';
-  

in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53)

  while (bits > 0) {
-    rand = Math.floor(Math.random()*0x100000000) // 32-bit integer

base 64 means 6 bits per character, so we use the top 30 bits from rand to give 30/6=5 characters.

    for (i=26; i>0 && bits>0; i-=6, bits-=6) { 
+  

in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53)

  while (bits > 0) {
+    rand = Math.floor(Math.random()*0x100000000) // 32-bit integer

base 64 means 6 bits per character, so we use the top 30 bits from rand to give 30/6=5 characters.

    for (i=26; i>0 && bits>0; i-=6, bits-=6) { 
       ret+=chars[0x3F & rand >>> i];
     }
   }
   return ret;
-};

function checkProcess (pid, callback)

+};

function logFilePath (logFile)

+ +

@logFile {string} Log file path

+ +

Determines the full logfile path name

forever.logFilePath = function(logFile, uid) {
+  if (logFile && logFile[0] === '/') {
+    return logFile;
+  } else {
+    return path.join(forever.config.get('root'), logFile || (uid || 'forever') + '.log');
+  }
+};

function pidFilePath (pidFile)

+ +

@logFile {string} Pid file path

+ +

Determines the full pid file path name

forever.pidFilePath = function(pidFile) {
+  if (pidFile && pidFile[0] === '/') {
+    return pidFile;
+  } else {
+    return path.join(forever.config.get('pidPath'), pidFile);
+  }
+};

function checkProcess (pid, callback)

@pid {string} pid of the process to check

@@ -318,7 +382,7 @@

@callback {function} Continuation to pass control backto.

if (err) return callback(false); callback(stdout.indexOf(pid) !== -1); }); -};

function formatProcess (proc index, padding)

+};

function formatProcess (proc index, padding)

@proc {Object} Process to format

@@ -327,40 +391,37 @@

@index {Number} Index of the process in the set of all processes

@padding {string} Padding to add to the formatted output

Returns a formatted string for the process @proc at -the specified index.

function formatProcess (proc, index, padding) {

Create an array of the output we can later join

  return ['  [' + index + ']', proc.file.green]
-    .concat(proc.options.map(function (opt) { return opt.green }))
+the specified index. 

function formatProcess (proc, index, padding) {

Create an array of the output we can later join

  return ['  [' + index + ']', proc.file.grey]
+    .concat(proc.options.map(function (opt) { return opt.grey }))
     .concat([padding + '[' + proc.pid + ',', proc.foreverPid + ']'])
-    .concat(proc.logFile.magenta)
+    .concat(proc.logFile ? proc.logFile.magenta : '')
     .concat(timespan.fromDates(new Date(proc.ctime), new Date()).toString().yellow)
     .join(' ');
-};

function getAllProcess ([findDead])

+};

function getAllProcess ([findDead])

@findDead {boolean} Optional parameter that indicates to return dead procs

Returns all data for processes managed by forever.

function getAllProcesses (findDead) {
   var results = [], processes = {},
-      files = fs.readdirSync(config.pidPath);
+      files = fs.readdirSync(forever.config.get('pidPath'));
   
   if (files.length === 0) return null;
   
   files.forEach(function (file) {
     try {
-      var fullPath = path.join(config.pidPath, file),
+      var fullPath = path.join(forever.config.get('pidPath'), file),
           data = fs.readFileSync(fullPath).toString();
 
-      switch (file.match(/\.(\w{3})$/)[1]) {
-        case 'pid':
-          var pid = parseInt(data);
-          if (!processes[pid]) processes[pid] = { foreverPid: pid };
-          break;
-
-        case 'fvr':
+      switch (path.extname(file)) {

case '.pid': + var pid = parseInt(data); + if (!processes[pid]) processes[pid] = { foreverPid: pid }; + break;

        case '.fvr':
           var child = JSON.parse(data);
-          processes[child.foreverPid] = child;
+          processes[child.pid] = child;
           break;
       }
     }
-    catch (ex) {

Ignore errors

    }
+    catch (ex) {

Ignore errors

    }
   });
   
   Object.keys(processes).forEach(function (key) {
@@ -370,7 +431,7 @@ 

@findDead {boolean} Optional parameter that indicates to return dead procs}); return results; -};

function getAllPids ()

+};

function getAllPids ()

Returns the set of all pids managed by forever. e.x. [{ pid: 12345, foreverPid: 12346 }, ...]

function getAllPids (processes) {
diff --git a/docs/forever/cli.html b/docs/forever/cli.html
new file mode 100644
index 00000000..ec5e6e87
--- /dev/null
+++ b/docs/forever/cli.html
@@ -0,0 +1,224 @@
+      cli.js           

cli.js

/*
+ * cli.js: Handlers for the forever CLI commands.
+ *
+ * (C) 2010 Charlie Robbins
+ * MIT LICENCE
+ *
+ */
+
+var sys = require('sys'),
+    path = require('path'),
+    eyes = require('eyes'),
+    winston = require('winston'),
+    forever = require('forever');
+
+var cli = exports;
+
+var reserved = ['root', 'pidPath'];
+
+var inspect = eyes.inspector({ stream: null,
+  styles: {               // Styles applied to stdout
+    all:     null,        // Overall style applied to everything
+    label:   'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]`
+    other:   'inverted',  // Objects which don't have a literal representation, such as functions
+    key:     'grey',      // The keys in object literals, like 'a' in `{a: 1}`
+    special: 'grey',      // null, undefined...
+    number:  'blue',      // 1, 2, 3, etc
+    bool:    'magenta',   // true false
+    regexp:  'green',     // /\d+/
+    string:  'yellow'
+  }
+});

function exec (action, file, options)

+ +

@action {string} CLI action to execute

+ +

@file {string} Location of the target forever script or process.

+ +

@options {Object} Options to pass to forever for the action.

+ +

Executes the action in forever with the specified file and options.

cli.exec = function (action, file, options) {
+  winston.info('Running action: ' + action.yellow);
+  
+  winston.silly('Tidying ' + forever.config.get('root'));
+  var tidy = forever.cleanUp(action === 'cleanlogs'); 
+  tidy.on('cleanUp', function () {
+    winston.silly(forever.config.get('root') + ' tidied.');
+
+    if (file && action !== 'set' && action !== 'clear') {
+      winston.info('Forever processing file: ' + file.magenta);
+    }
+
+    if (options) {
+      winston.silly('Forever using options', options);
+    }

If there is no action then start in the current +process with the specified file and options.

    if (!action) {
+      return cli.start(file, options);
+    }
+    else if (action === 'cleanlogs') {
+      return;
+    }
+
+    var daemon = true;
+    cli[action](file, options, daemon);
+  });
+};

function start (file, options, daemon)

+ +

@file {string} Location of the script to spawn with forever

+ +

@options {Object} Options to spawn the script file with.

+ +

@daemon {boolean} Value indicating if we should spawn as a daemon

+ +

Starts a forever process for the script located at file with the +specified options. If daemon is true, then the script will be +started as a daemon process.

cli.start = function (file, options, daemon) {
+  tryStart(file, options, function () { 
+    return daemon 
+      ? forever.startDaemon(file, options)
+      : forever.start(file, options);
+  });
+};

function stop (file)

+ +

@file {string} Target forever process to stop

+ +

Stops the forever process specified by file.

cli.stop = function (file) {
+  var runner = forever.stop(file, true);
+  
+  runner.on('stop', function (process) {
+    winston.info('Forever stopped process:');
+    sys.puts(process);
+  });
+  
+  runner.on('error', function (err) {
+    winston.error('Forever cannot find process with index: ' + file)
+  });
+};

function stopall ()

+ +

Stops all currently running forever processes.

cli.stopall = function () {
+  var runner = forever.stopAll(true);
+  runner.on('stopAll', function (processes) {
+    if (processes) {
+      winston.info('Forever stopped processes:');
+      sys.puts(processes);
+    }
+    else {
+      winston.info('No forever processes running');
+    }
+  });
+};

function restart (file)

+ +

@file {string} Target process to restart

+ +

Restarts the forever process specified by file.

cli.restart = function (file) {
+  var runner = forever.restart(file, true);
+  runner.on('restart', function (processes) {
+    if (processes) {
+      winston.info('Forever restarted processes:');
+      sys.puts(processes);
+    }
+    else {
+      winston.info('No forever processes running');
+    }
+  });
+};

function list ()

+ +

Lists all currently running forever processes.

cli.list = function () {
+  var processes = forever.list(true);
+  if (processes) {
+    winston.info('Forever processes running');
+    sys.puts(processes);
+  }
+  else {
+    winston.info('No forever processes running');
+  }
+};

function config ()

+ +

Lists all of the configuration in ~/.forever/config.json.

cli.config = function () {
+  var keys = Object.keys(forever.config.store),
+      conf = inspect(forever.config.store);
+  
+  if (keys.length <= 2) {
+    conf = conf.replace(/\{\s/, '{ \n')
+               .replace(/\}/, '\n}')
+               .replace('\033[90m', '  \033[90m')
+               .replace(/, /ig, ',\n  ')
+  }
+  else {
+    conf = conf.replace(/\n\s{4}/ig, '\n  ');
+  }
+  
+  conf.split('\n').forEach(function (line) {
+    winston.info(line);
+  });
+};

function set (key, value)

+ +

@key {string} Key to set in forever config

+ +

@value {string} Value to set for key

+ +

Sets the specified key / value pair in the +forever user config.

cli.set = function (key, value) {
+  if (!key || !value) {
+    return winston.error('Both <key> and <value> are required.');
+  }
+  
+  updateConfig(function () {
+    winston.info('Setting forever config: ' + key.grey);
+    forever.config.set(key, value);
+  });
+};

function clear (key)

+ +

@key {string} Key to remove from ~/.forever/config.json

+ +

Removes the specified key from the forever user config.

cli.clear = function (key) {
+  if (reserved.indexOf(key) !== -1) {
+    winston.warn('Cannot clear reserved config: ' + key.grey);
+    winston.warn('Use `forever set ' + key + '` instead');
+    return;
+  }
+  
+  updateConfig(function () {
+    winston.info('Clearing forever config: ' + key.grey);
+    forever.config.clear(key);
+  });
+};

function (file, options, callback)

+ +

@file {string} Target script to start

+ +

@options {Object} Options to start the script with

+ +

@callback {function} Continuation to respond to when complete.

+ +

Helper function that sets up the pathing for the specified file +then stats the appropriate files and responds.

function tryStart (file, options, callback) {
+  var fullLog, fullScript
+
+  fullLog = forever.logFilePath(options.logFile, options.uid);
+  fullScript = path.join(options.sourceDir, file);
+  
+  forever.stat(fullLog, fullScript, options.appendLog, function (err) {
+    if (err) {
+      winston.error('Cannot start forever: ' + err.message);
+      process.exit(-1);
+    }
+  
+    callback();
+  });
+}

function updateConfig (updater)

+ +

@updater {function} Function which updates the forever config

+ +

Helper which runs the specified updater and then saves the forever +config to forever.config.get('root').

function updateConfig (updater) {
+  updater();
+  forever.config.save(function (err) {
+    if (err) {
+      return winston.error('Error saving config: ' + err.message);
+    }
+    
+    cli.config();
+    winston.info('Forever config saved: ' + path.join(forever.config.get('root'), 'config.json').yellow);
+  });
+}
+
+
\ No newline at end of file diff --git a/docs/forever/monitor.html b/docs/forever/monitor.html index 60fa9fae..1059f86a 100644 --- a/docs/forever/monitor.html +++ b/docs/forever/monitor.html @@ -1,7 +1,7 @@ - monitor.js

monitor.js

/*
+      monitor.js           

monitor.js

/*
  * monitor.js: Core functionality for the Monitor object.
  *
- * (C) 2010 and Charlie Robbins
+ * (C) 2010 Charlie Robbins
  * MIT LICENCE
  *
  */
@@ -20,16 +20,17 @@ 

@options {Object} Configuration for this instance.

Creates a new instance of forever with specified params.

var Monitor = exports.Monitor = function (script, options) {
   events.EventEmitter.call(this);
-  
+    
   this.silent     = options.silent || false;
   this.forever    = options.forever || false;
   this.command    = options.command || 'node';
   this.sourceDir  = options.sourceDir;
   this.options    = options.options || [];
   this.spawnWith  = options.spawnWith || null;
+  this.uid        = options.uid || forever.randomString(24);
   this.max        = options.max;
-  this.logFile    = options.logFile;
-  this.pidFile    = options.pidFile;
+  this.logFile    = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
+  this.pidFile    = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
   this.outFile    = options.outFile;
   this.errFile    = options.errFile;
   this.logger     = options.logger || new (winston.Logger)({
@@ -82,8 +83,12 @@ 

@restart {boolean} Value indicating whether this is a restart.

this.ctime = Date.now(); this.child = child; this.running = true; - this.emit(restart ? 'restart' : 'start', self); - this.save() + + this.once('save', function () { + self.emit(restart ? 'restart' : 'start', self); + }); + + this.save();

Hook all stream data and process it

  function listenTo (stream) {
     child[stream].on('data', function (data) {
       if (!self.silent && !self[stream]) {

If we haven't been silenced, and we don't have a file stream @@ -145,6 +150,7 @@

@restart {boolean} Value indicating whether this is a restart.

} var childData = { + uid: this.uid, ctime: this.ctime, pid: this.child.pid, foreverPid: process.pid, @@ -162,8 +168,8 @@

@restart {boolean} Value indicating whether this is a restart.

childData.file = childData.file.replace(this.sourceDir + '/', ''); } - var childPath = path.join(forever.config.pidPath, childData.foreverPid + '.fvr'); - fs.writeFile(childPath, JSON.stringify(childData), function (err) { + var childPath = path.join(forever.config.get('pidPath'), this.uid + '.fvr'); + fs.writeFile(childPath, JSON.stringify(childData, null, 2), function (err) { if (err) self.emit('error', err); self.emit('save', childPath, childData); });