clean house & refactor
This commit is contained in:
parent
59e000249d
commit
31e65020b5
|
|
@ -1 +1,3 @@
|
|||
node_modules
|
||||
node_modules
|
||||
logs
|
||||
*.pid
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
node_modules
|
||||
.gitignore
|
||||
screenshots
|
||||
screenshots
|
||||
logs
|
||||
*.pid
|
||||
|
|
@ -5,3 +5,4 @@ branches:
|
|||
node_js:
|
||||
- 0.10
|
||||
- 0.11
|
||||
- 0.12
|
||||
|
|
|
|||
81
README.md
81
README.md
|
|
@ -13,10 +13,8 @@ An elegant web & terminal interface for Unitech/PM2.
|
|||
- [Cautions](#cauts)
|
||||
- [Installation](#ins)
|
||||
- [CLI](#cli)
|
||||
- [Curses-like dashboard](#dashboard)
|
||||
- [Run Web Interface](#cli_web)
|
||||
- [Daemonic](#daemonic)
|
||||
- [Configs](#cli_confs)
|
||||
- [Curses-like dashboard](#dashboard)
|
||||
- [Authorization](#auth)
|
||||
- [UI/UX](#ui)
|
||||
- [Serving apps locally with nginx and custom domain](#serv)
|
||||
|
|
@ -103,8 +101,8 @@ $ npm install -g pm2-gui
|
|||
--no-debug hide stdout / stderr information
|
||||
```
|
||||
|
||||
<a name="daemonic" />
|
||||
## Daemonic
|
||||
<a name="daemonize" />
|
||||
## daemonize
|
||||
```bash
|
||||
# start
|
||||
$ nohup pm2-gui start > /dev/null 2>&1 & echo $! > /path/to/pm2-gui.pid
|
||||
|
|
@ -121,79 +119,6 @@ debug = false
|
|||
port = 8088
|
||||
```
|
||||
|
||||
- **refresh** The heartbeat duration of monitor (backend), `5000` by default.
|
||||
- **pm2** Root directory of Unitech/PM2, `~/.pm2` by default.
|
||||
- **port** Port of web interface.
|
||||
- **debug** A value indicates whether show the debug information, `true` by default.
|
||||
- **password** The encrypted authentication code, if this config is set, users need to be authorized before accessing the index page, `password` could only be set by `pm2-gui set password [password]` ([authorization](#authorization)).
|
||||
|
||||
### File
|
||||
You can quick set configurations by `pm2-gui start --config [file]`, the `[file]` must be a valid **ini** file, and can include all the above keys.
|
||||
|
||||
Example
|
||||
```bash
|
||||
# Load the configuration file which is named as `pm2-gui.ini` in current directory.
|
||||
$ pm2-gui start --config
|
||||
|
||||
# Load the specific configuration file under current directory, `.ini` postfix is optional.
|
||||
$ pm2-gui start --config conf
|
||||
$ pm2-gui start --config conf.ini
|
||||
```
|
||||
|
||||
### Set
|
||||
Usage
|
||||
```bash
|
||||
$ pm2-gui set <key> <value>
|
||||
```
|
||||
|
||||
Example
|
||||
```bash
|
||||
$ pm2-gui set refresh 2000
|
||||
```
|
||||
|
||||
Above command will set `refresh` to 2 seconds.
|
||||
|
||||
### Remove
|
||||
Usage
|
||||
```bash
|
||||
$ pm2-gui rm <key>
|
||||
```
|
||||
|
||||
Example
|
||||
```bash
|
||||
$ pm2-gui rm refresh
|
||||
```
|
||||
|
||||
Above command will remove `refresh` config and it will be set to `5000` (milliseconds) by default.
|
||||
|
||||
### Update via `vi`
|
||||
```bash
|
||||
$ vi $PM2_ROOT/.pm2/pm2-gui.ini
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
```bash
|
||||
$ rm $PM2_ROOT/.pm2/pm2-gui.ini
|
||||
```
|
||||
|
||||
> The value of `$PM2_ROOT` is `~/` by default.
|
||||
|
||||
<a name="auth" />
|
||||
# Authorization
|
||||
Run the following commands:
|
||||
```bash
|
||||
$ pm2-gui set password 1234
|
||||
$ pm2-gui start
|
||||
```
|
||||
|
||||
When you visiting `http://[domain]:8088` in your browser, it will be redirected to `http://[domain]:8088/auth`, and you need to typo the password (`1234`) to login.
|
||||
|
||||
Otherwise, if you do not want to deny anybody, just simply remove it:
|
||||
```bash
|
||||
$ pm2-gui rm password
|
||||
$ pm2-gui start
|
||||
```
|
||||
|
||||
<a name="ui" />
|
||||
# UI/UX
|
||||
- Amazing and smooth animations.
|
||||
|
|
|
|||
165
bin/pm2-gui
165
bin/pm2-gui
|
|
@ -1,165 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var commander = require('commander'),
|
||||
path = p = require('path'),
|
||||
fs = require('fs'),
|
||||
chalk = require('chalk'),
|
||||
_ = require('lodash'),
|
||||
pkg = require('../package.json'),
|
||||
Monitor = require('../lib/mon'),
|
||||
crypto = require('crypto'),
|
||||
conf = require('../lib/util/conf'),
|
||||
interface = require('../web/index');
|
||||
|
||||
commander.version(pkg.version, '-v, --version')
|
||||
.usage('[cmd] [options]');
|
||||
|
||||
commander.on('--help', function(){
|
||||
console.log(' Basic Examples:\n\n' +
|
||||
' Start the web server, by default port (8088):\n' +
|
||||
chalk.grey(' $ pm2-gui start\n') +
|
||||
'\n' +
|
||||
' Start the web server, by specific port (8090):\n' +
|
||||
chalk.grey(' $ pm2-gui start 8090\n') +
|
||||
'\n' +
|
||||
' Start the web server, by specific configuration file (pm2-gui.ini):\n' +
|
||||
chalk.grey(' $ pm2-gui start --config\n') +
|
||||
'\n' +
|
||||
' Start the web server, by specific configuration file:\n' +
|
||||
chalk.grey(' $ pm2-gui start --config my-config.ini\n')
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Run web interface.
|
||||
*/
|
||||
commander.command('start [port]')
|
||||
.option('--config [file]', 'pass ".ini" configuration file (with options)')
|
||||
.option('--no-debug', 'hide stdout / stderr information')
|
||||
.description('Launch the web server, port default by 8088')
|
||||
.action(function(port, cmd){
|
||||
if (cmd.config) {
|
||||
var jsonFile;
|
||||
if (typeof cmd.config != 'string') {
|
||||
jsonFile = 'pm2-gui.ini';
|
||||
} else {
|
||||
jsonFile = cmd.config;
|
||||
if (jsonFile.indexOf('.') < 0) {
|
||||
jsonFile += '.ini';
|
||||
}
|
||||
}
|
||||
if (!fs.existsSync(jsonFile)) {
|
||||
console.log(chalk.red('✘ .ini configuration file does not exist!\n'));
|
||||
process.exit();
|
||||
}
|
||||
|
||||
try {
|
||||
var config = conf.File(path.resolve(process.cwd(), jsonFile)).loadSync().valueOf();
|
||||
setConfig(config);
|
||||
} catch (err) {
|
||||
console.log(chalk.red('✘ .ini configuration file is invalid!\n'));
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
port && setConfig('port', port);
|
||||
interface(cmd.debug);
|
||||
});
|
||||
|
||||
commander.command('mon')
|
||||
.description('curses-like dashboard')
|
||||
.action(function(){
|
||||
var mon = Monitor({
|
||||
debug: false
|
||||
});
|
||||
mon.dashboard();
|
||||
});
|
||||
|
||||
/**
|
||||
* Show configurations.
|
||||
*/
|
||||
commander.command('config')
|
||||
.description('show all configs')
|
||||
.action(showConfigs);
|
||||
|
||||
/**
|
||||
* Set config by key-value pairs.
|
||||
*/
|
||||
commander.command('set <key> <value>')
|
||||
.description('set config by key-value pairs')
|
||||
.action(function(key, value, cmd){
|
||||
var mon = setConfig(key, value);
|
||||
mon && showConfigs(cmd, mon);
|
||||
});
|
||||
|
||||
/**
|
||||
* Unset config by key.
|
||||
*/
|
||||
commander.command('rm <key>')
|
||||
.description('remove config by key')
|
||||
.action(function(key, cmd){
|
||||
var mon = Monitor();
|
||||
mon.config(key, null);
|
||||
showConfigs(cmd, mon);
|
||||
});
|
||||
|
||||
commander.parse(process.argv);
|
||||
|
||||
if (process.argv.length == 2) {
|
||||
commander.outputHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration.
|
||||
* @param key
|
||||
* @param value
|
||||
* @returns {*}
|
||||
*/
|
||||
function setConfig(key, value){
|
||||
var mon = Monitor(),
|
||||
acceptKeys = Object.keys(mon.options).filter(function(key){
|
||||
return !~['socketio', 'pm2Conf'].indexOf(key);
|
||||
});
|
||||
|
||||
acceptKeys.push('password');
|
||||
|
||||
(function config(pairs){
|
||||
if (pairs.length == 0) {
|
||||
return;
|
||||
}
|
||||
var pair = pairs.shift();
|
||||
if (!~acceptKeys.indexOf(pair[0])) {
|
||||
console.log(chalk.bold.yellow('[WARN]'), chalk.cyan(pair[0]), 'is not an acceptable configuration.');
|
||||
return config(pairs);
|
||||
}
|
||||
if (pair[0] == 'password') {
|
||||
var md5 = crypto.createHash('md5');
|
||||
md5.update(pair[1]);
|
||||
pair[1] = md5.digest('hex');
|
||||
}
|
||||
mon.config(pair[0], pair[1]);
|
||||
config(pairs);
|
||||
})(typeof key == 'object' ? _.pairs(key) : [[key, value]]);
|
||||
|
||||
return mon;
|
||||
}
|
||||
/**
|
||||
* Show all configurations.
|
||||
* @param cmd
|
||||
* @param mon
|
||||
*/
|
||||
function showConfigs(cmd, mon){
|
||||
if (!mon) {
|
||||
mon = Monitor();
|
||||
}
|
||||
var storage = mon._config.valueOf(), prints = '';
|
||||
var maxLen = 0;
|
||||
for (var k in storage) {
|
||||
maxLen = Math.max(k.length, maxLen);
|
||||
}
|
||||
maxLen += 1;
|
||||
for (var k in storage) {
|
||||
prints += chalk.bold(Array(maxLen - k.length).join(' ') + k + ': ') + ' ' + chalk.blue(storage[k] + '\n');
|
||||
}
|
||||
console.log(prints);
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
var chalk = require('chalk'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
async = require('async'),
|
||||
cp = require('child_process'),
|
||||
fork = cp.fork,
|
||||
spawn = cp.spawn,
|
||||
Monitor = require('./monitor'),
|
||||
Log = require('./util/log');
|
||||
|
||||
var processDirname = process.cwd(),
|
||||
confFile = './pm2-gui.ini';
|
||||
|
||||
if (process.argv.length > 3) {
|
||||
var confFile = process.argv[3];
|
||||
}
|
||||
|
||||
confFile = path.resolve(processDirname, confFile);
|
||||
|
||||
if (!fs.existsSync(confFile)) {
|
||||
console.error(chalk.bold(confFile), chalk.red('does not exist!'));
|
||||
return process.exit(0);
|
||||
}
|
||||
|
||||
var monitor = Monitor({
|
||||
confFile: confFile
|
||||
});
|
||||
|
||||
Log(monitor.options.log);
|
||||
|
||||
var pidfile = path.resolve(processDirname, './pm2-gui.pid');
|
||||
|
||||
var Daemon = {
|
||||
restarts: 0,
|
||||
init: function (next) {
|
||||
process.on('SIGTERM', Daemon.stop);
|
||||
process.on('SIGINT', Daemon.stop);
|
||||
process.on('SIGHUP', Daemon.restart);
|
||||
next && next();
|
||||
},
|
||||
start: function (next) {
|
||||
Daemon.worker = Daemon.fork();
|
||||
next && next();
|
||||
},
|
||||
restart: function () {
|
||||
console.info('Restarting...');
|
||||
Daemon.kill();
|
||||
Daemon.start();
|
||||
},
|
||||
stop: function () {
|
||||
console.info('Stopping...');
|
||||
Daemon.kill();
|
||||
fs.unlinkSync(pidfile);
|
||||
},
|
||||
kill: function () {
|
||||
if (Daemon.worker) {
|
||||
Daemon.worker.suicide = true;
|
||||
Daemon.worker.kill();
|
||||
}
|
||||
},
|
||||
fork: function () {
|
||||
console.info('Forking slave...');
|
||||
var worker = fork(path.resolve(processDirname, 'pm2-gui.js'), [confFile, '--color'], {
|
||||
silent: monitor.options.daemonize,
|
||||
env: process.env
|
||||
});
|
||||
worker.on('exit', function (code, signal) {
|
||||
if (code != 0) {
|
||||
if (Daemon.restarts < 10) {
|
||||
Daemon.restarts++;
|
||||
setTimeout(function () {
|
||||
Daemon.restarts--;
|
||||
}, 20000);
|
||||
} else {
|
||||
console.error(Daemon.restarts + ' restarts in 20 seconds, view the logs to investigate the crash problem.');
|
||||
return process.exit(0);
|
||||
}
|
||||
}
|
||||
if (!(worker.suicide || code === 0)) {
|
||||
setTimeout(Daemon.fork, 3000);
|
||||
}
|
||||
});
|
||||
|
||||
worker.on('message', function (message) {
|
||||
if (typeof message == 'object' && message.action)
|
||||
if (message.action == 'restart') {
|
||||
Daemon.restart();
|
||||
}
|
||||
});
|
||||
|
||||
var logDir = monitor.options.log.dir,
|
||||
stdout = 'pm2-gui.out',
|
||||
stderr = 'pm2-gui.err';
|
||||
|
||||
if (!logDir) {
|
||||
logDir = './logs';
|
||||
}
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir);
|
||||
}
|
||||
|
||||
if (monitor.options.daemonize) {
|
||||
stdout = fs.createWriteStream(path.resolve(processDirname, logDir, stdout));
|
||||
stderr = fs.createWriteStream(path.resolve(processDirname, logDir, stderr));
|
||||
worker.stdout.pipe(stdout);
|
||||
worker.stderr.pipe(stderr);
|
||||
}
|
||||
|
||||
fs.writeFile(pidfile, worker.pid);
|
||||
return worker;
|
||||
},
|
||||
daemonize: function () {
|
||||
if (process.env.daemonized) {
|
||||
console.info('Daemonized with pid [' + process.pid + '].');
|
||||
return;
|
||||
}
|
||||
console.info('Spawning daemon...');
|
||||
var args = [].concat(process.argv);
|
||||
args.shift();
|
||||
var env = process.env;
|
||||
env.daemonized = true;
|
||||
var child = spawn(process.execPath, args, {
|
||||
env: env,
|
||||
detached: false,
|
||||
cwd: processDirname,
|
||||
stdio: ['ignore', process.stdout, process.stderr]
|
||||
});
|
||||
child.unref();
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
if (monitor.options.daemonize) {
|
||||
Daemon.daemonize();
|
||||
}
|
||||
|
||||
process.title = 'pm2-gui daemon';
|
||||
async.series([
|
||||
Daemon.init,
|
||||
Daemon.start
|
||||
], function (err) {
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
Debug = require('./util/debug'),
|
||||
stat = require('./stat'),
|
||||
_ = require('lodash'),
|
||||
chalk = require('chalk'),
|
||||
ansiHTML = require('ansi-html'),
|
||||
pm = require('./pm'),
|
||||
totalmem = require('os').totalmem(),
|
||||
pidusage = require('pidusage'),
|
||||
pm = require('./pm'),
|
||||
stat = require('./stat'),
|
||||
conf = require('./util/conf'),
|
||||
layout = require('./blessed-widget/layout'),
|
||||
Log = require('./util/log'),
|
||||
defConf;
|
||||
|
||||
module.exports = Monitor;
|
||||
|
|
@ -29,6 +29,9 @@ function Monitor(options) {
|
|||
this._init(options);
|
||||
};
|
||||
|
||||
Monitor.ACCEPT_KEYS = ['pm2', 'refresh', 'statsd', 'node', 'log', 'daemonize', 'max_restarts'];
|
||||
Monitor.DEF_CONF_FILE = 'pm2-gui.ini';
|
||||
|
||||
/**
|
||||
* Resolve home path.
|
||||
* @param {String} pm2Home
|
||||
|
|
@ -42,7 +45,7 @@ Monitor.prototype._resolveHome = function (pm2Home) {
|
|||
|
||||
// Make sure exist.
|
||||
if (!pm2Home || !fs.existsSync(pm2Home)) {
|
||||
throw new Error('PM2 root can not be located, try to initialize PM2 by executing `pm2 ls` or set env by `export PM2_HOME=[ROOT]`.');
|
||||
throw new Error('PM2 root can not be located, try to initialize PM2 by executing `pm2 ls` or set environment variable vi `export PM2_HOME=[ROOT]`.');
|
||||
}
|
||||
}
|
||||
return pm2Home;
|
||||
|
|
@ -55,11 +58,14 @@ Monitor.prototype._resolveHome = function (pm2Home) {
|
|||
Monitor.prototype._init = function (options) {
|
||||
options = options || {};
|
||||
|
||||
// bind default options.
|
||||
defConf = conf.File(path.resolve(__dirname, '..', 'pm2-gui.ini')).loadSync().valueOf();
|
||||
options = _.defaults(defConf, options);
|
||||
defConf = conf.File(options.confFile || path.resolve(__dirname, '..', Monitor.DEF_CONF_FILE)).loadSync().valueOf();
|
||||
defConf = _.pick.call(null, defConf, Monitor.ACCEPT_KEYS);
|
||||
|
||||
options.pm2 = this._resolveHome(options.pm2.home);
|
||||
options = _.pick.apply(options, Monitor.ACCEPT_KEYS).valueOf();
|
||||
options = _.defaults(options, defConf);
|
||||
|
||||
options.pm2 = this._resolveHome(options.pm2);
|
||||
Log(options.log);
|
||||
|
||||
// Load PM2 config.
|
||||
var pm2ConfPath = path.join(options.pm2, 'conf.js');
|
||||
|
|
@ -69,21 +75,17 @@ Monitor.prototype._init = function (options) {
|
|||
throw new Error(404);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Can not load PM2 config, the file "' + pm2ConfPath + '" does not exist.');
|
||||
var fbMsg = 'Can not load PM2 config, the file "' + pm2ConfPath + '" does not exist or empty, fallback to auto-load by pm2 home.';
|
||||
console.warn(fbMsg);
|
||||
options.pm2Conf = {
|
||||
DAEMON_RPC_PORT: path.resolve(options.pm2, 'rpc.sock'),
|
||||
DAEMON_PUB_PORT: path.resolve(options.pm2, 'pub.sock'),
|
||||
PM2_LOG_FILE_PATH: path.resolve(options.pm2, 'pm2.log')
|
||||
};
|
||||
|
||||
if (!fs.existsSync(options.pm2Conf.DAEMON_RPC_PORT)) {
|
||||
throw new Error(fbMsg + ' But file ' + options.pm2Conf.DAEMON_RPC_PORT + ' can not found.');
|
||||
}
|
||||
if (!fs.existsSync(options.pm2Conf.DAEMON_PUB_PORT)) {
|
||||
throw new Error(fbMsg + ' But file ' + options.pm2Conf.DAEMON_PUB_PORT + ' can not found.');
|
||||
}
|
||||
if (!fs.existsSync(options.pm2Conf.PM2_LOG_FILE_PATH)) {
|
||||
throw new Error(fbMsg + ' But file ' + options.pm2Conf.PM2_LOG_FILE_PATH + ' can not found.');
|
||||
for (var pm2ConfProp in options.pm2Conf) {
|
||||
if (!fs.existsSync(options.pm2Conf[pm2ConfProp])) {
|
||||
throw new Error(fbMsg + ' But file ' + options.pm2Conf[pm2ConfProp] + ' can not found.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,40 +98,6 @@ Monitor.prototype._init = function (options) {
|
|||
// Bind to context.
|
||||
this.options = options;
|
||||
Object.freeze(this.options);
|
||||
|
||||
// Initialize configurations.
|
||||
this._config = new conf.File(path.resolve(this.options.pm2, 'pm2-gui.ini'));
|
||||
|
||||
// Set configurations.
|
||||
this.config('pm2', this._resolveHome(this.config('pm2')) || this.options.pm2);
|
||||
this.config('refresh', this.config('refresh') || this.options.refresh);
|
||||
this.config('port', this.config('port') || this.options.port || 8088);
|
||||
var debug = this.config('debug');
|
||||
this.config('debug', typeof debug == 'undefined' ? (debug = !!this.options.debug) : !!debug);
|
||||
|
||||
// Logger.
|
||||
this._log = Debug({
|
||||
namespace: 'monitor',
|
||||
debug: debug
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Operations of configuration.
|
||||
* @example:
|
||||
* set config : mon.config('key', 'value');
|
||||
* clear config : mon.config('key', null);
|
||||
* get config : mon.config('key');
|
||||
* @param {String} key
|
||||
* @param {Mixed} value
|
||||
* @returns {*}
|
||||
*/
|
||||
Monitor.prototype.config = function (key, value) {
|
||||
var def;
|
||||
if (value == null) {
|
||||
def = defConf[key];
|
||||
}
|
||||
return this._config.val(key, value, def);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -145,36 +113,38 @@ Monitor.prototype.run = function () {
|
|||
this._tails = {};
|
||||
this._usages = {};
|
||||
|
||||
// Watching PM2
|
||||
this._startWatching();
|
||||
// Observe PM2
|
||||
this._observePM2();
|
||||
|
||||
// Listen connection event.
|
||||
this._sockio.of(conf.NSP.SYS).on('connection', this._connectSysSock.bind(this));
|
||||
this._sockio.of(conf.NSP.LOG).on('connection', this._connectLogSock.bind(this));
|
||||
this._sockio.of(conf.NSP.PROC).on('connection', this._connectProcSock.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Quit monitor.
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
Monitor.prototype.quit = function () {
|
||||
console.debug('Closing pm2 pub emitter socket.');
|
||||
this.pm2Sock && this.pm2Sock.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Monitor dashboard.
|
||||
*/
|
||||
Monitor.prototype.dashboard = function () {
|
||||
// No logger anymore.
|
||||
this._log = Debug({
|
||||
namespace: 'monitor',
|
||||
debug: false
|
||||
});
|
||||
|
||||
// Socket.io server.
|
||||
var port = this.config('port');
|
||||
var port = this.options.port;
|
||||
this._sockio = require('socket.io')();
|
||||
this._sockio.listen(port);
|
||||
this.run();
|
||||
|
||||
// Render screen.
|
||||
var lyt = layout({
|
||||
layout({
|
||||
port: port
|
||||
});
|
||||
lyt.render();
|
||||
}).render();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -193,13 +163,13 @@ Monitor.prototype._connectSysSock = function (socket) {
|
|||
|
||||
// Trigger actions of process.
|
||||
socket.on('action', function (action, id) {
|
||||
this._log.i('action', chalk.magenta(action), ' ', id);
|
||||
console.info('[' + id + ']', action, 'sending to pm2 daemon...');
|
||||
pm.action(this.options.pm2Conf.DAEMON_RPC_PORT, action, id, function (err, forceRefresh) {
|
||||
if (err) {
|
||||
this._log.e(action, err.message);
|
||||
console.error(action, err.message);
|
||||
return socket.emit('action', id, err.message);
|
||||
}
|
||||
this._log.i('action', chalk.magenta(action), 'finished', id);
|
||||
console.info('[' + id + ']', action, 'completed!');
|
||||
forceRefresh && this._throttleRefresh();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
|
@ -213,8 +183,8 @@ Monitor.prototype._connectSysSock = function (socket) {
|
|||
this._sysStat && this._broadcast('system_stat', this._sysStat);
|
||||
|
||||
// Grep system states once and again.
|
||||
(this._status != 'R') && this._nextTick(this.config('refresh') || 5000);
|
||||
}
|
||||
(this._status != 'R') && this._nextTick(this.options.refresh || 5000);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection event of `log` namespace.
|
||||
|
|
@ -222,44 +192,33 @@ Monitor.prototype._connectSysSock = function (socket) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._connectLogSock = function (socket) {
|
||||
// Broadcast tailed logs.
|
||||
function broadcast(data) {
|
||||
var socks = this._sockio.of(conf.NSP.LOG).sockets;
|
||||
if (!socks || socks.length == 0) {
|
||||
return;
|
||||
}
|
||||
socks.forEach(function (sock) {
|
||||
if (sock._pm_id == data.pm_id) {
|
||||
sock.emit('log', data)
|
||||
}
|
||||
});
|
||||
}
|
||||
var self = this;
|
||||
|
||||
// Emit error.
|
||||
function emitError(err, pm_id, keepANSI) {
|
||||
broadcast.call(this, {
|
||||
var data = {
|
||||
pm_id: pm_id,
|
||||
msg: keepANSI ? chalk.red(err.message) : '<span style="color: #ff0000">Error: ' + err.message + '</span>'
|
||||
});
|
||||
};
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG);
|
||||
}
|
||||
|
||||
// Clean up `tail` after socket disconnected.
|
||||
function killTail(pm_id) {
|
||||
this._tails[pm_id].forEach(function (tail) {
|
||||
self._tails[pm_id].forEach(function (tail) {
|
||||
try {
|
||||
tail.kill('SIGTERM');
|
||||
} catch (err) {}
|
||||
});
|
||||
delete this._tails[pm_id];
|
||||
this._log.i('tail', chalk.magenta('destroy'), pm_id);
|
||||
delete self._tails[pm_id];
|
||||
console.info('[' + pm_id + ']', 'tail destroyed!');
|
||||
}
|
||||
|
||||
function killTailProcess(pm_id) {
|
||||
if (!isNaN(pm_id)) {
|
||||
killTail.call(this, pm_id);
|
||||
return;
|
||||
return killTail(pm_id);
|
||||
}
|
||||
var socks = this._sockio.of(conf.NSP.LOG).sockets,
|
||||
var socks = self._sockio.of(conf.NSP.LOG).sockets,
|
||||
canNotBeDeleted = {};
|
||||
if (socks && socks.length > 0) {
|
||||
socks.forEach(function (sock) {
|
||||
|
|
@ -267,32 +226,31 @@ Monitor.prototype._connectLogSock = function (socket) {
|
|||
});
|
||||
}
|
||||
|
||||
for (var pm_id in this._tails) {
|
||||
for (var pm_id in self._tails) {
|
||||
if (!canNotBeDeleted[pm_id]) {
|
||||
killTail.call(this, pm_id);
|
||||
killTail(pm_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
socket.on('disconnect', killTailProcess.bind(this));
|
||||
socket.on('tail_kill', killTailProcess.bind(this));
|
||||
socket.on('tail', function (pm_id, keepANSI) {
|
||||
|
||||
function startTailProcess(pm_id, keepANSI) {
|
||||
socket._pm_id = pm_id;
|
||||
|
||||
if (this._tails[pm_id]) {
|
||||
if (self._tails[pm_id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tail logs.
|
||||
pm.tail({
|
||||
sockPath: this.options.pm2Conf.DAEMON_RPC_PORT,
|
||||
logPath: this.options.pm2Conf.PM2_LOG_FILE_PATH,
|
||||
sockPath: self.options.pm2Conf.DAEMON_RPC_PORT,
|
||||
logPath: self.options.pm2Conf.PM2_LOG_FILE_PATH,
|
||||
pm_id: pm_id
|
||||
}, function (err, lines) {
|
||||
if (err) {
|
||||
return emitError.call(this, err, pm_id, keepANSI);
|
||||
return emitError(err, pm_id, keepANSI);
|
||||
}
|
||||
// Emit logs to clients.
|
||||
broadcast.call(this, {
|
||||
var data = {
|
||||
pm_id: pm_id,
|
||||
msg: lines.map(function (line) {
|
||||
if (!keepANSI) {
|
||||
|
|
@ -302,16 +260,21 @@ Monitor.prototype._connectLogSock = function (socket) {
|
|||
return line;
|
||||
}
|
||||
}).join(keepANSI ? '\n' : '')
|
||||
});
|
||||
}.bind(this), function (err, tails) {
|
||||
};
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG);
|
||||
}, function (err, tails) {
|
||||
if (err) {
|
||||
return emitError.call(this, err, pm_id, keepANSI);
|
||||
return emitError(err, pm_id, keepANSI);
|
||||
}
|
||||
|
||||
this._log.i('tail', chalk.magenta('start'), pm_id);
|
||||
this._tails[pm_id] = tails;
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
console.info('[' + pm_id + ']', 'tail starting...');
|
||||
self._tails[pm_id] = tails;
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('disconnect', killTailProcess);
|
||||
socket.on('tail_kill', killTailProcess);
|
||||
socket.on('tail', startTailProcess);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -320,30 +283,18 @@ Monitor.prototype._connectLogSock = function (socket) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._connectProcSock = function (socket) {
|
||||
// Broadcast memory/CPU usage of process.
|
||||
function broadcast(data) {
|
||||
var socks = this._sockio.of(conf.NSP.PROC).sockets;
|
||||
if (!socks || socks.length == 0) {
|
||||
return;
|
||||
}
|
||||
socks.forEach(function (sock) {
|
||||
if (sock._pid == data.pid) {
|
||||
sock.emit('proc', data)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
// Emit error.
|
||||
function emitError(err, pid) {
|
||||
broadcast.call(this, {
|
||||
var data = {
|
||||
pid: pid,
|
||||
msg: '<span style="color: #ff0000">Error: ' + err.message + '</span>'
|
||||
});
|
||||
};
|
||||
self._broadcast.call(self, 'proc', data, conf.NSP.PROC);
|
||||
}
|
||||
|
||||
// Clean up `proc` timer after socket disconnected.
|
||||
socket.on('disconnect', function () {
|
||||
var socks = this._sockio.of(conf.NSP.PROC).sockets,
|
||||
function killObserver() {
|
||||
var socks = self._sockio.of(conf.NSP.PROC).sockets,
|
||||
canNotBeDeleted = {};
|
||||
if (socks && socks.length > 0) {
|
||||
socks.forEach(function (sock) {
|
||||
|
|
@ -356,20 +307,20 @@ Monitor.prototype._connectProcSock = function (socket) {
|
|||
if (!canNotBeDeleted[pid] && (timer = this._usages[pid])) {
|
||||
clearInterval(timer);
|
||||
delete this._usages[pid];
|
||||
this._log.i('usage', chalk.magenta('destroy'), pid);
|
||||
console.info('[' + pid + ']', 'cpu and memory observer destroyed!');
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
socket.on('proc', function (pid) {
|
||||
function runObserver(pid) {
|
||||
socket._pid = pid;
|
||||
|
||||
var pidStr = pid.toString();
|
||||
if (this._usages[pidStr]) {
|
||||
if (self._usages[pidStr]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.i('usage', chalk.magenta('start'), pidStr);
|
||||
console.info('[' + pidStr + ']', 'cpu and memory observer is running...');
|
||||
|
||||
function runTimer(ctx) {
|
||||
pidusage.stat(pid, function (err, stat) {
|
||||
|
|
@ -379,18 +330,22 @@ Monitor.prototype._connectProcSock = function (socket) {
|
|||
return emitError.call(ctx, err, pid);
|
||||
}
|
||||
stat.memory = stat.memory * 100 / totalmem;
|
||||
// Emit memory/CPU usage to clients.
|
||||
broadcast.call(ctx, {
|
||||
|
||||
var data = {
|
||||
pid: pid,
|
||||
time: Date.now(),
|
||||
usage: stat
|
||||
});
|
||||
};
|
||||
self._broadcast.call(ctx, 'proc', data, conf.NSP.PROC);
|
||||
});
|
||||
}
|
||||
|
||||
this._usages[pidStr] = setInterval(runTimer, 3000, this);
|
||||
self._usages[pidStr] = setInterval(runTimer, 3000, self);
|
||||
runTimer(this);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
socket.on('disconnect', killObserver);
|
||||
socket.on('proc', runObserver);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -405,7 +360,7 @@ Monitor.prototype._nextTick = function (tick, continuously) {
|
|||
}
|
||||
// Running
|
||||
this._status = 'R';
|
||||
this._log.d(chalk.magenta('monitor'), tick);
|
||||
console.debug('monitor heartbeat', tick);
|
||||
// Grep system state
|
||||
this._systemStat(function () {
|
||||
// If there still has any client, grep again after `tick` ms.
|
||||
|
|
@ -414,9 +369,9 @@ Monitor.prototype._nextTick = function (tick, continuously) {
|
|||
}
|
||||
// Stop
|
||||
delete this._status;
|
||||
this._log.d(chalk.magenta('monitor'), chalk.red('destroy'));
|
||||
}.bind(this));
|
||||
}
|
||||
console.debug('monitor heartbeat destroyed!');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Grep system states.
|
||||
|
|
@ -427,7 +382,7 @@ Monitor.prototype._systemStat = function (cb) {
|
|||
stat.cpuUsage(function (err, cpu_usage) {
|
||||
if (err) {
|
||||
// Log only.
|
||||
this._log.e('sockio', 'Can not load system/cpu/memory information: ' + err.message);
|
||||
console.error('Can not load system/cpu/memory information: ', err.message);
|
||||
} else {
|
||||
// System states.
|
||||
this._sysStat = _.defaults(_(stat).pick('cpus', 'arch', 'hostname', 'platform', 'release', 'uptime', 'memory').clone(), {
|
||||
|
|
@ -435,20 +390,20 @@ Monitor.prototype._systemStat = function (cb) {
|
|||
});
|
||||
this._broadcast('system_stat', this._sysStat);
|
||||
}
|
||||
cb();
|
||||
}.bind(this));
|
||||
}
|
||||
cb.call(this);
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Watching PM2
|
||||
* Observe PM2
|
||||
* @private
|
||||
*/
|
||||
Monitor.prototype._startWatching = function () {
|
||||
// Watching sub-emitter.
|
||||
pm.sub(this.options.pm2Conf.DAEMON_PUB_PORT, function (data) {
|
||||
this._log.i('sub-emitter', chalk.magenta(data.event), data.process.name + '-' + data.process.pm_id);
|
||||
Monitor.prototype._observePM2 = function () {
|
||||
console.info('Connecting to pm2 daemon:', pm2Daemon);
|
||||
this.pm2Sock = pm.sub(this.options.pm2Conf.DAEMON_PUB_PORT, function (data) {
|
||||
console.info('sub-emitter', chalk.magenta(data.event), data.process.name + '-' + data.process.pm_id);
|
||||
this._throttleRefresh();
|
||||
}.bind(this));
|
||||
}, this);
|
||||
|
||||
// Enforce a refresh operation if RPC is not online.
|
||||
this._throttleRefresh();
|
||||
|
|
@ -467,6 +422,7 @@ Monitor.prototype._throttleRefresh = function () {
|
|||
ctx._refreshProcs();
|
||||
}, 500, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh processes
|
||||
* @private
|
||||
|
|
@ -474,7 +430,7 @@ Monitor.prototype._throttleRefresh = function () {
|
|||
Monitor.prototype._refreshProcs = function () {
|
||||
pm.list(this.options.pm2Conf.DAEMON_RPC_PORT, function (err, procs) {
|
||||
if (err) {
|
||||
return this._broadcast('info', 'Error: ' + err.message);
|
||||
return this._broadcast('info', 'Can not connect to pm2 daemon, ' + err.message);
|
||||
}
|
||||
// Wrap processes and cache them.
|
||||
this._procs = procs.map(function (proc) {
|
||||
|
|
@ -499,7 +455,7 @@ Monitor.prototype._refreshProcs = function () {
|
|||
});
|
||||
// Emit to client.
|
||||
this._broadcast('procs', this._procs);
|
||||
}.bind(this))
|
||||
}, this)
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -521,4 +477,14 @@ Monitor.prototype._pm2Ver = function (socket) {
|
|||
*/
|
||||
Monitor.prototype._broadcast = function (event, data, nsp) {
|
||||
this._sockio.of(nsp || conf.NSP.SYS).emit(event, data);
|
||||
/*
|
||||
var socks = this._sockio.of(nsp || conf.NSP.SYS).sockets;
|
||||
if (!socks || socks.length == 0) {
|
||||
return;
|
||||
}
|
||||
socks.forEach(function (sock) {
|
||||
if (sock._pm_id == data.pm_id) {
|
||||
sock.emit(event, data)
|
||||
}
|
||||
});*/
|
||||
};
|
||||
134
lib/pm.js
134
lib/pm.js
|
|
@ -1,12 +1,12 @@
|
|||
var spawn = require('child_process').spawn,
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
async = require('async'),
|
||||
chalk = require('chalk'),
|
||||
stat = require('./stat'),
|
||||
rpc = require('pm2-axon-rpc'),
|
||||
axon = require('pm2-axon');
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
async = require('async'),
|
||||
chalk = require('chalk'),
|
||||
stat = require('./stat'),
|
||||
rpc = require('pm2-axon-rpc'),
|
||||
axon = require('pm2-axon');
|
||||
|
||||
/**
|
||||
* Forever lib.
|
||||
|
|
@ -14,32 +14,33 @@ var spawn = require('child_process').spawn,
|
|||
*/
|
||||
var pm = module.exports = {};
|
||||
|
||||
var re_blank = /^[\s\r\t]*$/,
|
||||
allowedEvents = ['start', 'restart', 'exit', 'online'];
|
||||
var re_blank = /^[\s\r\t]*$/,
|
||||
allowedEvents = ['start', 'restart', 'exit', 'online'];
|
||||
|
||||
/**
|
||||
* Subscribe event BUS.
|
||||
* @param {String} sockPath
|
||||
* @param {Function} cb
|
||||
* @param {Object} context
|
||||
*/
|
||||
pm.sub = function(sockPath, cb){
|
||||
pm.sub = function (sockPath, cb, context) {
|
||||
var sub = axon.socket('sub-emitter');
|
||||
sub.connect(sockPath);
|
||||
|
||||
// Once awake from sleeping.
|
||||
sub.on('log:*', function(e, d){
|
||||
sub.on('log:*', function (e, d) {
|
||||
// Do not subscribe it.
|
||||
sub.off('log:*');
|
||||
d.event = 'awake';
|
||||
cb(d);
|
||||
cb.call(context, d);
|
||||
});
|
||||
|
||||
// Process events.
|
||||
sub.on('process:*', function(e, d){
|
||||
sub.on('process:*', function (e, d) {
|
||||
if (d && !!~allowedEvents.indexOf(d.event)) {
|
||||
cb(d);
|
||||
cb.call(context, d);
|
||||
}
|
||||
});
|
||||
sub.connect(sockPath);
|
||||
return sub;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -47,10 +48,10 @@ pm.sub = function(sockPath, cb){
|
|||
* @param {String} sockPath
|
||||
* @param {Function} cb
|
||||
*/
|
||||
pm.version = function(sockPath, cb){
|
||||
pm.version = function (sockPath, cb) {
|
||||
pm._rpc({
|
||||
sockPath: sockPath,
|
||||
events : [
|
||||
events: [
|
||||
['getVersion', {}, cb]
|
||||
]
|
||||
});
|
||||
|
|
@ -60,16 +61,18 @@ pm.version = function(sockPath, cb){
|
|||
* List available processes.
|
||||
* @param {String} sockPath
|
||||
* @param {Function} cb
|
||||
* @param {Object} context
|
||||
*/
|
||||
pm.list = function(sockPath, cb){
|
||||
pm.list = function (sockPath, cb, context) {
|
||||
if (!fs.existsSync(sockPath)) {
|
||||
return cb(null, []);
|
||||
return cb.call(context, []);
|
||||
}
|
||||
pm._rpc({
|
||||
sockPath: sockPath,
|
||||
events : [
|
||||
events: [
|
||||
['getMonitorData', {}, cb]
|
||||
]
|
||||
],
|
||||
context: context || this
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -77,42 +80,43 @@ pm.list = function(sockPath, cb){
|
|||
* Execute remote RPC events.
|
||||
* @param {Object} opts including:
|
||||
* {String} sockPath
|
||||
* {Object} context
|
||||
* {Object} args
|
||||
* {Object} events
|
||||
* key: event name
|
||||
* value: callback function
|
||||
* @private
|
||||
*/
|
||||
pm._rpc = function(opts){
|
||||
pm._rpc = function (opts) {
|
||||
var req = axon.socket("req"),
|
||||
rpc_sock = req.connect(opts.sockPath),
|
||||
rpc_client = new rpc.Client(req);
|
||||
rpcSock = req.connect(opts.sockPath),
|
||||
rpcClient = new rpc.Client(req);
|
||||
|
||||
// Connect RPC server.
|
||||
rpc_sock.on('connect', function(){
|
||||
rpcSock.on('connect', function () {
|
||||
// Execute request.
|
||||
var waterfalls = opts.events.map(function(event){
|
||||
return function(next){
|
||||
var waterfalls = opts.events.map(function (event) {
|
||||
return function (next) {
|
||||
var cb = typeof event[event.length - 1] == 'function' ? event.pop() : null;
|
||||
if (cb) {
|
||||
event.push(function(){
|
||||
event.push(function () {
|
||||
// Wrap arguments, no [].slice (avoid leak)!!!
|
||||
var args = new Array(arguments.length);
|
||||
for (var i = 0; i < args; i++) {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
cb.apply(null, arguments);
|
||||
cb.apply(opts.context, arguments);
|
||||
next();
|
||||
});
|
||||
}
|
||||
rpc_client.call.apply(rpc_client, event);
|
||||
rpcClient.call.apply(rpcClient, event);
|
||||
if (!cb) {
|
||||
next();
|
||||
}
|
||||
};
|
||||
});
|
||||
async.waterfall(waterfalls, function(err, res){
|
||||
rpc_sock.close();
|
||||
async.waterfall(waterfalls, function (err, res) {
|
||||
rpcSock.close();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -124,8 +128,8 @@ pm._rpc = function(opts){
|
|||
* @param {Function} cb
|
||||
* @private
|
||||
*/
|
||||
pm._findById = function(sockPath, id, cb){
|
||||
pm.list(sockPath, function(err, procs){
|
||||
pm._findById = function (sockPath, id, cb) {
|
||||
pm.list(sockPath, function (err, procs) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
|
@ -133,7 +137,7 @@ pm._findById = function(sockPath, id, cb){
|
|||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'));
|
||||
}
|
||||
|
||||
var proc = _.find(procs, function(p){
|
||||
var proc = _.find(procs, function (p) {
|
||||
return p && p.pm_id == id;
|
||||
});
|
||||
|
||||
|
|
@ -151,9 +155,9 @@ pm._findById = function(sockPath, id, cb){
|
|||
* @param {String} id
|
||||
* @param {Function} cb
|
||||
*/
|
||||
pm.action = function(sockPath, action, id, cb){
|
||||
pm.action = function (sockPath, action, id, cb) {
|
||||
if (id == 'all') {
|
||||
pm.list(sockPath, function(err, procs){
|
||||
pm.list(sockPath, function (err, procs) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
|
@ -161,12 +165,12 @@ pm.action = function(sockPath, action, id, cb){
|
|||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'));
|
||||
}
|
||||
|
||||
async.map(procs, function(proc, next){
|
||||
async.map(procs, function (proc, next) {
|
||||
pm._actionByPMId(sockPath, proc, action, next.bind(null, null));
|
||||
}, cb);
|
||||
});
|
||||
} else {
|
||||
pm._findById(sockPath, id, function(err, proc){
|
||||
pm._findById(sockPath, id, function (err, proc) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
|
@ -183,30 +187,33 @@ pm.action = function(sockPath, action, id, cb){
|
|||
* @param {Function} cb
|
||||
* @private
|
||||
*/
|
||||
pm._actionByPMId = function(sockPath, proc, action, cb){
|
||||
pm._actionByPMId = function (sockPath, proc, action, cb) {
|
||||
var noBusEvent = action == 'delete' && proc.pm2_env.status != 'online',
|
||||
pm_id = proc.pm_id;
|
||||
pm_id = proc.pm_id;
|
||||
|
||||
action += 'ProcessId';
|
||||
var watchEvent = ['stopWatch', action, {id: pm_id}, function(err, success){
|
||||
}];
|
||||
var watchEvent = ['stopWatch', action, {
|
||||
id: pm_id
|
||||
}, function (err, success) {}];
|
||||
|
||||
if (!!~['restart'].indexOf(action)) {
|
||||
watchEvent.splice(0, 1, 'restartWatch');
|
||||
watchEvent.pop();
|
||||
}
|
||||
|
||||
var actionEvent = [action, pm_id, function(err, sock){
|
||||
var actionEvent = [action, pm_id, function (err, sock) {
|
||||
cb(err, noBusEvent);
|
||||
}];
|
||||
|
||||
if (action == 'restartProcessId') {
|
||||
actionEvent.splice(1, 1, {id: pm_id});
|
||||
actionEvent.splice(1, 1, {
|
||||
id: pm_id
|
||||
});
|
||||
}
|
||||
|
||||
pm._rpc({
|
||||
sockPath: sockPath,
|
||||
events : [
|
||||
events: [
|
||||
watchEvent,
|
||||
actionEvent
|
||||
]
|
||||
|
|
@ -220,9 +227,9 @@ pm._actionByPMId = function(sockPath, proc, action, cb){
|
|||
* @param {Function} cb
|
||||
* @returns {*}
|
||||
*/
|
||||
pm.tail = function(opts, each, cb){
|
||||
pm.tail = function (opts, each, cb) {
|
||||
// Fetch the proccess that we need.
|
||||
pm._findById(opts.sockPath, opts.pm_id, function(err, proc){
|
||||
pm._findById(opts.sockPath, opts.pm_id, function (err, proc) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
|
@ -239,8 +246,10 @@ pm.tail = function(opts, each, cb){
|
|||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
pm._tailLogs = function(proc, cb){
|
||||
var logs = [['PM2', proc.pm2_log]];
|
||||
pm._tailLogs = function (proc, cb) {
|
||||
var logs = [
|
||||
['PM2', proc.pm2_log]
|
||||
];
|
||||
if (proc.pm_log_path) {
|
||||
logs.push(['entire', proc.pm2_env.pm_log_path]);
|
||||
} else {
|
||||
|
|
@ -252,7 +261,7 @@ pm._tailLogs = function(proc, cb){
|
|||
paths.push(['err', proc.pm2_env.pm_err_log_path]);
|
||||
}
|
||||
|
||||
paths = paths.sort(function(a, b) {
|
||||
paths = paths.sort(function (a, b) {
|
||||
return (fs.existsSync(a[1]) ? fs.statSync(a[1]).mtime.valueOf() : 0) -
|
||||
(fs.existsSync(b[1]) ? fs.statSync(b[1]).mtime.valueOf() : 0);
|
||||
});
|
||||
|
|
@ -260,7 +269,7 @@ pm._tailLogs = function(proc, cb){
|
|||
}
|
||||
|
||||
var tails = [];
|
||||
(function tailLog(ls){
|
||||
(function tailLog(ls) {
|
||||
var log = ls.shift();
|
||||
if (!log) {
|
||||
return;
|
||||
|
|
@ -270,22 +279,23 @@ pm._tailLogs = function(proc, cb){
|
|||
return;
|
||||
}
|
||||
var key = log[0],
|
||||
prefix = chalk[key == 'err' ? 'red' : 'green'].bold('[' + key + ']');
|
||||
prefix = chalk[key == 'err' ? 'red' : 'green'].bold('[' + key + ']');
|
||||
|
||||
var tail = spawn('tail', ['-f', '-n', 10, logPath], {
|
||||
killSignal: 'SIGTERM',
|
||||
stdio : [null, 'pipe', 'pipe']
|
||||
stdio: [null, 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
// Use utf8 encoding.
|
||||
tail.stdio.forEach(function(stdio){
|
||||
tail.stdio.forEach(function (stdio) {
|
||||
stdio.setEncoding('utf8');
|
||||
});
|
||||
|
||||
// stdout.
|
||||
tail.stdout.on('data', function(data){
|
||||
var lines = [], _lines = data.split(/\n/);
|
||||
_lines.forEach(function(line){
|
||||
tail.stdout.on('data', function (data) {
|
||||
var lines = [],
|
||||
_lines = data.split(/\n/);
|
||||
_lines.forEach(function (line) {
|
||||
if (!re_blank.test(line)) {
|
||||
lines.push(prefix + ' ' + line);
|
||||
}
|
||||
|
|
@ -296,7 +306,7 @@ pm._tailLogs = function(proc, cb){
|
|||
});
|
||||
|
||||
// handle error.
|
||||
tail.stderr.on('data', function(data){
|
||||
tail.stderr.on('data', function (data) {
|
||||
tail.disconnect();
|
||||
cb(new Error(data.toString().replace(/\n/, '')));
|
||||
});
|
||||
|
|
@ -304,4 +314,4 @@ pm._tailLogs = function(proc, cb){
|
|||
tailLog(ls);
|
||||
})(logs);
|
||||
return tails;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
36
lib/stat.js
36
lib/stat.js
|
|
@ -1,4 +1,4 @@
|
|||
var os = require('os');
|
||||
var os = require('os');
|
||||
|
||||
/**
|
||||
* System states
|
||||
|
|
@ -16,33 +16,33 @@ var stat = module.exports = {
|
|||
/**
|
||||
* Architecture, e.g.: 64, 32...
|
||||
*/
|
||||
arch : os.arch(),
|
||||
arch: os.arch(),
|
||||
/**
|
||||
* Ver number of system.
|
||||
*/
|
||||
release : os.release(),
|
||||
release: os.release(),
|
||||
/**
|
||||
* List all CPUs.
|
||||
* @returns {*}
|
||||
*/
|
||||
get cpus(){
|
||||
get cpus() {
|
||||
return os.cpus();
|
||||
},
|
||||
/**
|
||||
* Uptime.
|
||||
* @returns {*}
|
||||
*/
|
||||
get uptime(){
|
||||
get uptime() {
|
||||
return os.uptime();
|
||||
},
|
||||
/**
|
||||
* System memory usage.
|
||||
* @returns {{free: *, total: *, percentage: number}}
|
||||
*/
|
||||
get memory(){
|
||||
get memory() {
|
||||
return {
|
||||
free : os.freemem(),
|
||||
total : os.totalmem(),
|
||||
free: os.freemem(),
|
||||
total: os.totalmem(),
|
||||
percentage: Math.round(100 * (1 - os.freemem() / os.totalmem()))
|
||||
}
|
||||
}
|
||||
|
|
@ -51,22 +51,24 @@ var stat = module.exports = {
|
|||
/**
|
||||
* System CPU usage percentage (total).
|
||||
* @param fn
|
||||
* @param context
|
||||
*/
|
||||
stat.cpuUsage = function(fn){
|
||||
setTimeout(function(ctx, stat1){
|
||||
stat.cpuUsage = function (fn, context) {
|
||||
setTimeout(function (ctx, stat1) {
|
||||
var stat2 = ctx.cpuInfo(),
|
||||
perc = 100 * (1 - (stat2.idle - stat1.idle) / (stat2.total - stat1.total));
|
||||
fn(null, perc.toFixed(2));
|
||||
perc = 100 * (1 - (stat2.idle - stat1.idle) / (stat2.total - stat1.total));
|
||||
fn.call(context, perc.toFixed(2));
|
||||
}, 1000, this, this.cpuInfo());
|
||||
};
|
||||
|
||||
/**
|
||||
* System CPU usage detail information.
|
||||
* @param fn
|
||||
* @returns {{idle: number, total: number}}
|
||||
*/
|
||||
stat.cpuInfo = function(fn){
|
||||
var cpus = this.cpus, idle = 0, total = 0;
|
||||
stat.cpuInfo = function () {
|
||||
var cpus = this.cpus,
|
||||
idle = 0,
|
||||
total = 0;
|
||||
for (var i in cpus) {
|
||||
idle += cpus[i].times.idle;
|
||||
for (var k in cpus[i].times) {
|
||||
|
|
@ -74,7 +76,7 @@ stat.cpuInfo = function(fn){
|
|||
}
|
||||
}
|
||||
return {
|
||||
'idle' : idle,
|
||||
'idle': idle,
|
||||
'total': total
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,20 @@
|
|||
var fs = require('fs'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Namespaces of socket.io
|
||||
* @type {{SYS: string, LOG: string, PROC: string}}
|
||||
*/
|
||||
exports.NSP = {
|
||||
SYS: '/sys',
|
||||
LOG: '/log',
|
||||
PROC: '/proc'
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurations
|
||||
* @type {[type]}
|
||||
*/
|
||||
exports.File = File;
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +68,6 @@ File.prototype.loadSync = function () {
|
|||
if (re_comment.test(line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ms;
|
||||
// Sections.
|
||||
if ((ms = line.match(re_setion)) && ms.length == 2) {
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
var chalk = require('chalk'),
|
||||
_ = require('lodash');
|
||||
|
||||
module.exports = Debug;
|
||||
|
||||
/**
|
||||
* Simple debug tool.
|
||||
* @param {Object} options
|
||||
* @returns {Debug}
|
||||
* @constructor
|
||||
*/
|
||||
function Debug(options) {
|
||||
if (!(this instanceof Debug)) {
|
||||
return new Debug(options);
|
||||
}
|
||||
if (typeof options == 'string') {
|
||||
options = {
|
||||
namespace: options
|
||||
};
|
||||
}
|
||||
this.options = _.defaults(options || {}, {
|
||||
namespace: 'pm2-gui',
|
||||
timestamp: true,
|
||||
debug: false
|
||||
});
|
||||
}
|
||||
Debug.prototype._l = function (level, args) {
|
||||
if(!this.options.debug){
|
||||
return;
|
||||
}
|
||||
args = _.values(args);
|
||||
|
||||
var prints = [chalk.bgBlack.grey(this.options.namespace)];
|
||||
var prefix, color;
|
||||
switch (level) {
|
||||
case 'e':
|
||||
prefix = 'ERR!', color = 'red';
|
||||
break;
|
||||
case 'w':
|
||||
prefix = 'warn', color = 'yellow';
|
||||
break;
|
||||
case 'd':
|
||||
if(this.options.timestamp){
|
||||
prints.push(chalk.underline.dim((new Date()).toISOString()))
|
||||
}
|
||||
break;
|
||||
default :
|
||||
prefix = args.splice(0, 1), color = 'green';
|
||||
break;
|
||||
}
|
||||
if(prefix && color){
|
||||
prints.splice(2, 0, chalk.bgBlack[color](prefix));
|
||||
}
|
||||
prints.push(args.join(' '));
|
||||
console.log.apply(null, prints);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loggers: info, error, debug, log, warn.
|
||||
*/
|
||||
['i', 'e', 'd', 'l', 'w'].forEach(function(s){
|
||||
Debug.prototype[s] = function(){
|
||||
this._l.call(this, s, arguments);
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
var chalk = require('chalk');
|
||||
|
||||
module.exports = function (options) {
|
||||
if (console.__hacked) {
|
||||
return;
|
||||
}
|
||||
options = options || {};
|
||||
var hacks = ['debug', 'log', 'info', 'warn', 'error'],
|
||||
colors = ['grey', '', 'green', 'yellow', 'red'],
|
||||
consoled = {},
|
||||
lev = options.level;
|
||||
|
||||
if ((typeof lev == 'string' && !(lev = hacks[lev])) || (isFinite(lev) && (lev < 0 || lev > hacks.length))) {
|
||||
options.level = 0;
|
||||
}
|
||||
|
||||
hacks.forEach(function (method) {
|
||||
if (method == 'debug') {
|
||||
consoled.debug = console.log;
|
||||
return;
|
||||
}
|
||||
consoled[method] = console[method];
|
||||
});
|
||||
|
||||
hacks.forEach(function (method, index) {
|
||||
console[method] = function () {
|
||||
if (index < options.level) {
|
||||
return;
|
||||
}
|
||||
if (method != 'log' && arguments.length > 0) {
|
||||
arguments[0] = (options.prefix ? chalk.bold[colors[index]]('[' + method.toUpperCase() + '] ') : '') +
|
||||
(options.date ? (new Date()).toLocaleString() + ' ' : '') + arguments[0];
|
||||
}
|
||||
consoled[method].apply(console, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
console.__hacked = true;
|
||||
};
|
||||
30
package.json
30
package.json
|
|
@ -2,11 +2,12 @@
|
|||
"name": "pm2-gui",
|
||||
"version": "0.1.1",
|
||||
"description": "An elegant web & terminal interface for Unitech/PM2.",
|
||||
"main": "./pm2-gui.js",
|
||||
"scripts": {
|
||||
"test": "NODE_ENV=test bash test/index.sh"
|
||||
},
|
||||
"bin": {
|
||||
"pm2-gui": "bin/pm2-gui"
|
||||
"pm2-gui": "./pm2-gui"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -34,24 +35,23 @@
|
|||
"node >= 0.8.0"
|
||||
],
|
||||
"dependencies": {
|
||||
"async": "~0.9.0",
|
||||
"lodash": "~2.4.1",
|
||||
"commander": "~2.5.0",
|
||||
"chalk": "~1.0.0",
|
||||
"express": "~4.10.1",
|
||||
"async": "~1.4.2",
|
||||
"lodash": "~3.10.1",
|
||||
"commander": "~2.9.0",
|
||||
"chalk": "~1.1.0",
|
||||
"express": "~4.13.3",
|
||||
"express-session": "~1.12.1",
|
||||
"swig": "~1.4.2",
|
||||
"windows-cpu": "~0.1.1",
|
||||
"socket.io": "~1.2.0",
|
||||
"windows-cpu": "~0.1.4",
|
||||
"socket.io": "~1.3.7",
|
||||
"pm2-axon": "~2.0.7",
|
||||
"pm2-axon-rpc": "~0.3.6",
|
||||
"ansi-html": "~0.0.4",
|
||||
"express-session": "~1.9.3",
|
||||
"pidusage": "~0.1.0",
|
||||
"blessed": "^0.1.60",
|
||||
"socket.io-client": "^1.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ansi-html": "~0.0.5",
|
||||
"pidusage": "~1.0.0",
|
||||
"blessed": "^0.1.81",
|
||||
"socket.io-client": "^1.3.7"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"readmeFilename": "README.md",
|
||||
"homepage": "https://github.com/Tjatse/pm2-gui"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
#!/bin/bash
|
||||
node="$(which node)"
|
||||
pidfile="pm2-gui.pid"
|
||||
prefixINFO="\033[1;32m[INFO]\033[0m"
|
||||
prefixWARN="\033[1;33m[WARNING]\033[0m"
|
||||
|
||||
function isRunning() {
|
||||
if [ ! -f $pidfile ];
|
||||
then
|
||||
return 0;
|
||||
fi
|
||||
if [ ! -s $pidfile ];
|
||||
then
|
||||
rm $pidfile;
|
||||
return 0;
|
||||
fi
|
||||
if test $(ps -p $(cat $pidfile) | wc -l) -gt 1;
|
||||
then
|
||||
return 1;
|
||||
fi
|
||||
rm $pidfile;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function status() {
|
||||
isRunning;
|
||||
if [ 0 -eq $? ];
|
||||
then
|
||||
echo -e "$prefixWARN \033[31m✘ stopped\033[0m";
|
||||
else
|
||||
echo -e "$prefixINFO \033[32m✔ running\033[0m";
|
||||
fi
|
||||
}
|
||||
|
||||
function usage () {
|
||||
cat <<-EOF
|
||||
|
||||
Usage: $0 <cmd> [options]
|
||||
|
||||
Commands:
|
||||
start [config_file] start the service
|
||||
stop stop the running service
|
||||
restart restart the service
|
||||
mon run the curses-like dashboard in terminal
|
||||
logs [log_directory] view the logs
|
||||
|
||||
Examples:
|
||||
$0 start
|
||||
$0 start /path/to/my-pm2-gui.ini
|
||||
$0 mon
|
||||
$0 logs
|
||||
$0 logs /path/to/logs
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
isRunning;
|
||||
if [ 0 -eq $? ];
|
||||
then
|
||||
echo -e "$prefixINFO Starting..."
|
||||
$node ./lib/daemon "$@"
|
||||
sleep 1;
|
||||
fi
|
||||
status;
|
||||
;;
|
||||
|
||||
stop)
|
||||
isRunning;
|
||||
if [ 0 -eq $? ];
|
||||
then
|
||||
echo -e "$prefixWARN Already stopped.";
|
||||
else
|
||||
echo -e "$prefixINFO Stopping..."
|
||||
kill $(cat $pidfile);
|
||||
sleep 1;
|
||||
status;
|
||||
fi
|
||||
;;
|
||||
|
||||
restart)
|
||||
isRunning;
|
||||
if [ 0 -eq $? ];
|
||||
then
|
||||
echo -e "$prefixWARN PID not found, no need to stop anything.";
|
||||
$node ./lib/daemon "$@"
|
||||
else
|
||||
kill -SIGHUP $(cat $pidfile);
|
||||
fi
|
||||
sleep 1;
|
||||
status;
|
||||
;;
|
||||
|
||||
logs)
|
||||
out="pm2-gui.out";
|
||||
err="pm2-gui.err";
|
||||
if [ ! -n "$2" ];
|
||||
then
|
||||
out="./logs/$out";
|
||||
err="./logs/$err";
|
||||
else
|
||||
out="$2/$out";
|
||||
err="$2/$err";
|
||||
fi
|
||||
if [ ! -f $out ] && [ ! -f $err ];
|
||||
then
|
||||
echo -e "$prefixWARN Logs can not be found in directory \033[1m$2\033[0m.";
|
||||
else
|
||||
echo -e "$prefixINFO Logs from \033[1m$out\033[0m and \033[1m$err\033[0m:";
|
||||
tail -n 20 -F $out $err;
|
||||
fi
|
||||
;;
|
||||
|
||||
status)
|
||||
status;
|
||||
;;
|
||||
|
||||
*)
|
||||
usage "$0";
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
13
pm2-gui.ini
13
pm2-gui.ini
|
|
@ -1,4 +1,11 @@
|
|||
refresh = 5000
|
||||
node = LocalPM2
|
||||
pm2 = ~/.pm2
|
||||
debug = true
|
||||
port = 8088
|
||||
refresh = 5000
|
||||
port = 8088
|
||||
daemonize = false
|
||||
|
||||
[log]
|
||||
dir = ./logs
|
||||
prefix = true
|
||||
date = false
|
||||
level = debug
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
var Monitor = require('./lib/monitor'),
|
||||
chalk = require('chalk'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
_ = require('lodash'),
|
||||
Log = require('./lib/util/log');
|
||||
|
||||
exports.start = function (options) {
|
||||
process.title = 'pm2-gui slave';
|
||||
options = options || {};
|
||||
var confFile = options.confFile;
|
||||
if (!confFile) {
|
||||
confFile = './pm2-gui.ini';
|
||||
if (process.argv.length > 2) {
|
||||
confFile = process.argv[2];
|
||||
}
|
||||
confFile = path.resolve(__dirname, confFile);
|
||||
|
||||
if (!fs.existsSync(confFile)) {
|
||||
console.error(chalk.bold(confFile), 'does not exist!');
|
||||
return process.exit(0);
|
||||
}
|
||||
}
|
||||
var monitor = Monitor({
|
||||
confFile: confFile
|
||||
});
|
||||
|
||||
Log(monitor.options.log);
|
||||
|
||||
console.log(chalk.cyan(
|
||||
'\n' +
|
||||
'█▀▀█ █▀▄▀█ █▀█ ░░ █▀▀▀ █░░█ ░▀░\n' +
|
||||
'█░░█ █░▀░█ ░▄▀ ▀▀ █░▀█ █░░█ ▀█▀\n' +
|
||||
'█▀▀▀ ▀░░░▀ █▄▄ ░░ ▀▀▀▀ ░▀▀▀ ▀▀▀\n'));
|
||||
monitor.run();
|
||||
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGHUP', restart);
|
||||
process.on('uncaughtException', caughtException);
|
||||
process.on('exit', exports.exitGraceful)
|
||||
|
||||
function shutdown(code, signal) {
|
||||
console.info('Shutting down....');
|
||||
monitor.quit();
|
||||
console.info('Both', chalk.bold('pm2-emitter'), 'and', chalk.bold('statsd dgram'), 'sockets are closed.');
|
||||
console.info('Shutdown complete!');
|
||||
exports.exitGraceful(code, '-f');
|
||||
}
|
||||
|
||||
function restart() {
|
||||
if (process.send) {
|
||||
process.send({
|
||||
action: 'restart'
|
||||
});
|
||||
} else {
|
||||
console.error('No IPC found, could not restart monitor, shutting down.');
|
||||
shutdown(1);
|
||||
}
|
||||
}
|
||||
|
||||
function caughtException(err) {
|
||||
console.error(err.stack);
|
||||
shutdown(1);
|
||||
}
|
||||
};
|
||||
|
||||
exports.exitGraceful = function exit(code, signal) {
|
||||
code = code || 0;
|
||||
if (signal != '-f') {
|
||||
console.debug('Slave has exited, code: ' + code + ', signal: ' + (signal || 'NULL'));
|
||||
}
|
||||
var fds = 0;
|
||||
|
||||
function tryToExit() {
|
||||
if ((fds & 1) && (fds & 2)) {
|
||||
process.exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
[process.stdout, process.stderr].forEach(function (std) {
|
||||
var fd = std.fd;
|
||||
if (!std.bufferSize) {
|
||||
fds = fds | fd;
|
||||
} else {
|
||||
std.write && std.write('', function () {
|
||||
fds = fds | fd;
|
||||
tryToExit();
|
||||
});
|
||||
}
|
||||
});
|
||||
tryToExit();
|
||||
};
|
||||
if (path.basename(process.mainModule.filename, '.js') == 'pm2-ant') {
|
||||
exports.start();
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* Overidde PM2 configuration
|
||||
*/
|
||||
|
||||
var p = require('path');
|
||||
|
||||
module.exports = function(DEFAULT_HOME) {
|
||||
|
||||
if (!DEFAULT_HOME)
|
||||
return false;
|
||||
|
||||
var PM2_HOME = DEFAULT_HOME;
|
||||
|
||||
var pm2_conf = {
|
||||
PM2_HOME : PM2_HOME,
|
||||
|
||||
PM2_LOG_FILE_PATH : p.join(PM2_HOME, 'pm2.log'),
|
||||
PM2_PID_FILE_PATH : p.join(PM2_HOME, 'pm2.pid'),
|
||||
|
||||
DEFAULT_PID_PATH : p.join(PM2_HOME, 'pids'),
|
||||
DEFAULT_LOG_PATH : p.join(PM2_HOME, 'logs'),
|
||||
DUMP_FILE_PATH : p.join(PM2_HOME, 'dump.pm2'),
|
||||
|
||||
DAEMON_RPC_PORT : p.join(PM2_HOME, 'rpc.sock'),
|
||||
DAEMON_PUB_PORT : p.join(PM2_HOME, 'pub.sock'),
|
||||
INTERACTOR_RPC_PORT : p.join(PM2_HOME, 'interactor.sock'),
|
||||
|
||||
GRACEFUL_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_TIMEOUT) || 8000,
|
||||
GRACEFUL_LISTEN_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_LISTEN_TIMEOUT) || 4000,
|
||||
|
||||
DEBUG : process.env.PM2_DEBUG || false,
|
||||
WEB_INTERFACE : parseInt(process.env.PM2_API_PORT) || 9615,
|
||||
MODIFY_REQUIRE : process.env.PM2_MODIFY_REQUIRE || false,
|
||||
|
||||
PM2_LOG_DATE_FORMAT : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DD HH:mm:ss',
|
||||
|
||||
INTERACTOR_LOG_FILE_PATH : p.join(PM2_HOME, 'agent.log'),
|
||||
INTERACTOR_PID_PATH : p.join(PM2_HOME, 'agent.pid'),
|
||||
INTERACTION_CONF : p.join(PM2_HOME, 'agent.json5')
|
||||
};
|
||||
|
||||
return pm2_conf || null;
|
||||
};
|
||||
|
|
@ -2,7 +2,7 @@ var express = require('express'),
|
|||
swig = require('swig'),
|
||||
path = require('path'),
|
||||
chalk = require('chalk'),
|
||||
Monitor = require('../lib/mon'),
|
||||
Monitor = require('../lib/monitor'),
|
||||
Debug = require('../lib/util/debug'),
|
||||
session = require('express-session');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue