UNPKG

9.2 kBJavaScriptView Raw
1/**
2 * Copyright 2013-2021 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 if (id !== 'all'
102 && packet.process.name != id
103 && packet.process.pm_id != id)
104 return;
105
106 if ((type === 'out' && exclusive === 'err')
107 || (type === 'err' && exclusive === 'out')
108 || (type === 'PM2' && exclusive !== false))
109 return;
110
111 var lines;
112
113 if (typeof(packet.data) === 'string')
114 lines = (packet.data || '').split('\n');
115 else
116 return;
117
118 lines.forEach(function(line) {
119 if (!line || line.length === 0) return;
120
121 if (raw)
122 return type === 'err' ? process.stderr.write(util.format(line) + '\n') : process.stdout.write(util.format(line) + '\n');
123
124 if (timestamp)
125 process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
126
127 var name = packet.process.pm_id + '|' + packet.process.name;
128
129 if (name.length > min_padding)
130 min_padding = name.length + 1
131
132 if (type === 'out')
133 process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
134 else if (type === 'err')
135 process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
136 else if (!raw && (id === 'all' || id === 'PM2'))
137 process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
138 if (highlight)
139 process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
140 else
141 process.stdout.write(util.format(line)+ '\n');
142 });
143 });
144 });
145};
146
147Log.devStream = function(Client, id, raw, timestamp, exclusive) {
148 var that = this;
149
150 Client.launchBus(function(err, bus) {
151
152 setTimeout(function() {
153 bus.on('process:event', function(packet) {
154 if (packet.event == 'online')
155 console.log(chalk.green('[rundev] App %s restarted'), packet.process.name);
156 });
157 }, 1000);
158
159 var min_padding = 3
160
161 bus.on('log:*', function(type, packet) {
162 if (id !== 'all'
163 && packet.process.name != id
164 && packet.process.pm_id != id)
165 return;
166
167 if ((type === 'out' && exclusive === 'err')
168 || (type === 'err' && exclusive === 'out')
169 || (type === 'PM2' && exclusive !== false))
170 return;
171
172 if (type === 'PM2')
173 return;
174
175 var name = packet.process.pm_id + '|' + packet.process.name;
176
177 var lines;
178
179 if (typeof(packet.data) === 'string')
180 lines = (packet.data || '').split('\n');
181 else
182 return;
183
184 lines.forEach(function(line) {
185 if (!line || line.length === 0) return;
186
187 if (raw)
188 return process.stdout.write(util.format(line) + '\n');
189
190 if (timestamp)
191 process.stdout.write(chalk['dim'](chalk.grey(dayjs().format(timestamp) + ' ')));
192
193 var name = packet.process.name + '-' + packet.process.pm_id;
194
195 if (name.length > min_padding)
196 min_padding = name.length + 1
197
198 if (type === 'out')
199 process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name) + ' | '));
200 else if (type === 'err')
201 process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name) + ' | '));
202 else if (!raw && (id === 'all' || id === 'PM2'))
203 process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));
204 process.stdout.write(util.format(line) + '\n');
205 });
206 });
207 });
208};
209
210Log.jsonStream = function(Client, id) {
211 var that = this;
212
213 Client.launchBus(function(err, bus) {
214 if (err) console.error(err);
215
216 bus.on('process:event', function(packet) {
217 process.stdout.write(JSON.stringify({
218 timestamp : dayjs(packet.at),
219 type : 'process_event',
220 status : packet.event,
221 app_name : packet.process.name
222 }));
223 process.stdout.write('\n');
224 });
225
226 bus.on('log:*', function(type, packet) {
227 if (id !== 'all'
228 && packet.process.name != id
229 && packet.process.pm_id != id)
230 return;
231
232 if (type === 'PM2')
233 return;
234
235 if (typeof(packet.data) == 'string')
236 packet.data = packet.data.replace(/(\r\n|\n|\r)/gm,'');
237
238 process.stdout.write(JSON.stringify({
239 message : packet.data,
240 timestamp : dayjs(packet.at),
241 type : type,
242 process_id : packet.process.pm_id,
243 app_name : packet.process.name
244 }));
245 process.stdout.write('\n');
246 });
247 });
248};
249
250Log.formatStream = function(Client, id, raw, timestamp, exclusive, highlight) {
251 var that = this;
252
253 Client.launchBus(function(err, bus) {
254
255 bus.on('log:*', function(type, packet) {
256 if (id !== 'all'
257 && packet.process.name != id
258 && packet.process.pm_id != id)
259 return;
260
261 if ((type === 'out' && exclusive === 'err')
262 || (type === 'err' && exclusive === 'out')
263 || (type === 'PM2' && exclusive !== false))
264 return;
265
266 if (type === 'PM2' && raw)
267 return;
268
269 var name = packet.process.name + '-' + packet.process.pm_id;
270
271 var lines;
272
273 if (typeof(packet.data) === 'string')
274 lines = (packet.data || '').split('\n');
275 else
276 return;
277
278 lines.forEach(function(line) {
279 if (!line || line.length === 0) return;
280
281 if (!raw) {
282 if (timestamp)
283 process.stdout.write('timestamp=' + dayjs().format(timestamp) + ' ');
284 if (packet.process.name === 'PM2')
285 process.stdout.write('app=pm2 ');
286 if (packet.process.name !== 'PM2')
287 process.stdout.write('app=' + packet.process.name + ' id=' + packet.process.pm_id + ' ');
288 if (type === 'out')
289 process.stdout.write('type=out ');
290 else if (type === 'err')
291 process.stdout.write('type=error ');
292 }
293
294 process.stdout.write('message=');
295 if (highlight)
296 process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\n');
297 else
298 process.stdout.write(util.format(line) + '\n');
299 });
300 });
301 });
302};
303
304function pad(pad, str, padLeft) {
305 if (typeof str === 'undefined')
306 return pad;
307 if (padLeft) {
308 return (pad + str).slice(-pad.length);
309 } else {
310 return (str + pad).substring(0, pad.length);
311 }
312}