UNPKG

5.38 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 */
6// pm2-htop
7// Library who interacts with PM2 to display processes resources in htop way
8// by Strzelewicz Alexandre
9
10var multimeter = require('pm2-multimeter');
11var os = require('os');
12var p = require('path');
13var chalk = require('chalk');
14
15var UX = require('./UX');
16
17var debug = require('debug')('pm2:monit');
18
19// Cst for light programs
20const RATIO_T1 = Math.floor(os.totalmem() / 500);
21// Cst for medium programs
22const RATIO_T2 = Math.floor(os.totalmem() / 50);
23// Cst for heavy programs
24const RATIO_T3 = Math.floor(os.totalmem() / 5);
25// Cst for heavy programs
26const RATIO_T4 = Math.floor(os.totalmem());
27
28var Monit = {};
29
30//helper to get bars.length (num bars printed)
31Object.size = function(obj) {
32 var size = 0, key;
33 for (key in obj) {
34 if (obj.hasOwnProperty(key)) size++;
35 }
36 return size;
37};
38
39/**
40 * Reset the monitor through charm, basically \033c
41 * @param String msg optional message to show
42 * @return Monit
43 */
44Monit.reset = function(msg) {
45
46 this.multi.charm.reset();
47
48 this.multi.write('\x1B[32m⌬ PM2 \x1B[39mmonitoring\x1B[96m (To go further check out https://app.pm2.io) \x1B[39m\n\n');
49
50 if(msg) {
51 this.multi.write(msg);
52 }
53
54 this.bars = {};
55
56 return this;
57}
58
59/**
60 * Synchronous Monitor init method
61 * @method init
62 * @return Monit
63 */
64Monit.init = function() {
65
66 this.multi = multimeter(process);
67
68 this.multi.on('^C', this.stop);
69
70 this.reset();
71
72 return this;
73}
74
75/**
76 * Stops monitor
77 * @method stop
78 */
79Monit.stop = function() {
80 this.multi.charm.destroy();
81 process.exit(0);
82}
83
84
85/**
86 * Refresh monitor
87 * @method refresh
88 * @param {} processes
89 * @return this
90 */
91Monit.refresh = function(processes) {
92 debug('Monit refresh');
93
94 if(!processes) {
95 processes = [];
96 }
97
98 var num = processes.length;
99 this.num_bars = Object.size(this.bars);
100
101 if(num !== this.num_bars) {
102 debug('Monit addProcesses - actual: %s, new: %s', this.num_bars, num);
103 return this.addProcesses(processes);
104 } else {
105
106 if(num === 0) {
107 return;
108 }
109
110 debug('Monit refresh');
111 var proc;
112
113 for(var i = 0; i < num; i++) {
114 proc = processes[i];
115
116 //this is to avoid a print issue when the process is restarted for example
117 //we might also check for the pid but restarted|restarting will be rendered bad
118 if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) {
119 debug('bars for %s does not exist', proc.pm_id);
120 this.addProcesses(processes);
121 break;
122 }
123
124 this.updateBars(proc);
125
126 }
127 }
128
129 return this;
130}
131
132Monit.addProcess = function(proc, i) {
133 if(proc.pm_id in this.bars) {
134 return ;
135 }
136
137 if (proc.monit.error)
138 throw new Error(JSON.stringify(proc.monit.error));
139
140 var process_name = proc.pm2_env.name || p.basename(proc.pm2_env.pm_exec_path);
141 var status = proc.pm2_env.status == 'online' ? chalk.green.bold('●') : chalk.red.bold('●');
142
143 this.multi.write(' ' + status + ' ' + chalk.green.bold(process_name));
144 this.multi.write('\n');
145 this.multi.write('[' + proc.pm2_env.pm_id + '] [' + proc.pm2_env.exec_mode + ']\n');
146
147 var bar_cpu = this.multi(40, (i * 2) + 3 + i, {
148 width: 30,
149 solid: {
150 text: '|',
151 foreground: 'white',
152 background: 'blue'
153 },
154 empty: {
155 text: ' '
156 }
157 });
158
159 var bar_memory = this.multi(40, (i * 2) + 4 + i, {
160 width: 30,
161 solid: {
162 text: '|',
163 foreground: 'white',
164 background: 'red'
165 },
166 empty: {
167 text: ' '
168 }
169 });
170
171 this.bars[proc.pm_id] = {
172 memory: bar_memory,
173 cpu: bar_cpu,
174 status: proc.pm2_env.status
175 };
176
177 this.updateBars(proc);
178
179 this.multi.write('\n');
180
181 return this;
182}
183
184Monit.addProcesses = function(processes) {
185
186 if(!processes) {
187 processes = [];
188 }
189
190 this.reset();
191
192 var num = processes.length;
193
194 if(num > 0) {
195 for(var i = 0; i < num; i++) {
196 this.addProcess(processes[i], i);
197 }
198 } else {
199 this.reset('No processes to monit');
200 }
201
202}
203
204// Draw memory bars
205/**
206 * Description
207 * @method drawRatio
208 * @param {} bar_memory
209 * @param {} memory
210 * @return
211 */
212Monit.drawRatio = function(bar_memory, memory) {
213 var scale = 0;
214
215 if (memory < RATIO_T1) scale = RATIO_T1;
216 else if (memory < RATIO_T2) scale = RATIO_T2;
217 else if (memory < RATIO_T3) scale = RATIO_T3;
218 else scale = RATIO_T4;
219
220 bar_memory.ratio(memory,
221 scale,
222 UX.helpers.bytesToSize(memory, 3));
223};
224
225/**
226 * Updates bars informations
227 * @param {} proc proc object
228 * @return this
229 */
230Monit.updateBars = function(proc) {
231 if (this.bars[proc.pm_id]) {
232 if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) {
233 this.bars[proc.pm_id].cpu.percent(0, chalk.red(proc.pm2_env.status));
234 this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red(proc.pm2_env.status));
235 } else if (!proc.monit) {
236 this.bars[proc.pm_id].cpu.percent(0, chalk.red('No data'));
237 this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red('No data'));
238 } else {
239 this.bars[proc.pm_id].cpu.percent(proc.monit.cpu);
240 this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory);
241 }
242 }
243
244 return this;
245}
246
247module.exports = Monit;