feature: merge std(out && err)

This commit is contained in:
Tjatse 2014-11-17 17:17:49 +08:00
parent aae6ffe938
commit 0c6c089f4f
6 changed files with 164 additions and 79 deletions

View File

@ -83,6 +83,8 @@ CLI.start = function(script, opts, cb) {
appConf.max_memory_restart = opts.maxMemoryRestart;
if (opts.instances)
appConf['instances'] = opts.instances;
if (opts.log)
appConf['log_file'] = opts.log;
if (opts.error)
appConf['error_file'] = opts.error;
if (opts.output)

View File

@ -79,6 +79,9 @@ UX.describeTable = function(process) {
{ 'uptime' : (pm2_env.pm_uptime && pm2_env.status == 'online') ? timeSince(pm2_env.pm_uptime) : 0 },
{ 'created at' : created_at }
);
if('pm_log_path' in pm2_env){
table.splice(6, 0, {'entire log path': pm2_env.pm_log_path});
}
console.log(table.toString());
if (pm2_env.versioning) {

View File

@ -103,12 +103,17 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
if (fs.existsSync(app.pm_exec_path) == false) {
return new Error('script not found : ' + app.pm_exec_path);
}
if (app.log_file){
app["pm_log_path"] = typeof app.log_file == 'boolean' ?
path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '.log'].join(''))
: path.resolve(cwd, app.log_file);
}
delete app.log_file;
if (app.out_file)
app["pm_out_log_path"] = path.resolve(cwd, app.out_file);
else {
app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-out.log'].join(''));
app.out_file = app["pm_out_log_path"];
}
delete app.out_file;
@ -116,7 +121,6 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
app["pm_err_log_path"] = path.resolve(cwd, app.error_file);
else {
app["pm_err_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-err.log'].join(''));
app.error_file = app["pm_err_log_path"];
}
delete app.error_file;

View File

@ -121,8 +121,10 @@ God.executeApp = function executeApp(env, cb) {
// If merge option, dont separate the logs
if (!env_copy['merge_logs']) {
env_copy.pm_out_log_path = env_copy.pm_out_log_path.replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log');
env_copy.pm_err_log_path = env_copy.pm_err_log_path.replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log');
['', '_out', '_err'].forEach(function(k){
var key = 'pm' + k + '_log_path';
env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log'));
})
}
// Initiate watch file

View File

@ -8,6 +8,7 @@
var log = require('debug')('pm2:god');
var fs = require('fs');
var async = require('async');
var cst = require('../../constants.js');
var moment = require('moment');
var Common = require('../Common');
@ -63,10 +64,15 @@ module.exports = function ForkMode(God) {
args = args.concat(eval((pm2_env.args)));
}
var stdout, stderr;
var outFile = pm2_env.pm_out_log_path;
var errFile = pm2_env.pm_err_log_path;
// piping stream o file
var stds = {
out: pm2_env.pm_out_log_path,
err: pm2_env.pm_err_log_path
};
// entire log std if necessary.
if('pm_log_path' in pm2_env){
stds.std = pm2_env.pm_log_path;
}
/**
* Description
@ -75,24 +81,44 @@ module.exports = function ForkMode(God) {
* @return
*/
function startLogging(cb) {
stdout = fs.createWriteStream(outFile, { flags : 'a' });
stdout.on('error', function(e) {
God.logAndGenerateError(e);
return cb(e);
// waterfall.
var flows = [];
// types of stdio, should be sorted as `std(entire log)`, `out`, `err`.
var types = Object.keys(stds).sort(function(x, y){
return -x.charCodeAt(0) + y.charCodeAt(0);
});
stdout.on('open', function() {
stderr = fs.createWriteStream(errFile, { flags : 'a' });
// Create write streams.
(function createWS(io){
if(io.length != 1){
return;
}
io = io[0];
stderr.on('error', function(e) {
God.logAndGenerateError(e);
return cb(e);
});
// If `std` is a Stream type, try next `std`.
// compatible with `pm2 reloadLogs`
if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){
return createWS(types.splice(0, 1));
}
stderr.on('open', function() {
return cb(null);
flows.push(function(next){
var file = stds[io];
stds[io] = fs.createWriteStream(file, {flags: 'a'})
.on('error', function(e){
next(e);
}).on('open', function(){
next();
});
stds[io]._file = file;
});
createWS(types.splice(0, 1));
})(types.splice(0, 1));
async.waterfall(flows, function(err, result){
if(err){
God.logAndGenerateError(err);
}
cb(err);
});
}
@ -125,7 +151,8 @@ module.exports = function ForkMode(God) {
if (pm2_env.log_date_format)
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
stderr.write(log_data);
stds.err.write(log_data);
stds.std && stds.std.write(log_data);
God.bus.emit('log:err', {
process : Common.formatCLU(cspr),
@ -142,7 +169,8 @@ module.exports = function ForkMode(God) {
if (pm2_env.log_date_format)
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
stdout.write(log_data);
stds.out.write(log_data);
stds.std && stds.std.write(log_data);
God.bus.emit('log:out', {
process : Common.formatCLU(cspr),
@ -177,14 +205,18 @@ module.exports = function ForkMode(God) {
cspr.once('close', function forkClose(status) {
try {
stderr.close();
stdout.close();
for(var k in stds){
stds[k].close();
stds[k] = stds[k]._file;
}
} catch(e) { God.logAndGenerateError(e);}
});
cspr._reloadLogs = function(cb) {
stdout.close();
stderr.close();
for(var k in stds){
stds[k].close();
stds[k] = stds[k]._file;
}
startLogging(cb);
};

View File

@ -8,6 +8,7 @@ if (process.env.name != null)
var fs = require('fs');
var p = require('path');
var async = require('async');
var cst = require('../constants');
var axm = require('axm');
/**
@ -18,6 +19,7 @@ var axm = require('axm');
var fs = require('fs');
var worker = require('cluster').worker;
var stdFile = process.env.pm_log_path;
var outFile = process.env.pm_out_log_path;
var errFile = process.env.pm_err_log_path;
var pmId = process.env.pm_id;
@ -41,8 +43,13 @@ var axm = require('axm');
if (process.env.args != null)
process.argv = process.argv.concat(eval(process.env.args));
exec(script, outFile, errFile);
// stdio, including: out, err and entire (both out and err if necessary).
var stds = {
out: outFile,
err: errFile
};
stdFile && (stds.std = stdFile);
exec(script, stds);
if (cronRestart)
cronize(cronRestart);
@ -78,21 +85,20 @@ function cronize(cron_pattern) {
* Description
* @method exec
* @param {} script
* @param {} outFile
* @param {} errFile
* @param {} stds
* @return
*/
function exec(script, outFile, errFile) {
var stderr, stdout;
function exec(script, stds) {
if (p.extname(script) == '.coffee') {
require('coffee-script/register');
}
process.on('message', function (msg) {
if (msg.type === 'log:reload') {
stdout.end();
stderr.end();
for(var k in stds){
stds[k].close();
stds[k] = stds[k]._file;
}
startLogging(function () {
console.log('Reloading log...');
});
@ -112,53 +118,91 @@ function exec(script, outFile, errFile) {
* @return
*/
function startLogging(callback) {
stdout = fs.createWriteStream(outFile, { flags : 'a' });
stdout.on('open', function() {
stderr = fs.createWriteStream(errFile, { flags : 'a' });
stderr.on('open', function() {
process.stderr.write = (function(write) {
return function(string, encoding, fd) {
var log_data = string.toString();
if (process.env.log_date_format && moment)
log_data = moment().format(process.env.log_date_format) + ': ' + log_data;
stderr.write(log_data);
process.send({
type : 'log:err',
data : string
});
};
}
)(process.stderr.write);
process.stdout.write = (function(write) {
return function(string, encoding, fd) {
var log_data = string.toString();
if (process.env.log_date_format && moment)
log_data = moment().format(process.env.log_date_format) + ': ' + log_data;
stdout.write(log_data);
process.send({
type : 'log:out',
data : string
});
};
})(process.stdout.write);
return callback();
});
// waterfall.
var flows = [];
// types of stdio, should be sorted as `std(entire log)`, `out`, `err`.
var types = Object.keys(stds).sort(function(x, y){
return -x.charCodeAt(0) + y.charCodeAt(0);
});
// Create write streams.
(function createWS(io){
if(io.length != 1){
return;
}
io = io[0];
// If `std` is a Stream type, try next `std`.
// compatible with `pm2 reloadLogs`
if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){
return createWS(types.splice(0, 1));
}
flows.push(function(next){
var file = stds[io];
stds[io] = fs.createWriteStream(file, {flags: 'a'})
.on('error', function(e){
next(e);
}).on('open', function(){
next();
});
stds[io]._file = file;
});
createWS(types.splice(0, 1));
})(types.splice(0, 1));
async.waterfall(flows, callback);
}
startLogging(function () {
startLogging(function (err) {
if(err){
process.send({
type : 'process:exception',
data : {
message: err.message,
syscall: 'ProcessContainer.startLogging'
}
});
return;
}
process.stderr.write = (function(write) {
return function(string, encoding, fd) {
var log_data = string.toString();
if (process.env.log_date_format && moment)
log_data = moment().format(process.env.log_date_format) + ': ' + log_data;
stds.err.write(log_data);
stds.std && stds.std.write(log_data);
process.send({
type : 'log:err',
data : string
});
};
}
)(process.stderr.write);
process.stdout.write = (function(write) {
return function(string, encoding, fd) {
var log_data = string.toString();
if (process.env.log_date_format && moment)
log_data = moment().format(process.env.log_date_format) + ': ' + log_data;
stds.out.write(log_data);
stds.std && stds.std.write(log_data);
process.send({
type : 'log:out',
data : string
});
};
})(process.stdout.write);
process.on('uncaughtException', function uncaughtListener(err) {
try {
stderr.write(err.stack);
} catch(e) {
function logError(types, error){
try {
stderr.write(err.toString());
} catch(e) {}
types.forEach(function(type){
stds[type].write(error + '\n');
});
} catch(e) { }
}
logError(['std', 'err'], err.stack);
// Notify master that an uncaughtException has been catched
try {
@ -174,9 +218,7 @@ function exec(script, outFile, errFile) {
data : errObj
});
} catch(e) {
try {
stderr.write('Channel is already closed can\'t broadcast error', err);
} catch(e) {}
logError(['std', 'err'], 'Channel is already closed can\'t broadcast error:\n' + e.stack);
}
if (!process.listeners('uncaughtException').filter(function (listener) {