/** * 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. */ // ProcessContainer.js // Child wrapper. Redirect output to files, assign pid & co. // by Strzelewicz Alexandre var p = require('path'); var cst = require('../constants'); var Utility = require('./Utility.js'); // Load all env-vars from master. var pm2_env = JSON.parse(process.env.pm2_env); for(var k in pm2_env){ process.env[k] = pm2_env[k]; } // Rename process process.title = 'node ' + pm2_env.pm_exec_path; delete process.env.pm2_env; /** * Main entrance to wrap the desired code */ (function ProcessContainer() { var fs = require('fs'); if (process.env.pmx !== "false") require('pmx').init(); var stdFile = pm2_env.pm_log_path; var outFile = pm2_env.pm_out_log_path; var errFile = pm2_env.pm_err_log_path; var pmId = pm2_env.pm_id; var pidFile = pm2_env.pm_pid_path; var script = pm2_env.pm_exec_path; var cronRestart = pm2_env.cron_restart; var original_send = process.send; if (typeof(process.env.source_map_support) != "undefined" && process.env.source_map_support !== "false") { require('source-map-support').install(); } process.send = function() { if (process.connected) original_send.apply(this, arguments); }; //send node version if (process.versions && process.versions.node) { process.send({ 'node_version': process.versions.node }); } if (cst.MODIFY_REQUIRE) require.main.filename = pm2_env.pm_exec_path; // Resets global paths for require() require('module')._initPaths(); try { fs.writeFileSync(pidFile, process.pid); } catch (e) { console.error(e.stack || e); } // Add args to process if args specified on start if (process.env.args != null) process.argv = process.argv.concat(pm2_env.args); // 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); })(); /** * Cron restart */ function cronize(cron_pattern) { var cronJob = require('cron').CronJob; var job = new cronJob({ cronTime: cron_pattern, onTick: function() { if (process.connected && process.send){ process.send({ 'cron_restart': 1 }); } else { process.emit('disconnect'); process.nextTick(function() { process.exit(0); }); } }, start: false }); job.start(); } /** * Description * @method exec * @param {} script * @param {} stds * @return */ function exec(script, stds) { if (p.extname(script) == '.coffee') { try { require('coffee-script/register'); } catch (e) { console.error('Failed to load CoffeeScript interpreter:', e.stack || e); } } if (p.extname(script) == '.ls') { try { require('livescript'); } catch (e) { console.error('Failed to load LiveScript interpreter:', e.stack || e); } } if (p.extname(script) == '.ts') { try { require('ts-node/register'); } catch (e) { console.error('Failed to load Typescript interpreter:', e.stack || e); } } process.on('message', function (msg) { if (msg.type === 'log:reload') { for(var k in stds){ if(typeof stds[k] == 'object' && !isNaN(stds[k].fd)){ if (stds[k].destroy) stds[k].destroy(); else if (stds[k].end) stds[k].end(); else if (stds[k].close) stds[k].close(); stds[k] = stds[k]._file; } } Utility.startLogging(stds, function (err) { if(err){ console.error('Failed to reload logs:', err.stack); }else { console.log('Reloading log...'); } }); } }); var moment = null; if (pm2_env.log_date_format) moment = require('moment'); Utility.startLogging(stds, function (err) { if (err) { process.send({ type : 'process:exception', data : { message: err.message, syscall: 'ProcessContainer.startLogging' } }); throw err; return; } process.stderr.write = (function(write) { return function(string, encoding, cb) { var log_data = string.toString(); if (pm2_env.log_date_format && moment) log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; stds.err.write && stds.err.write(log_data, encoding, cb); stds.std && stds.std.write && stds.std.write(log_data); process.send({ type : 'log:err', data : string }); }; } )(process.stderr.write); process.stdout.write = (function(write) { return function(string, encoding, cb) { var log_data = string.toString(); if (pm2_env.log_date_format && moment) log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; stds.out.write && stds.out.write(log_data, encoding, cb); stds.std && stds.std.write && stds.std.write(log_data); process.send({ type : 'log:out', data : string }); }; })(process.stdout.write); function getUncaughtExceptionListener(listener) { return function uncaughtListener(err) { var error = err && err.stack ? err.stack : err; if (listener === 'unhandledRejection') { error = 'You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:\n' + error; } logError(['std', 'err'], error); // Notify master that an uncaughtException has been catched try { if (err) { var errObj = {}; Object.getOwnPropertyNames(err).forEach(function(key) { errObj[key] = err[key]; }); } process.send({ type : 'log:err', data : '\n' + error + '\n' }); process.send({ type : 'process:exception', data : errObj !== undefined ? errObj : {message: 'No error but ' + listener + ' was caught!'} }); } catch(e) { logError(['std', 'err'], 'Channel is already closed can\'t broadcast error:\n' + e.stack); } if (!process.listeners(listener).filter(function (listener) { return listener !== uncaughtListener; }).length) { if (listener == 'uncaughtException') { process.emit('disconnect'); process.exit(cst.CODE_UNCAUGHTEXCEPTION); } } } } process.on('uncaughtException', getUncaughtExceptionListener('uncaughtException')); process.on('unhandledRejection', getUncaughtExceptionListener('unhandledRejection')); // Change dir to fix process.cwd process.chdir(pm2_env.pm_cwd || process.env.PWD || p.dirname(script)); require('module')._load(script, null, true); function logError(types, error){ try { types.forEach(function(type){ stds[type] && typeof stds[type].write == 'function' && stds[type].write(error + '\n'); }); } catch(e) { } } }); }