1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | var log = require('debug')('pm2:fork_mode');
|
14 | var fs = require('fs');
|
15 | var Utility = require('../Utility.js');
|
16 | var path = require('path');
|
17 | var dayjs = require('dayjs');
|
18 | var semver = require('semver')
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | module.exports = function ForkMode(God) {
|
27 | |
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | God.forkMode = function forkMode(pm2_env, cb) {
|
36 | var command = '';
|
37 | var args = [];
|
38 |
|
39 | console.log(`App [${pm2_env.name}:${pm2_env.pm_id}] starting in -fork mode-`)
|
40 | var spawn = require('child_process').spawn;
|
41 |
|
42 | var interpreter = pm2_env.exec_interpreter || 'node';
|
43 | var pidFile = pm2_env.pm_pid_path;
|
44 |
|
45 | if (interpreter !== 'none') {
|
46 | command = interpreter;
|
47 |
|
48 | if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {
|
49 | args = args.concat(pm2_env.node_args);
|
50 | }
|
51 |
|
52 |
|
53 | if (process.env.PM2_NODE_OPTIONS) {
|
54 | args = args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
|
55 | }
|
56 |
|
57 | if (interpreter === 'node' || RegExp('node$').test(interpreter)) {
|
58 | args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerFork.js'));
|
59 | }
|
60 | else
|
61 | args.push(pm2_env.pm_exec_path);
|
62 | }
|
63 | else {
|
64 | command = pm2_env.pm_exec_path;
|
65 | args = [ ];
|
66 | }
|
67 |
|
68 | if (pm2_env.args) {
|
69 | args = args.concat(pm2_env.args);
|
70 | }
|
71 |
|
72 |
|
73 | var stds = {
|
74 | out: pm2_env.pm_out_log_path,
|
75 | err: pm2_env.pm_err_log_path
|
76 | };
|
77 |
|
78 |
|
79 | if ('pm_log_path' in pm2_env){
|
80 | stds.std = pm2_env.pm_log_path;
|
81 | }
|
82 |
|
83 | log("stds: %j", stds);
|
84 |
|
85 | Utility.startLogging(stds, function(err, result) {
|
86 | if (err) {
|
87 | God.logAndGenerateError(err);
|
88 | return cb(err);
|
89 | };
|
90 |
|
91 | try {
|
92 | var options = {
|
93 | env : pm2_env,
|
94 | detached : true,
|
95 | cwd : pm2_env.pm_cwd || process.cwd(),
|
96 | stdio : ['pipe', 'pipe', 'pipe', 'ipc']
|
97 | }
|
98 |
|
99 | if (typeof(pm2_env.windowsHide) === "boolean") {
|
100 | options.windowsHide = pm2_env.windowsHide;
|
101 | } else {
|
102 | options.windowsHide = true;
|
103 | }
|
104 |
|
105 | if (pm2_env.uid) {
|
106 | options.uid = pm2_env.uid
|
107 | }
|
108 |
|
109 | if (pm2_env.gid) {
|
110 | options.gid = pm2_env.gid
|
111 | }
|
112 |
|
113 | var cspr = spawn(command, args, options);
|
114 | } catch(e) {
|
115 | God.logAndGenerateError(e);
|
116 | return cb(e);
|
117 | }
|
118 |
|
119 | if (!cspr || !cspr.stderr || !cspr.stdout) {
|
120 | var fatalError = new Error('Process could not be forked properly, check your system health')
|
121 | God.logAndGenerateError(fatalError);
|
122 | return cb(fatalError);
|
123 | }
|
124 |
|
125 | cspr.process = {};
|
126 | cspr.process.pid = cspr.pid;
|
127 | cspr.pm2_env = pm2_env;
|
128 |
|
129 | function transformLogToJson(pm2_env, type, data) {
|
130 | return JSON.stringify({
|
131 | message : data.toString(),
|
132 | timestamp : pm2_env.log_date_format ? dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
|
133 | type : type,
|
134 | process_id : cspr.pm2_env.pm_id,
|
135 | app_name : cspr.pm2_env.name
|
136 | }) + '\n'
|
137 | }
|
138 |
|
139 | function prefixLogWithDate(pm2_env, data) {
|
140 | var log_data = []
|
141 | log_data = data.toString().split('\n')
|
142 | if (log_data.length > 1)
|
143 | log_data.pop()
|
144 | log_data = log_data.map(line => `${dayjs().format(pm2_env.log_date_format)}: ${line}\n`)
|
145 | log_data = log_data.join('')
|
146 | return log_data
|
147 | }
|
148 |
|
149 | cspr.stderr.on('data', function forkErrData(data) {
|
150 | var log_data = null;
|
151 |
|
152 |
|
153 | if (pm2_env.disable_logs === true) return false;
|
154 |
|
155 | if (pm2_env.log_type && pm2_env.log_type === 'json')
|
156 | log_data = transformLogToJson(pm2_env, 'err', data)
|
157 | else if (pm2_env.log_date_format)
|
158 | log_data = prefixLogWithDate(pm2_env, data)
|
159 | else
|
160 | log_data = data.toString();
|
161 |
|
162 | God.bus.emit('log:err', {
|
163 | process : {
|
164 | pm_id : cspr.pm2_env.pm_id,
|
165 | name : cspr.pm2_env.name,
|
166 | rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
|
167 | namespace : cspr.pm2_env.namespace
|
168 | },
|
169 | at : Utility.getDate(),
|
170 | data : log_data
|
171 | });
|
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 false;
|
176 | }
|
177 |
|
178 | stds.std && stds.std.write && stds.std.write(log_data);
|
179 | stds.err && stds.err.write && stds.err.write(log_data);
|
180 | });
|
181 |
|
182 | cspr.stdout.on('data', function forkOutData(data) {
|
183 | var log_data = null;
|
184 |
|
185 | if (pm2_env.disable_logs === true)
|
186 | return false;
|
187 |
|
188 | if (pm2_env.log_type && pm2_env.log_type === 'json')
|
189 | log_data = transformLogToJson(pm2_env, 'out', data)
|
190 | else if (pm2_env.log_date_format)
|
191 | log_data = prefixLogWithDate(pm2_env, data)
|
192 | else
|
193 | log_data = data.toString()
|
194 |
|
195 | God.bus.emit('log:out', {
|
196 | process : {
|
197 | pm_id : cspr.pm2_env.pm_id,
|
198 | name : cspr.pm2_env.name,
|
199 | rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
|
200 | namespace : cspr.pm2_env.namespace
|
201 | },
|
202 | at : Utility.getDate(),
|
203 | data : log_data
|
204 | });
|
205 |
|
206 | if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
|
207 | (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
|
208 | return false;
|
209 |
|
210 | stds.std && stds.std.write && stds.std.write(log_data);
|
211 | stds.out && stds.out.write && stds.out.write(log_data);
|
212 | });
|
213 |
|
214 | |
215 |
|
216 |
|
217 | cspr.on('message', function forkMessage(msg) {
|
218 | |
219 |
|
220 |
|
221 |
|
222 | if (msg.data && msg.type) {
|
223 | process.nextTick(function() {
|
224 | return God.bus.emit(msg.type ? msg.type : 'process:msg', {
|
225 | at : Utility.getDate(),
|
226 | data : msg.data,
|
227 | process : {
|
228 | pm_id : cspr.pm2_env.pm_id,
|
229 | name : cspr.pm2_env.name,
|
230 | versioning : cspr.pm2_env.versioning,
|
231 | namespace : cspr.pm2_env.namespace
|
232 | }
|
233 | });
|
234 | });
|
235 | }
|
236 | else {
|
237 |
|
238 | if (typeof msg == 'object' && 'node_version' in msg) {
|
239 | cspr.pm2_env.node_version = msg.node_version;
|
240 | return false;
|
241 | }
|
242 |
|
243 | return God.bus.emit('process:msg', {
|
244 | at : Utility.getDate(),
|
245 | raw : msg,
|
246 | process : {
|
247 | pm_id : cspr.pm2_env.pm_id,
|
248 | name : cspr.pm2_env.name,
|
249 | namespace : cspr.pm2_env.namespace
|
250 | }
|
251 | });
|
252 | }
|
253 | });
|
254 |
|
255 | try {
|
256 | var pid = cspr.pid
|
257 | if (typeof(pid) !== 'undefined')
|
258 | fs.writeFileSync(pidFile, pid.toString());
|
259 | } catch (e) {
|
260 | console.error(e.stack || e);
|
261 | }
|
262 |
|
263 | cspr.once('exit', function forkClose(status) {
|
264 | try {
|
265 | for(var k in stds){
|
266 | if (stds[k] && stds[k].destroy) stds[k].destroy();
|
267 | else if (stds[k] && stds[k].end) stds[k].end();
|
268 | else if (stds[k] && stds[k].close) stds[k].close();
|
269 | stds[k] = stds[k]._file;
|
270 | }
|
271 | } catch(e) { God.logAndGenerateError(e);}
|
272 | });
|
273 |
|
274 | cspr._reloadLogs = function(cb) {
|
275 | try {
|
276 | for (var k in stds){
|
277 | if (stds[k] && stds[k].destroy) stds[k].destroy();
|
278 | else if (stds[k] && stds[k].end) stds[k].end();
|
279 | else if (stds[k] && stds[k].close) stds[k].close();
|
280 | stds[k] = stds[k]._file;
|
281 | }
|
282 | } catch(e) { God.logAndGenerateError(e);}
|
283 |
|
284 | Utility.startLogging(stds, cb);
|
285 | };
|
286 |
|
287 | cspr.unref();
|
288 |
|
289 | return cb(null, cspr);
|
290 | });
|
291 |
|
292 | };
|
293 | };
|