pm2/lib/API/Log.js
Serkan Yerşen 3ce8d65ebb
raw output should keep the original output stream.
in docker/kubernetes setup, error logging depends on errors being on stderr. My app does this by default but when PM2 takes over, all logs gets combined into stdout. Which makes really hard to automatically detect the error logs.
2017-11-27 16:53:54 -08:00

292 lines
8.2 KiB
JavaScript

/**
* Copyright 2013 the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*/
var fs = require('fs'),
util = require('util'),
chalk = require('chalk'),
async = require('async'),
moment = require('moment');
var Log = module.exports = {};
var DEFAULT_PADDING = ' ';
/**
* Tail logs from file stream.
* @param {Object} apps_list
* @param {Number} lines
* @param {Boolean} raw
* @param {Function} callback
* @return
*/
Log.tail = function(apps_list, lines, raw, callback) {
var that = this;
if (lines === 0 || apps_list.length === 0)
return callback && callback();
var count = 0;
var getLastLines = function (filename, lines, callback) {
var chunk = '';
var size = Math.max(0, fs.statSync(filename).size - (lines * 200));
var fd = fs.createReadStream(filename, {start : size});
fd.on('data', function(data) { chunk += data.toString(); });
fd.on('end', function() {
chunk = chunk.split('\n').slice(-(lines+1));
chunk.pop();
callback(chunk);
});
};
apps_list.sort(function(a, b) {
return (fs.existsSync(a.path) ? fs.statSync(a.path).mtime.valueOf() : 0) -
(fs.existsSync(b.path) ? fs.statSync(b.path).mtime.valueOf() : 0);
});
async.forEachLimit(apps_list, 1, function(app, next) {
if (!fs.existsSync(app.path || ''))
return next();
getLastLines(app.path, lines, function(output) {
console.log(chalk.grey('%s last %d lines:'), app.path, lines);
output.forEach(function(out) {
if (raw)
return app.type === 'err' ? console.error : console.log(out);
if (app.type === 'out')
process.stdout.write(chalk.green(pad(DEFAULT_PADDING, app.app_name) + ' | '));
else if (app.type === 'err')
process.stdout.write(chalk.red(pad(DEFAULT_PADDING, app.app_name) + ' | '));
else
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));
console.log(out);
});
if (output.length)
process.stdout.write('\n');
next();
});
}, function() {
callback && callback();
});
};
/**
* Stream logs in realtime from the bus eventemitter.
* @param {String} id
* @param {Boolean} raw
* @return
*/
Log.stream = function(Client, id, raw, timestamp, exclusive) {
var that = this;
Client.launchBus(function(err, bus, socket) {
socket.on('reconnect attempt', function() {
if (global._auto_exit === true) {
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(moment().format(timestamp) + ' ')));
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | ') + '[[[ Target PM2 killed. ]]]');
process.exit(0);
}
});
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (raw)
return process.stdout.write(util.format(line) + '\n');
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(moment().format(timestamp) + ' ')));
var name = packet.process.pm_id + '|' + packet.process.name;
if (type === 'out')
process.stdout.write(chalk.green(pad(DEFAULT_PADDING, name) + ' | '));
else if (type === 'err')
process.stdout.write(chalk.red(pad(DEFAULT_PADDING, name) + ' | '));
else if (!raw && (id === 'all' || id === 'PM2'))
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));
process.stdout.write(util.format(line) + '\n');
});
});
});
};
Log.devStream = function(Client, id, raw, timestamp, exclusive) {
var that = this;
Client.launchBus(function(err, bus) {
setTimeout(function() {
bus.on('process:event', function(packet) {
if (packet.event == 'online')
console.log(chalk.green('[rundev] App %s restarted'), packet.process.name);
});
}, 1000);
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
if (type === 'PM2')
return;
var name = packet.process.pm_id + '|' + packet.process.name;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (raw)
return process.stdout.write(util.format(line) + '\n');
if (timestamp)
process.stdout.write(chalk['dim'](chalk.grey(moment().format(timestamp) + ' ')));
var name = packet.process.name + '-' + packet.process.pm_id;
if (type === 'out')
process.stdout.write(chalk.green(pad(DEFAULT_PADDING, name) + ' | '));
else if (type === 'err')
process.stdout.write(chalk.red(pad(DEFAULT_PADDING, name) + ' | '));
else if (!raw && (id === 'all' || id === 'PM2'))
process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));
process.stdout.write(util.format(line) + '\n');
});
});
});
};
Log.jsonStream = function(Client, app_name) {
var that = this;
Client.launchBus(function(err, bus) {
if (err) console.error(err);
bus.on('process:event', function(packet) {
console.log(JSON.stringify({
timestamp : moment(packet.at),
type : 'process_event',
status : packet.event,
app_name : packet.process.name
}));
});
bus.on('log:*', function(type, packet) {
if (app_name != 'all' && app_name && app_name != packet.process.name) return false;
if (typeof(packet.data) == 'string')
packet.data = packet.data.replace(/(\r\n|\n|\r)/gm,'');
console.log(JSON.stringify({
message : packet.data,
timestamp : moment(packet.at),
type : type,
process_id : packet.process.pm_id,
app_name : packet.process.name
}));
});
});
};
Log.formatStream = function(Client, id, raw, timestamp, exclusive) {
var that = this;
Client.launchBus(function(err, bus) {
bus.on('log:*', function(type, packet) {
if (id !== 'all'
&& packet.process.name != id
&& packet.process.pm_id != id)
return;
if ((type === 'out' && exclusive === 'err')
|| (type === 'err' && exclusive === 'out')
|| (type === 'PM2' && exclusive !== false))
return;
if (type === 'PM2' && raw)
return;
var name = packet.process.name + '-' + packet.process.pm_id;
var lines;
if (typeof(packet.data) === 'string')
lines = (packet.data || '').split('\n');
else
return;
lines.forEach(function(line) {
if (!line || line.length === 0) return;
if (!raw) {
if (timestamp)
process.stdout.write('timestamp=' + moment().format(timestamp) + ' ');
if (packet.process.name === 'PM2')
process.stdout.write('app=pm2 ');
if (packet.process.name !== 'PM2')
process.stdout.write('app=' + packet.process.name + ' id=' + packet.process.pm_id + ' ');
if (type === 'out')
process.stdout.write('type=out ');
else if (type === 'err')
process.stdout.write('type=error ');
}
process.stdout.write('message=');
process.stdout.write(util.format(line) + '\n');
});
});
});
};
function printLines(lines) {
}
function pad(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}