ProcessContainer.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * Copyright 2013-2022 the PM2 project authors. All rights reserved.
  3. * Use of this source code is governed by a license that
  4. * can be found in the LICENSE file.
  5. *
  6. * This file wrap target application
  7. * - redirect stdin, stderr to bus + log files
  8. * - rename process
  9. * - pid
  10. */
  11. var p = require('path');
  12. var cst = require('../constants');
  13. var Utility = require('./Utility.js');
  14. var ProcessUtils = require('./ProcessUtils');
  15. // Load all env-vars from master.
  16. var pm2_env = JSON.parse(process.env.pm2_env);
  17. for(var k in pm2_env) {
  18. process.env[k] = pm2_env[k];
  19. }
  20. // Rename process
  21. process.title = process.env.PROCESS_TITLE || 'node ' + pm2_env.pm_exec_path;
  22. delete process.env.pm2_env;
  23. /**
  24. * Main entrance to wrap the desired code
  25. */
  26. (function ProcessContainer() {
  27. var fs = require('fs');
  28. ProcessUtils.injectModules()
  29. var stdFile = pm2_env.pm_log_path;
  30. var outFile = pm2_env.pm_out_log_path;
  31. var errFile = pm2_env.pm_err_log_path;
  32. var pidFile = pm2_env.pm_pid_path;
  33. var script = pm2_env.pm_exec_path;
  34. var original_send = process.send;
  35. if (typeof(process.env.source_map_support) != 'undefined' &&
  36. process.env.source_map_support !== 'false') {
  37. require('source-map-support').install();
  38. }
  39. process.send = function() {
  40. if (process.connected)
  41. original_send.apply(this, arguments);
  42. };
  43. //send node version
  44. if (process.versions && process.versions.node) {
  45. process.send({
  46. 'node_version': process.versions.node
  47. });
  48. }
  49. if (cst.MODIFY_REQUIRE)
  50. require.main.filename = pm2_env.pm_exec_path;
  51. // Resets global paths for require()
  52. require('module')._initPaths();
  53. try {
  54. var pid = process.pid
  55. if (typeof(pid) !== 'undefined')
  56. fs.writeFileSync(pidFile, process.pid.toString());
  57. } catch (e) {
  58. console.error(e.stack || e);
  59. }
  60. // Add args to process if args specified on start
  61. if (process.env.args != null)
  62. process.argv = process.argv.concat(pm2_env.args);
  63. // stdio, including: out, err and entire (both out and err if necessary).
  64. var stds = {
  65. out: outFile,
  66. err: errFile
  67. };
  68. stdFile && (stds.std = stdFile);
  69. // uid/gid management
  70. if (pm2_env.uid || pm2_env.gid) {
  71. try {
  72. if (process.env.gid)
  73. process.setgid(pm2_env.gid);
  74. if (pm2_env.uid)
  75. process.setuid(pm2_env.uid);
  76. } catch(e) {
  77. setTimeout(function() {
  78. console.error('%s on call %s', e.message, e.syscall);
  79. console.error('%s is not accessible', pm2_env.uid);
  80. return process.exit(1);
  81. }, 100);
  82. }
  83. }
  84. exec(script, stds);
  85. })();
  86. /**
  87. * Description
  88. * @method exec
  89. * @param {} script
  90. * @param {} stds
  91. * @return
  92. */
  93. function exec(script, stds) {
  94. if (p.extname(script) == '.coffee') {
  95. try {
  96. require('coffee-script/register');
  97. } catch (e) {
  98. console.error('Failed to load CoffeeScript interpreter:', e.message || e);
  99. }
  100. }
  101. if (p.extname(script) == '.ls') {
  102. try {
  103. require('livescript');
  104. } catch (e) {
  105. console.error('Failed to load LiveScript interpreter:', e.message || e);
  106. }
  107. }
  108. if (p.extname(script) == '.ts' || p.extname(script) == '.tsx') {
  109. try {
  110. require('ts-node/register');
  111. } catch (e) {
  112. console.error('Failed to load Typescript interpreter:', e.message || e);
  113. }
  114. }
  115. process.on('message', function (msg) {
  116. if (msg.type === 'log:reload') {
  117. for (var k in stds){
  118. if (typeof stds[k] == 'object' && !isNaN(stds[k].fd)){
  119. if (stds[k].destroy) stds[k].destroy();
  120. else if (stds[k].end) stds[k].end();
  121. else if (stds[k].close) stds[k].close();
  122. stds[k] = stds[k]._file;
  123. }
  124. }
  125. Utility.startLogging(stds, function (err) {
  126. if (err)
  127. return console.error('Failed to reload logs:', err.stack);
  128. console.log('Reloading log...');
  129. });
  130. }
  131. });
  132. var dayjs = null;
  133. if (pm2_env.log_date_format)
  134. dayjs = require('dayjs');
  135. Utility.startLogging(stds, function (err) {
  136. if (err) {
  137. process.send({
  138. type : 'process:exception',
  139. data : {
  140. message: err.message,
  141. syscall: 'ProcessContainer.startLogging'
  142. }
  143. });
  144. throw err;
  145. return;
  146. }
  147. process.stderr.write = (function(write) {
  148. return function(string, encoding, cb) {
  149. var log_data = null;
  150. // Disable logs if specified
  151. if (pm2_env.disable_logs === true) {
  152. return cb ? cb() : false;
  153. }
  154. if (pm2_env.log_type && pm2_env.log_type === 'json') {
  155. log_data = JSON.stringify({
  156. message : string.toString(),
  157. timestamp : pm2_env.log_date_format && dayjs ?
  158. dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
  159. type : 'err',
  160. process_id : pm2_env.pm_id,
  161. app_name : pm2_env.name
  162. }) + '\n';
  163. }
  164. else if (pm2_env.log_date_format && dayjs)
  165. log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;
  166. else
  167. log_data = string.toString();
  168. process.send({
  169. type : 'log:err',
  170. topic : 'log:err',
  171. data : log_data
  172. });
  173. if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&
  174. (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
  175. return cb ? cb() : false;
  176. stds.std && stds.std.write && stds.std.write(log_data, encoding);
  177. stds.err && stds.err.write && stds.err.write(log_data, encoding, cb);
  178. };
  179. })(process.stderr.write);
  180. process.stdout.write = (function(write) {
  181. return function(string, encoding, cb) {
  182. var log_data = null;
  183. // Disable logs if specified
  184. if (pm2_env.disable_logs === true) {
  185. return cb ? cb() : false;
  186. }
  187. if (pm2_env.log_type && pm2_env.log_type === 'json') {
  188. log_data = JSON.stringify({
  189. message : string.toString(),
  190. timestamp : pm2_env.log_date_format && dayjs ?
  191. dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
  192. type : 'out',
  193. process_id : pm2_env.pm_id,
  194. app_name : pm2_env.name
  195. }) + '\n';
  196. }
  197. else if (pm2_env.log_date_format && dayjs)
  198. log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;
  199. else
  200. log_data = string.toString();
  201. process.send({
  202. type : 'log:out',
  203. data : log_data
  204. });
  205. if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
  206. (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
  207. return cb ? cb() : null;
  208. stds.std && stds.std.write && stds.std.write(log_data, encoding);
  209. stds.out && stds.out.write && stds.out.write(log_data, encoding, cb);
  210. };
  211. })(process.stdout.write);
  212. function getUncaughtExceptionListener(listener) {
  213. return function uncaughtListener(err) {
  214. var error = err && err.stack ? err.stack : err;
  215. if (listener === 'unhandledRejection') {
  216. error = 'You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:\n' + error;
  217. }
  218. logError(['std', 'err'], error);
  219. // Notify master that an uncaughtException has been catched
  220. try {
  221. if (err) {
  222. var errObj = {};
  223. Object.getOwnPropertyNames(err).forEach(function(key) {
  224. errObj[key] = err[key];
  225. });
  226. }
  227. process.send({
  228. type : 'log:err',
  229. topic : 'log:err',
  230. data : '\n' + error + '\n'
  231. });
  232. process.send({
  233. type : 'process:exception',
  234. data : errObj !== undefined ? errObj : {message: 'No error but ' + listener + ' was caught!'}
  235. });
  236. } catch(e) {
  237. logError(['std', 'err'], 'Channel is already closed can\'t broadcast error:\n' + e.stack);
  238. }
  239. if (!process.listeners(listener).filter(function (listener) {
  240. return listener !== uncaughtListener;
  241. }).length) {
  242. if (listener == 'uncaughtException') {
  243. process.emit('disconnect');
  244. process.exit(cst.CODE_UNCAUGHTEXCEPTION);
  245. }
  246. }
  247. }
  248. }
  249. process.on('uncaughtException', getUncaughtExceptionListener('uncaughtException'));
  250. process.on('unhandledRejection', getUncaughtExceptionListener('unhandledRejection'));
  251. // Change dir to fix process.cwd
  252. process.chdir(pm2_env.pm_cwd || process.env.PWD || p.dirname(script));
  253. if (ProcessUtils.isESModule(script) === true)
  254. import(process.env.pm_exec_path);
  255. else
  256. require('module')._load(script, null, true);
  257. function logError(types, error){
  258. try {
  259. types.forEach(function(type){
  260. stds[type] && typeof stds[type].write == 'function' && stds[type].write(error + '\n');
  261. });
  262. } catch(e) { }
  263. }
  264. });
  265. }