UNPKG

9.31 kBJavaScriptView Raw
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 */
6var fs = require('fs'),
7 util = require('util'),
8 chalk = require('chalk'),
9 forEachLimit = require('async/forEachLimit'),
10 dayjs = require('dayjs');
11
12var Log = module.exports = {};
13
14var DEFAULT_PADDING = ' ';
15
16/**
17 * Tail logs from file stream.
18 * @param {Object} apps_list
19 * @param {Number} lines
20 * @param {Boolean} raw
21 * @param {Function} callback
22 * @return
23 */
24
25Log.tail = function(apps_list, lines, raw, callback) {
26 var that = this;
27
28 if (lines === 0 || apps_list.length === 0)
29 return callback && callback();
30
31 var count = 0;
32
33 var getLastLines = function (filename, lines, callback) {
34 var chunk = '';
35 var size = Math.max(0, fs.statSync(filename).size - (lines * 200));
36
37 var fd = fs.createReadStream(filename, {start : size});
38 fd.on('data', function(data) { chunk += data.toString(); });
39 fd.on('end', function() {
40 chunk = chunk.split('\n').slice(-(lines+1));
41 chunk.pop();
42 callback(chunk);
43 });
44 };
45
46 apps_list.sort(function(a, b) {
47 return (fs.existsSync(a.path) ? fs.statSync(a.path).mtime.valueOf() : 0) -
48 (fs.existsSync(b.path) ? fs.statSync(b.path).mtime.valueOf() : 0);
49 });
50
51 forEachLimit(apps_list, 1, function(app, next) {
52 if (!fs.existsSync(app.path || ''))
53 return next();
54
55 getLastLines(app.path, lines, function(output) {
56 console.log(chalk.grey('%s last %d lines:'), app.path, lines);
57 output.forEach(function(out) {
58 if (raw)
59 return app.type === 'err' ? console.error(out) : console.log(out);
60 if (app.type === 'out')
61 process.stdout.write(chalk.green(pad(DEFAULT_PADDING, app.app_name) + ' | '));
62 else if (app.type === 'err')
63 process.stdout.write(chalk.red(pad(DEFAULT_PADDING, app.app_name) + ' | '));
64 else
65 process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));
66 console.log(out);
67 });
68 if (output.length)
69 process.stdout.write('\n');
70 next();
71 });
72 }, function() {
73 callback && callback();
74 });
75};
76
77/**
78 * Stream logs in realtime from the bus eventemitter.
79 * @param {String} id
80 * @param {Boolean} raw
81 * @return
82 */
83
84Log.stream = function(Client, id, raw, timestamp, exclusive, highlight) {
85 var that = this;
86
87 Client.launchBus(function(err, bus, socket) {
88
89 socket.on('reconnect attempt', function() {
90 if (global._auto_exit === true) {
91 if (timestamp)
92 process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
93 process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | ') + '[[[ Target PM2 killed. ]]]');
94 process.exit(0);
95 }
96 });
97
98 var min_padding = 3
99
100 bus.on('log:*', function(type, packet) {
101 var isMatchingProcess = id === 'all'
102 || packet.process.name == id
103 || packet.process.pm_id == id
104 || packet.process.namespace == id;
105
106 if (!isMatchingProcess)
107 return;
108
109 if ((type === 'out' && exclusive === 'err')
110 || (type === 'err' && exclusive === 'out')
111 || (type === 'PM2' && exclusive !== false))
112 return;
113
114 var lines;
115
116 if (typeof(packet.data) === 'string')
117 lines = (packet.data || '').split('\n');
118 else
119 return;
120
121 lines.forEach(function(line) {
122 if (!line || line.length === 0) return;
123
124 if (raw)
125 return type === 'err' ? process.stderr.write(util.format(line) + '\n') : process.stdout.write(util.format(line) + '\n');
126
127 if (timestamp)
128 process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
129
130 var name = packet.process.pm_id + '|' + packet.process.name;
131
132 if (name.length > min_padding)
133 min_padding = name.length + 1
134
135 if (type === 'out')
136 process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
137 else if (type === 'err')
138 process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
139 else if (!raw && (id === 'all' || id === 'PM2'))
140 process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
141 if (highlight)
142 process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
143 else
144 process.stdout.write(util.format(line)+ '\n');
145 });
146 });
147 });
148};
149
150Log.devStream = function(Client, id, raw, timestamp, exclusive) {
151 var that = this;
152
153 Client.launchBus(function(err, bus) {
154
155 setTimeout(function() {
156 bus.on('process:event', function(packet) {
157 if (packet.event == 'online')
158 console.log(chalk.green('[rundev] App %s restarted'), packet.process.name);
159 });
160 }, 1000);
161
162 var min_padding = 3
163
164 bus.on('log:*', function(type, packet) {
165 if (id !== 'all'
166 && packet.process.name != id
167 && packet.process.pm_id != id)
168 return;
169
170 if ((type === 'out' && exclusive === 'err')
171 || (type === 'err' && exclusive === 'out')
172 || (type === 'PM2' && exclusive !== false))
173 return;
174
175 if (type === 'PM2')
176 return;
177
178 var name = packet.process.pm_id + '|' + packet.process.name;
179
180 var lines;
181
182 if (typeof(packet.data) === 'string')
183 lines = (packet.data || '').split('\n');
184 else
185 return;
186
187 lines.forEach(function(line) {
188 if (!line || line.length === 0) return;
189
190 if (raw)
191 return process.stdout.write(util.format(line) + '\n');
192
193 if (timestamp)
194 process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
195
196 var name = packet.process.name + '-' + packet.process.pm_id;
197
198 if (name.length > min_padding)
199 min_padding = name.length + 1
200
201 if (type === 'out')
202 process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
203 else if (type === 'err')
204 process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
205 else if (!raw && (id === 'all' || id === 'PM2'))
206 process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
207 process.stdout.write(util.format(line) + '\n');
208 });
209 });
210 });
211};
212
213Log.jsonStream = function(Client, id) {
214 var that = this;
215
216 Client.launchBus(function(err, bus) {
217 if (err) console.error(err);
218
219 bus.on('process:event', function(packet) {
220 process.stdout.write(JSON.stringify({
221 timestamp : dayjs(packet.at),
222 type : 'process_event',
223 status : packet.event,
224 app_name : packet.process.name
225 }));
226 process.stdout.write('\n');
227 });
228
229 bus.on('log:*', function(type, packet) {
230 if (id !== 'all'
231 && packet.process.name != id
232 && packet.process.pm_id != id)
233 return;
234
235 if (type === 'PM2')
236 return;
237
238 if (typeof(packet.data) == 'string')
239 packet.data = packet.data.replace(/(\r\n|\n|\r)/gm,'');
240
241 process.stdout.write(JSON.stringify({
242 message : packet.data,
243 timestamp : dayjs(packet.at),
244 type : type,
245 process_id : packet.process.pm_id,
246 app_name : packet.process.name
247 }));
248 process.stdout.write('\n');
249 });
250 });
251};
252
253Log.formatStream = function(Client, id, raw, timestamp, exclusive, highlight) {
254 var that = this;
255
256 Client.launchBus(function(err, bus) {
257
258 bus.on('log:*', function(type, packet) {
259 if (id !== 'all'
260 && packet.process.name != id
261 && packet.process.pm_id != id)
262 return;
263
264 if ((type === 'out' && exclusive === 'err')
265 || (type === 'err' && exclusive === 'out')
266 || (type === 'PM2' && exclusive !== false))
267 return;
268
269 if (type === 'PM2' && raw)
270 return;
271
272 var name = packet.process.name + '-' + packet.process.pm_id;
273
274 var lines;
275
276 if (typeof(packet.data) === 'string')
277 lines = (packet.data || '').split('\n');
278 else
279 return;
280
281 lines.forEach(function(line) {
282 if (!line || line.length === 0) return;
283
284 if (!raw) {
285 if (timestamp)
286 process.stdout.write('timestamp=' + dayjs().format(timestamp) + ' ');
287 if (packet.process.name === 'PM2')
288 process.stdout.write('app=pm2 ');
289 if (packet.process.name !== 'PM2')
290 process.stdout.write('app=' + packet.process.name + ' id=' + packet.process.pm_id + ' ');
291 if (type === 'out')
292 process.stdout.write('type=out ');
293 else if (type === 'err')
294 process.stdout.write('type=error ');
295 }
296
297 process.stdout.write('message=');
298 if (highlight)
299 process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
300 else
301 process.stdout.write(util.format(line) + '\n');
302 });
303 });
304 });
305};
306
307function pad(pad, str, padLeft) {
308 if (typeof str === 'undefined')
309 return pad;
310 if (padLeft) {
311 return (pad + str).slice(-pad.length);
312 } else {
313 return (str + pad).substring(0, pad.length);
314 }
315}