UNPKG

13.5 kBJavaScriptView Raw
1
2const cst = require('../../../constants')
3const Common = require('../../Common')
4const Configuration = require('../../Configuration')
5const UxHelpers = require('./helpers.js')
6const chalk = require('chalk')
7const Table = require('cli-tableau')
8const Passwd = require('../../tools/passwd.js')
9
10const List = {}
11
12const CONDENSED_MODE = (process.stdout.columns || 300) < 120
13
14/**
15 * Check if dump file contains same apps that the one managed by PM2
16 */
17function checkIfProcessAreDumped(list) {
18 try {
19 var dump_raw = require('fs').readFileSync(cst.DUMP_FILE_PATH)
20 var dump = JSON.parse(dump_raw)
21 var apps_dumped = dump.map(proc => proc.name)
22 var apps_running = list
23 .filter(proc => proc.pm2_env.pmx_module != true)
24 .map(proc => proc.name)
25 var diff = apps_dumped.filter(a => !apps_running.includes(a))
26 if (diff.length > 0) {
27 Common.printOut(`Current process list running is not in sync with saved list. App ${chalk.bold(diff.join(' '))} differs. Type 'pm2 save' to synchronize.`)
28 }
29 else if (apps_dumped.length != apps_running.length) {
30 Common.printOut(`Current process list running is not in sync with saved list. Type 'pm2 save' to synchronize'`)
31 }
32 } catch(e) {
33 }
34}
35
36var proc_id = 0
37
38/**
39 * List Applications and Modules managed by PM2
40 */
41function listModulesAndAppsManaged(list, commander) {
42 var name_col_size = 11
43
44 if (list && list.length > 0)
45 name_col_size = (list.reduce((p, c) => (p.name.length > c.name.length) ? p : c)).name.length + 5
46
47 var app_head = {
48 id: 5,
49 name: name_col_size,
50 namespace: 13,
51 version: 9,
52 mode: 9,
53 pid: 10,
54 uptime: 8,
55 '↺': 6,
56 status: 11,
57 cpu: 10,
58 mem: 10,
59 user: 10,
60 watching: 10
61 }
62
63 var mod_head = {
64 id: 4,
65 module: 39,
66 version: 20,
67 pid: 7,
68 status: 10,
69 '↺': 6,
70 cpu: 10,
71 mem: 10,
72 user: 10
73 }
74
75 if (CONDENSED_MODE) {
76 app_head = {
77 id: 4,
78 name: 20,
79 mode: 10,
80 '↺': 6,
81 status: 11,
82 cpu: 10,
83 memory: 10
84 }
85
86 mod_head = {
87 id: 4,
88 name: 20,
89 status: 10,
90 cpu: 10,
91 mem: 10
92 }
93 }
94
95 var app_table = new Table({
96 head : Object.keys(app_head),
97 colWidths: Object.keys(app_head).map(k => app_head[k]),
98 colAligns : ['left'],
99 style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
100 })
101
102 var module_table = new Table({
103 head : Object.keys(mod_head),
104 colWidths: Object.keys(mod_head).map(k => mod_head[k]),
105 colAligns : ['left'],
106 style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
107 })
108
109 var sortField = 'name', sortOrder = 'asc', sort,
110 fields = {
111 name: 'pm2_env.name',
112 namespace: 'pm2_env.namespace',
113 pid: 'pid',
114 id: 'pm_id',
115 cpu: 'monit.cpu',
116 memory: 'monit.memory',
117 uptime: 'pm2_env.pm_uptime',
118 status: 'pm2_env.status'
119 }
120
121 if (commander && commander.sort) {
122 sort = commander.sort.split(':');
123
124 if(fields[sort[0].toLowerCase()]) {
125 sortField = sort[0].toLowerCase();
126 sortOrder = sort.length === 2 ? sort[1] : 'asc';
127 }
128 }
129
130 list.sort(function(a, b) {
131 var fieldA = UxHelpers.getNestedProperty(fields[sortField], a)
132 var fieldB = UxHelpers.getNestedProperty(fields[sortField], b)
133
134 if (sortOrder === 'desc') {
135 if (fieldA > fieldB)
136 return -1
137 if (fieldA < fieldB)
138 return 1
139 } else {
140 if (fieldA < fieldB)
141 return -1
142 if (fieldA > fieldB)
143 return 1
144 }
145 return 0
146 })
147
148 list.forEach(function(l) {
149 var obj = {}
150
151 if (l.pm2_env.pm_id > proc_id) {
152 proc_id = l.pm2_env.pm_id
153 }
154
155 var mode = l.pm2_env.exec_mode
156 var status = l.pm2_env.status
157 var key = l.pm2_env.pm_id
158 key = chalk.bold.cyan(key)
159
160 if (l.pm2_env.axm_options) {
161 var is_tracing_enabled = false
162
163 if (l.pm2_env.axm_options.tracing &&
164 typeof(l.pm2_env.axm_options.tracing) == 'boolean' &&
165 l.pm2_env.axm_options.tracing == true)
166 is_tracing_enabled = true
167
168 if (l.pm2_env.axm_options.tracing &&
169 l.pm2_env.axm_options.tracing.enabled &&
170 typeof(l.pm2_env.axm_options.tracing.enabled) == 'boolean' &&
171 l.pm2_env.axm_options.tracing.enabled == true)
172 is_tracing_enabled = true
173
174 if (is_tracing_enabled == true)
175 l.pm2_env.name = chalk.green('☵') + ' ' + l.pm2_env.name
176
177 if (l.pm2_env._km_monitored)
178 l.pm2_env.name = chalk.bold.green('◉') + ' ' + l.pm2_env.name
179 }
180
181 if (l.pm2_env.pmx_module == true) {
182 // pm2 ls for Modules
183 obj[key] = []
184
185 obj[key].push(l.name)
186
187 // Module version + PID
188 if (!CONDENSED_MODE) {
189 var pid = l.pm2_env.axm_options.pid ? l.pm2_env.axm_options.pid : l.pid
190 obj[key].push(l.pm2_env.version || 'N/A', pid)
191 }
192
193 // Status
194 obj[key].push(UxHelpers.colorStatus(status))
195
196 // Restart
197 if (!CONDENSED_MODE)
198 obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)
199
200 // CPU + Memory
201 obj[key].push(l.monit ? (l.monit.cpu + '%') : 'N/A', l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A' )
202
203 // User
204 if (!CONDENSED_MODE) {
205
206 if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {
207 // Resolve user id to username
208 let users = Passwd.getUsers()
209 Object.keys(users).forEach(function(username) {
210 var user = users[username]
211 if (user.userId == l.pm2_env.uid) {
212 l.pm2_env.uid = user.username
213 }
214 })
215 }
216 obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))
217 }
218
219 UxHelpers.safe_push(module_table, obj)
220 }
221 else {
222 // pm2 ls for Applications
223 obj[key] = []
224
225 // PM2 ID
226 obj[key].push(l.pm2_env.name)
227
228 // Namespace
229 if (!CONDENSED_MODE)
230 obj[key].push(l.pm2_env.namespace)
231
232 // Version
233 if (!CONDENSED_MODE)
234 obj[key].push(l.pm2_env.version)
235
236 // Exec mode
237 obj[key].push(mode == 'fork_mode' ? chalk.inverse.bold('fork') : chalk.blue.bold('cluster'))
238
239 // PID
240 if (!CONDENSED_MODE)
241 obj[key].push(l.pid)
242
243 // Uptime
244 if (!CONDENSED_MODE)
245 obj[key].push((l.pm2_env.pm_uptime && status == 'online') ? UxHelpers.timeSince(l.pm2_env.pm_uptime) : 0)
246
247 // Restart
248 obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)
249
250 // Status
251 obj[key].push(UxHelpers.colorStatus(status))
252
253
254 // CPU
255 obj[key].push(l.monit ? l.monit.cpu + '%' : 'N/A')
256
257 // Memory
258 obj[key].push(l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A')
259
260 // User
261 if (!CONDENSED_MODE) {
262 if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {
263 // Resolve user id to username
264 let users = Passwd.getUsers()
265 Object.keys(users).forEach(function(username) {
266 var user = users[username]
267 if (user.userId == l.pm2_env.uid) {
268 l.pm2_env.uid = user.username
269 }
270 })
271 }
272 obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))
273 }
274
275 // Watch status
276 if (!CONDENSED_MODE)
277 obj[key].push(l.pm2_env.watch ? chalk.green.bold('enabled') : chalk.grey('disabled'))
278
279 UxHelpers.safe_push(app_table, obj)
280 }
281
282 })
283
284 // Print Applications Managed
285 console.log(app_table.toString())
286
287 // Print Modules Managed
288 if (module_table.length > 0) {
289 console.log(chalk.bold(`Module${module_table.length > 1 ? 's' : ''}`))
290 console.log(module_table.toString())
291 }
292
293 proc_id++
294}
295
296// Container display
297function containersListing(sys_infos) {
298 var stacked_docker = (process.stdout.columns || 100) < 140
299
300 var docker_head = {
301 id: 4,
302 image: 50,
303 status: 10,
304 '↺': 6,
305 cpu: 10,
306 mem: 10,
307 'net I/O ⇵': 11,
308 'fs I/O ⇵': 11
309 }
310
311 if (stacked_docker) {
312 docker_head = {
313 id: 4,
314 image: 25,
315 status: 10,
316 cpu: 10,
317 mem: 10
318 }
319 }
320
321 var docker_table = new Table({
322 colWidths: Object.keys(docker_head).map(k => docker_head[k]),
323 head : Object.keys(docker_head),
324 colAligns : ['left'],
325 style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
326 })
327
328 sys_infos.containers.forEach((c) => {
329 var cpu = c.stats.cpu_percent
330 var mem = c.stats.mem_percent == 0 ? '0' : c.stats.mem_percent
331 var id = chalk.bold.cyan(proc_id++)
332 var state = UxHelpers.colorStatus(c.state)
333
334 if (stacked_docker)
335 docker_table.push([id, c.image, state, `${cpu}%`, `${mem}mb`])
336 else {
337 docker_table.push([
338 id,
339 c.image,
340 state,
341 c.restartCount,
342 `${cpu == 0 ? '0' : cpu}%`,
343 `${mem}mb`,
344 `${c.stats.netIO.rx}/${isNaN(c.stats.netIO.tx) == true ? '0.0' : c.stats.netIO.tx}`,
345 `${c.stats.blockIO.r}/${c.stats.blockIO.w}`
346 ])
347 }
348 })
349
350 console.log(chalk.bold(`Container${sys_infos.containers.length > 1 ? 's' : ''}`))
351 console.log(docker_table.toString())
352}
353
354/**
355 * High resource processes
356 */
357function listHighResourcesProcesses(sys_infos) {
358 const CPU_MIN_SHOW = 60
359 const MEM_MIN_SHOW = 30
360
361 var sys_proc_head = ['id', 'cmd', 'pid', 'cpu', 'mem', 'uid']
362
363 var sys_proc_table = new Table({
364 colWidths: [4, CONDENSED_MODE ? 29 : 77, 10, 10, 10, 8],
365 head : sys_proc_head,
366 colAligns : ['left'],
367 style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}
368 })
369
370 sys_infos.processes.cpu_sorted = sys_infos.processes.cpu_sorted.filter((proc) => {
371 return proc.cpu > CPU_MIN_SHOW && proc.cmd.includes('node') === false &&
372 proc.cmd.includes('God Daemon') === false
373 })
374
375 sys_infos.processes.cpu_sorted.forEach(proc => {
376 var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`
377 var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`
378 var cmd = proc.cmd
379 sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])
380 })
381
382 sys_infos.processes.mem_sorted = sys_infos.processes.mem_sorted.filter((proc) => {
383 return proc.memory > MEM_MIN_SHOW && proc.cmd.includes('node') == false
384 })
385
386 sys_infos.processes.mem_sorted.forEach((proc) => {
387 var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`
388 var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`
389 var cmd = proc.cmd
390 // if (proc.cmd.length > 50)
391 // cmd = '…' + proc.cmd.slice(proc.cmd.length - 48, proc.cmd.length)
392 sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])
393 })
394
395 if (sys_infos.processes.cpu_sorted.length >= 1 || sys_infos.processes.mem_sorted.length >= 1) {
396 console.log(chalk.bold('Intensive Processes'))
397 console.log(sys_proc_table.toString())
398 }
399}
400
401/**
402 * Sys info line
403 */
404function miniMonitBar(sys_infos) {
405 var sys_summary_line = `${chalk.bold.cyan('host metrics')} `
406 sys_summary_line += `| ${chalk.bold('cpu')}: ${UxHelpers.colorizedMetric(sys_infos.cpu.usage, 40, 70, '%')}`
407 if (sys_infos.cpu.temperature && sys_infos.cpu.temperature != '-1') {
408 sys_summary_line += ` ${UxHelpers.colorizedMetric(sys_infos.cpu.temperature, 50, 70, 'º')}`
409 }
410 if (sys_infos.mem) {
411 var perc_mem_usage = (((sys_infos.mem.available) / sys_infos.mem.total) * 100).toFixed(1)
412 sys_summary_line += ` | ${chalk.bold('mem free')}: ${UxHelpers.colorizedMetric(perc_mem_usage, 30, 10, '%')} `
413 }
414 if (sys_infos.network) {
415 var latency = (sys_infos.network.latency).toFixed(1)
416 if (latency == -1) {
417 sys_summary_line += `| ${chalk.bold('net')}: ${chalk.red('offline')} `
418 }
419 else {
420 sys_summary_line += `| ${chalk.bold('net')}: `
421 //sys_summary_line += `${colorizedMetric(latency, 100, 150, 'ms')} `
422 sys_summary_line += `⇓ ${UxHelpers.colorizedMetric(sys_infos.network.rx_5, 10, 20, 'mb/s')} `
423 sys_summary_line += `⇑ ${UxHelpers.colorizedMetric(sys_infos.network.tx_5, 10, 20, 'mb/s')} `
424 }
425 }
426 if (CONDENSED_MODE == false) {
427 if (sys_infos.storage) {
428 sys_summary_line += `| ${chalk.bold('disk')}: ⇓ ${UxHelpers.colorizedMetric(sys_infos.storage.io.read, 10, 20, 'mb/s')}`
429 sys_summary_line += ` ⇑ ${UxHelpers.colorizedMetric(sys_infos.storage.io.write, 10, 20, 'mb/s')} `
430 }
431 var disk_nb = 0
432
433 sys_infos.storage.filesystems.forEach(fs => {
434 disk_nb++
435 var perc_used = ((fs.used / fs.size) * 100).toFixed()
436 if (perc_used > 60)
437 sys_summary_line += `${chalk.grey(fs.fs)} ${UxHelpers.colorizedMetric(perc_used, 80, 90, '%')} `
438 })
439 }
440
441 sys_summary_line += '|'
442 console.log(sys_summary_line)
443}
444
445/**
446 * pm2 ls
447 * @method dispAsTable
448 * @param {Object} list
449 * @param {Object} system informations (via pm2 sysmonit/pm2 sysinfos)
450 */
451module.exports = function(list, sys_infos, commander) {
452 var pm2_conf = Configuration.getSync('pm2')
453
454 if (!list)
455 return console.log('list empty')
456
457 listModulesAndAppsManaged(list, commander)
458
459 if (sys_infos) {
460 if (sys_infos.containers && sys_infos.containers.length > 0 &&
461 (pm2_conf && pm2_conf.show_docker == "true"))
462 containersListing(sys_infos)
463
464 if (sys_infos.processes && (sys_infos.processes.cpu_sorted || sys_infos.processes.mem_sorted))
465 listHighResourcesProcesses(sys_infos)
466
467 if (sys_infos.cpu && sys_infos.cpu.usage)
468 miniMonitBar(sys_infos)
469 }
470
471 checkIfProcessAreDumped(list)
472}