mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
move sysmonitoring logic to pm2-sysmonit + fix pm2 ls
This commit is contained in:
parent
d520de93a4
commit
042a32afce
10
lib/API.js
10
lib/API.js
@ -1725,13 +1725,11 @@ class API {
|
||||
if ((conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI'))
|
||||
return false;
|
||||
|
||||
return that.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
|
||||
that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
|
||||
doList(err, proc_list, sys_infos)
|
||||
})
|
||||
return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
|
||||
doList(err, proc_list)
|
||||
})
|
||||
|
||||
function doList(err, list, sys_infos) {
|
||||
function doList(err, list) {
|
||||
if (err) {
|
||||
if (that.gl_retry == 0) {
|
||||
that.gl_retry += 1;
|
||||
@ -1758,7 +1756,7 @@ class API {
|
||||
chalk.bold(that.gl_interact_infos.machine_name),
|
||||
chalk.bold(dashboard_url))
|
||||
}
|
||||
UX.list(list, sys_infos, commander);
|
||||
UX.list(list, commander);
|
||||
//Common.printOut(chalk.white.italic(' Use `pm2 show <id|name>` to get more details about an app'));
|
||||
}
|
||||
|
||||
|
||||
@ -32,21 +32,13 @@ module.exports = function(CLI) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get version of the daemonized PM2
|
||||
* @method getVersion
|
||||
* @callback cb
|
||||
* Install pm2-sysmonit
|
||||
*/
|
||||
CLI.prototype.launchSysMonitoring = function(cb) {
|
||||
var that = this;
|
||||
|
||||
this.set('pm2:sysmonit', 'true', () => {
|
||||
that.Client.executeRemote('launchSysMonitoring', {}, function(err) {
|
||||
if (err)
|
||||
Common.err(err)
|
||||
else
|
||||
Common.log('System Monitoring launched')
|
||||
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
||||
})
|
||||
that.install('pm2-sysmonit', (err, apps) => {
|
||||
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@ -24,10 +24,10 @@ function checkIfProcessAreDumped(list) {
|
||||
.map(proc => proc.name)
|
||||
var diff = apps_dumped.filter(a => !apps_running.includes(a))
|
||||
if (diff.length > 0) {
|
||||
Common.printOut(`Current process list running is not in sync with saved list. App ${chalk.bold(diff.join(' '))} differs. Type 'pm2 save' to synchronize.`)
|
||||
Common.warn(`Current process list is not synchronized with saved list. App ${chalk.bold(diff.join(' '))} differs. Type 'pm2 save' to synchronize.`)
|
||||
}
|
||||
else if (apps_dumped.length != apps_running.length) {
|
||||
Common.printOut(`Current process list running is not in sync with saved list. Type 'pm2 save' to synchronize'`)
|
||||
Common.warn(`Current process list is not synchronized with saved list. Type 'pm2 save' to synchronize.`)
|
||||
}
|
||||
} catch(e) {
|
||||
}
|
||||
@ -402,39 +402,51 @@ function listHighResourcesProcesses(sys_infos) {
|
||||
* Sys info line
|
||||
*/
|
||||
function miniMonitBar(sys_infos) {
|
||||
let sys_metrics = sys_infos.pm2_env.axm_monitor
|
||||
|
||||
let cpu = sys_metrics['cpu']
|
||||
|
||||
if (typeof(cpu) == 'undefined') return
|
||||
|
||||
var sys_summary_line = `${chalk.bold.cyan('host metrics')} `
|
||||
sys_summary_line += `| ${chalk.bold('cpu')}: ${UxHelpers.colorizedMetric(sys_infos.cpu.usage, 40, 70, '%')}`
|
||||
if (sys_infos.cpu.temperature && sys_infos.cpu.temperature != '-1') {
|
||||
sys_summary_line += ` ${UxHelpers.colorizedMetric(sys_infos.cpu.temperature, 50, 70, 'º')}`
|
||||
sys_summary_line += `| ${chalk.bold('cpu')}: ${UxHelpers.colorizedMetric(cpu.value, 40, 70, '%')}`
|
||||
|
||||
let temp = sys_metrics['cpu:temp'].value
|
||||
if (temp && temp != '-1') {
|
||||
sys_summary_line += ` ${UxHelpers.colorizedMetric(temp, 50, 70, 'º')}`
|
||||
}
|
||||
if (sys_infos.mem) {
|
||||
var perc_mem_usage = (((sys_infos.mem.available) / sys_infos.mem.total) * 100).toFixed(1)
|
||||
|
||||
let mem_total = sys_metrics['mem:total'].value
|
||||
let mem_available = sys_metrics['mem:available'].value
|
||||
|
||||
if (mem_total) {
|
||||
var perc_mem_usage = (((mem_available) / mem_total) * 100).toFixed(1)
|
||||
sys_summary_line += ` | ${chalk.bold('mem free')}: ${UxHelpers.colorizedMetric(perc_mem_usage, 30, 10, '%')} `
|
||||
}
|
||||
if (sys_infos.network) {
|
||||
var latency = (sys_infos.network.latency).toFixed(1)
|
||||
if (latency == -1) {
|
||||
sys_summary_line += `| ${chalk.bold('net')}: ${chalk.red('offline')} `
|
||||
}
|
||||
else {
|
||||
sys_summary_line += `| ${chalk.bold('net')}: `
|
||||
//sys_summary_line += `${colorizedMetric(latency, 100, 150, 'ms')} `
|
||||
sys_summary_line += `⇓ ${UxHelpers.colorizedMetric(sys_infos.network.rx_5, 10, 20, 'mb/s')} `
|
||||
sys_summary_line += `⇑ ${UxHelpers.colorizedMetric(sys_infos.network.tx_5, 10, 20, 'mb/s')} `
|
||||
}
|
||||
}
|
||||
|
||||
let interfaces = Object.keys(sys_metrics).filter(m => m.includes('net') && m != 'net:default').map(i => i.split(':')[1]).filter((iface, i, self) => self.indexOf(iface) === i)
|
||||
|
||||
interfaces.forEach(iface => {
|
||||
sys_summary_line += `| ${chalk.bold(iface)}: `
|
||||
sys_summary_line += `⇓ ${UxHelpers.colorizedMetric(sys_metrics[`net:${iface}:rx_5`].value, 10, 20, 'mb/s')} `
|
||||
sys_summary_line += `⇑ ${UxHelpers.colorizedMetric(sys_metrics[`net:${iface}:tx_5`].value, 10, 20, 'mb/s')} `
|
||||
})
|
||||
|
||||
|
||||
if (CONDENSED_MODE == false) {
|
||||
if (sys_infos.storage) {
|
||||
sys_summary_line += `| ${chalk.bold('disk')}: ⇓ ${UxHelpers.colorizedMetric(sys_infos.storage.io.read, 10, 20, 'mb/s')}`
|
||||
sys_summary_line += ` ⇑ ${UxHelpers.colorizedMetric(sys_infos.storage.io.write, 10, 20, 'mb/s')} `
|
||||
}
|
||||
let read = sys_metrics['io:r'].value
|
||||
let write = sys_metrics['io:w'].value
|
||||
|
||||
sys_summary_line += `| ${chalk.bold('disk')}: ⇓ ${UxHelpers.colorizedMetric(read, 10, 20, 'mb/s')}`
|
||||
sys_summary_line += ` ⇑ ${UxHelpers.colorizedMetric(write, 10, 20, 'mb/s')} `
|
||||
|
||||
let disks = Object.keys(sys_metrics).filter(m => m.includes('fs:')).map(i => i.split(':')[1]).filter((iface, i, self) => self.indexOf(iface) === i)
|
||||
var disk_nb = 0
|
||||
|
||||
sys_infos.storage.filesystems.forEach(fs => {
|
||||
disk_nb++
|
||||
var perc_used = ((fs.used / fs.size) * 100).toFixed()
|
||||
if (perc_used > 60)
|
||||
sys_summary_line += `${chalk.grey(fs.fs)} ${UxHelpers.colorizedMetric(perc_used, 80, 90, '%')} `
|
||||
disks.forEach(fs => {
|
||||
let use = sys_metrics[`fs:${fs}:use`].value
|
||||
if (use > 60)
|
||||
sys_summary_line += `${chalk.grey(fs)} ${UxHelpers.colorizedMetric(use, 80, 90, '%')} `
|
||||
})
|
||||
}
|
||||
|
||||
@ -448,7 +460,7 @@ function miniMonitBar(sys_infos) {
|
||||
* @param {Object} list
|
||||
* @param {Object} system informations (via pm2 sysmonit/pm2 sysinfos)
|
||||
*/
|
||||
module.exports = function(list, sys_infos, commander) {
|
||||
module.exports = function(list, commander) {
|
||||
var pm2_conf = Configuration.getSync('pm2')
|
||||
|
||||
if (!list)
|
||||
@ -456,17 +468,9 @@ module.exports = function(list, sys_infos, commander) {
|
||||
|
||||
listModulesAndAppsManaged(list, commander)
|
||||
|
||||
if (sys_infos) {
|
||||
if (sys_infos.containers && sys_infos.containers.length > 0 &&
|
||||
(pm2_conf && pm2_conf.show_docker == "true"))
|
||||
containersListing(sys_infos)
|
||||
|
||||
if (sys_infos.processes && (sys_infos.processes.cpu_sorted || sys_infos.processes.mem_sorted))
|
||||
listHighResourcesProcesses(sys_infos)
|
||||
|
||||
if (sys_infos.cpu && sys_infos.cpu.usage)
|
||||
miniMonitBar(sys_infos)
|
||||
}
|
||||
let sysmonit = list.filter(proc => proc.name == 'pm2-sysmonit')
|
||||
if (sysmonit && sysmonit[0])
|
||||
miniMonitBar(sysmonit[0])
|
||||
|
||||
checkIfProcessAreDumped(list)
|
||||
}
|
||||
|
||||
@ -217,9 +217,7 @@ Daemon.prototype.innerStart = function(cb) {
|
||||
profileCPU : profile.bind(this, 'cpu'),
|
||||
profileMEM : profile.bind(this, 'mem'),
|
||||
prepare : God.prepare,
|
||||
launchSysMonitoring : God.launchSysMonitoring,
|
||||
getMonitorData : God.getMonitorData,
|
||||
getSystemData : God.getSystemData,
|
||||
|
||||
startProcessId : God.startProcessId,
|
||||
stopProcessId : God.stopProcessId,
|
||||
|
||||
27
lib/God.js
27
lib/God.js
@ -79,10 +79,6 @@ God.init = function() {
|
||||
|
||||
this.configuration = Configuration.getSync('pm2')
|
||||
|
||||
if (this.configuration && this.configuration.sysmonit == 'true') {
|
||||
God.launchSysMonitoring({}, () => { console.log('System monitoring launched') })
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
God.Worker.start()
|
||||
}, 500)
|
||||
@ -586,27 +582,4 @@ God.injectVariables = function injectVariables (env, cb) {
|
||||
return cb(null, env);
|
||||
};
|
||||
|
||||
God.launchSysMonitoring = function(env, cb) {
|
||||
if (God.system_infos_proc !== null)
|
||||
return cb(new Error('Sys Monitoring already launched'))
|
||||
|
||||
try {
|
||||
var sysinfo = require('./Sysinfo/SystemInfo.js')
|
||||
God.system_infos_proc = new sysinfo()
|
||||
|
||||
setInterval(() => {
|
||||
God.system_infos_proc.query((err, data) => {
|
||||
if (err) return
|
||||
God.system_infos = data
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
God.system_infos_proc.fork()
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
God.system_infos_proc = null
|
||||
}
|
||||
return cb()
|
||||
}
|
||||
|
||||
God.init()
|
||||
|
||||
@ -118,23 +118,6 @@ module.exports = function(God) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method getSystemData
|
||||
* @param {} env
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
God.getSystemData = function getSystemData(env, cb) {
|
||||
if (God.system_infos_proc !== null)
|
||||
God.system_infos_proc.query((err, data) => {
|
||||
cb(null, data)
|
||||
})
|
||||
else {
|
||||
cb(new Error('Sysinfos not launched, type: pm2 sysmonit'))
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method dumpProcessList
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
|
||||
class MeanCalc {
|
||||
constructor(count) {
|
||||
this.metrics = []
|
||||
this.count = count
|
||||
}
|
||||
|
||||
inspect() {
|
||||
return this.val()
|
||||
}
|
||||
|
||||
add(value) {
|
||||
if (this.metrics.length >= this.count) {
|
||||
this.metrics.shift()
|
||||
}
|
||||
this.metrics.push(value)
|
||||
}
|
||||
|
||||
val() {
|
||||
if (this.metrics.length == 0) return 0
|
||||
let sum = this.metrics.reduce((prev, curr) => curr += prev)
|
||||
return Math.floor((sum / this.metrics.length) * 1000) / 1000
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MeanCalc
|
||||
@ -1,118 +0,0 @@
|
||||
|
||||
const PM2 = require('./../../API.js')
|
||||
const psList = require('../psList.js')
|
||||
|
||||
const SERVICES_ASSOCIATION = {
|
||||
'mongodb,mongo': {
|
||||
module: 'pm2-mongodb'
|
||||
},
|
||||
'redis,redis-server': {
|
||||
module: 'pm2-redis'
|
||||
},
|
||||
'elasticsearch': {
|
||||
module: 'pm2-elasticsearch'
|
||||
},
|
||||
'docker': {
|
||||
module: 'pm2-monit-docker'
|
||||
},
|
||||
'consul': {
|
||||
module:'pm2-monit-consul'
|
||||
},
|
||||
'pm2': {
|
||||
module: 'pm2-probe'
|
||||
},
|
||||
'fpm': {
|
||||
module: 'pm2-php-fpm'
|
||||
}
|
||||
}
|
||||
|
||||
// 'python,python3': {
|
||||
// module: 'pm2-python'
|
||||
// },
|
||||
// 'nginx': {
|
||||
// module: 'pm2-monit-nginx'
|
||||
// },
|
||||
// 'haproxy': {
|
||||
// module: 'pm2-monit-haproxy'
|
||||
// },
|
||||
// 'traeffik': {
|
||||
// module: 'pm2-monit-traeffik'
|
||||
// }
|
||||
|
||||
class ServicesDetection {
|
||||
constructor() {
|
||||
this.pm2 = new PM2()
|
||||
}
|
||||
|
||||
startDetection(cb = () => {}) {
|
||||
// Check running probes
|
||||
this.monitoredServices((err, pm2_services) => {
|
||||
// Check running services
|
||||
this.discover((err, required_modules) => {
|
||||
var required_monitoring_probes = Object.keys(required_modules)
|
||||
// Make the diff between
|
||||
// console.log(`Need to start following modules:`)
|
||||
// console.log(_.difference(required_monitoring_probes, pm2_services))
|
||||
this.pm2.install('pm2-server-monit', (err, apps) => {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
monitoredServices(cb) {
|
||||
var f_proc_list = []
|
||||
|
||||
this.pm2.list((err, proc_list) => {
|
||||
f_proc_list = proc_list.map(p => {
|
||||
return p.name
|
||||
})
|
||||
this.pm2.close()
|
||||
cb(err, f_proc_list)
|
||||
})
|
||||
}
|
||||
|
||||
discover(cb) {
|
||||
psList()
|
||||
.then(processes => {
|
||||
var supported_systems = Object.keys(SERVICES_ASSOCIATION)
|
||||
var required_modules = {}
|
||||
|
||||
processes.forEach((proc) => {
|
||||
supported_systems.forEach(sup_sys => {
|
||||
var proc_names = sup_sys.split(',')
|
||||
proc_names.forEach(proc_name => {
|
||||
if (proc.name.includes(proc_name) === true ||
|
||||
proc.cmd.includes(proc_name) === true) {
|
||||
var key = SERVICES_ASSOCIATION[sup_sys].module
|
||||
required_modules[key] = SERVICES_ASSOCIATION[sup_sys]
|
||||
required_modules[key].monit = proc
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return cb(null, required_modules)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while listing processes`, e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
var serviceDetection = new ServicesDetection()
|
||||
|
||||
var process = (done) => {
|
||||
serviceDetection.startDetection((err, procs) => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
var iterate = () => {
|
||||
process(() => {
|
||||
setTimeout(iterate, 3000)
|
||||
})
|
||||
}
|
||||
|
||||
iterate()
|
||||
}
|
||||
@ -1,633 +0,0 @@
|
||||
|
||||
const sysinfo = require('systeminformation')
|
||||
const psList = require('./psList.js')
|
||||
const async = require('async')
|
||||
const MeanCalc = require('./MeanCalc.js')
|
||||
const fork = require('child_process').fork
|
||||
const DEFAULT_CONVERSION = 1024 * 1024
|
||||
const os = require('os')
|
||||
const fs = require('fs')
|
||||
const debug = require('debug')('pm2:sysinfos')
|
||||
|
||||
class SystemInfo {
|
||||
constructor() {
|
||||
this.infos = {
|
||||
baseboard: {
|
||||
model: null,
|
||||
version: null
|
||||
},
|
||||
cpu: {
|
||||
manufacturer: null,
|
||||
brand: null,
|
||||
speedmax: null,
|
||||
cores: null,
|
||||
physicalCores: null,
|
||||
processors: null,
|
||||
temperature: null,
|
||||
usage: null
|
||||
},
|
||||
graphics: {
|
||||
model: null,
|
||||
driverVersion: null,
|
||||
memTotal: null,
|
||||
memUsed: null,
|
||||
temperature: null
|
||||
},
|
||||
mem: {
|
||||
total: null,
|
||||
free: null,
|
||||
active: null
|
||||
},
|
||||
os: {
|
||||
platform: null,
|
||||
distro: null,
|
||||
release: null,
|
||||
codename: null,
|
||||
kernel: null,
|
||||
arch: null,
|
||||
},
|
||||
fd: {
|
||||
opened: null,
|
||||
max: null
|
||||
},
|
||||
storage: {
|
||||
io: {
|
||||
read: new MeanCalc(15),
|
||||
write: new MeanCalc(15)
|
||||
},
|
||||
physical_disks: [{
|
||||
device: null,
|
||||
type: null,
|
||||
name: null,
|
||||
interfaceType: null,
|
||||
vendor: null
|
||||
}],
|
||||
filesystems: [{
|
||||
}]
|
||||
},
|
||||
connections: ['source_ip:source_port-dest_ip:dest_port-proc_name'],
|
||||
network: {
|
||||
latency: new MeanCalc(5),
|
||||
tx_5: new MeanCalc(5),
|
||||
rx_5: new MeanCalc(5),
|
||||
rx_errors_60: new MeanCalc(60),
|
||||
tx_errors_60: new MeanCalc(60),
|
||||
tx_dropped_60: new MeanCalc(60),
|
||||
rx_dropped_60: new MeanCalc(60)
|
||||
},
|
||||
// Procs
|
||||
containers: [],
|
||||
processes: {
|
||||
cpu_sorted: null,
|
||||
mem_sorted: null
|
||||
},
|
||||
services: {
|
||||
running: null,
|
||||
stopped: null
|
||||
}
|
||||
}
|
||||
this.restart = true
|
||||
this.ping_timeout = null
|
||||
}
|
||||
|
||||
// Cast MeanCalc and other object to real value
|
||||
// This method retrieve the machine snapshot well formated
|
||||
report() {
|
||||
var report = JSON.parse(JSON.stringify(this.infos))
|
||||
report.network.latency = this.infos.network.latency.val()
|
||||
report.network.tx_5 = this.infos.network.tx_5.val()
|
||||
report.network.rx_5 = this.infos.network.rx_5.val()
|
||||
report.network.rx_errors_60 = this.infos.network.rx_errors_60.val()
|
||||
report.network.tx_errors_60 = this.infos.network.tx_errors_60.val()
|
||||
report.network.rx_dropped_60 = this.infos.network.rx_dropped_60.val()
|
||||
report.network.tx_dropped_60 = this.infos.network.tx_dropped_60.val()
|
||||
report.storage.io.read = this.infos.storage.io.read.val()
|
||||
report.storage.io.write = this.infos.storage.io.write.val()
|
||||
return report
|
||||
}
|
||||
|
||||
fork() {
|
||||
this.process = fork(__filename, {
|
||||
detached: false,
|
||||
windowsHide: true,
|
||||
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
|
||||
})
|
||||
|
||||
this.process.on('exit', (code) => {
|
||||
console.log(`systeminfos collection process offline with code ${code}`)
|
||||
// if (this.restart == true)
|
||||
// this.fork()
|
||||
})
|
||||
|
||||
this.process.on('error', (e) => {
|
||||
console.log(`Sysinfo errored`, e)
|
||||
})
|
||||
|
||||
this.process.on('message', (msg) => {
|
||||
try {
|
||||
msg = JSON.parse(msg)
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
if (msg.cmd == 'ping') {
|
||||
if (this.process.connected == true) {
|
||||
try {
|
||||
this.process.send('pong')
|
||||
} catch(e) {
|
||||
console.error('Cannot send message to Sysinfos')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
query(cb) {
|
||||
if (this.process.connected == true) {
|
||||
try {
|
||||
this.process.send('query')
|
||||
} catch(e) {
|
||||
return cb(new Error('not ready yet'), null)
|
||||
}
|
||||
}
|
||||
else
|
||||
return cb(new Error('not ready yet'), null)
|
||||
|
||||
var res = (msg) => {
|
||||
try {
|
||||
msg = JSON.parse(msg)
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (msg.cmd == 'query:res') {
|
||||
listener.removeListener('message', res)
|
||||
return cb(null, msg.data)
|
||||
}
|
||||
}
|
||||
|
||||
var listener = this.process.on('message', res)
|
||||
}
|
||||
|
||||
kill() {
|
||||
this.restart = false
|
||||
this.process.kill()
|
||||
}
|
||||
|
||||
startCollection() {
|
||||
this.staticInformations()
|
||||
|
||||
var dockerCollection, processCollection, memCollection,
|
||||
servicesCollection, graphicsCollection
|
||||
|
||||
(dockerCollection = () => {
|
||||
this.dockerSummary(() => {
|
||||
setTimeout(dockerCollection.bind(this), 5000)
|
||||
})
|
||||
})();
|
||||
|
||||
(processCollection = () => {
|
||||
this.processesSummary(() => {
|
||||
setTimeout(processCollection.bind(this), 5000)
|
||||
})
|
||||
})();
|
||||
|
||||
(graphicsCollection = () => {
|
||||
this.graphicsInformations(() => {
|
||||
setTimeout(graphicsCollection.bind(this), 20000)
|
||||
})
|
||||
})();
|
||||
|
||||
// (servicesCollection = () => {
|
||||
// this.servicesSummary(() => {
|
||||
// setTimeout(servicesCollection.bind(this), 60000)
|
||||
// })
|
||||
// })();
|
||||
|
||||
(memCollection = () => {
|
||||
this.memStats(() => {
|
||||
setTimeout(memCollection.bind(this), 1000)
|
||||
})
|
||||
})();
|
||||
|
||||
//this.networkConnectionsWorker()
|
||||
this.disksStatsWorker()
|
||||
this.networkStatsWorker()
|
||||
|
||||
this.cpuStatsWorker()
|
||||
this.fdStatsWorker()
|
||||
|
||||
setInterval(() => {
|
||||
if (process.connected == false) {
|
||||
console.error('Sysinfos not connected, exiting')
|
||||
process.exit()
|
||||
}
|
||||
try {
|
||||
process.send(JSON.stringify({cmd: 'ping'}))
|
||||
} catch(e) {
|
||||
console.error('PM2 is dead while doing process.send')
|
||||
process.exit()
|
||||
}
|
||||
this.ping_timeout = setTimeout(() => {
|
||||
console.error('PM2 is dead while waiting for a pong')
|
||||
process.exit()
|
||||
}, 2000)
|
||||
}, 3000)
|
||||
|
||||
// Systeminfo receive command
|
||||
process.on('message', (cmd) => {
|
||||
if (cmd == 'query') {
|
||||
try {
|
||||
var res = JSON.stringify({
|
||||
cmd: 'query:res',
|
||||
data: this.report()
|
||||
})
|
||||
process.send(res)
|
||||
} catch (e) {
|
||||
console.error('Could not retrieve system informations', e)
|
||||
}
|
||||
}
|
||||
else if (cmd == 'pong') {
|
||||
clearTimeout(this.ping_timeout)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
staticInformations() {
|
||||
var getCPU = () => {
|
||||
return sysinfo.cpu()
|
||||
.then(data => {
|
||||
this.infos.cpu = {
|
||||
brand: data.manufacturer,
|
||||
model: data.brand,
|
||||
speed: data.speedmax,
|
||||
cores: data.cores,
|
||||
physicalCores: data.physicalCores
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var getBaseboard = () => {
|
||||
return sysinfo.system()
|
||||
.then(data => {
|
||||
this.infos.baseboard = {
|
||||
manufacturer: data.manufacturer,
|
||||
model: data.model,
|
||||
version: data.version
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var getOsInfo = () => {
|
||||
return sysinfo.osInfo()
|
||||
.then(data => {
|
||||
this.infos.os = {
|
||||
platform: data.platform,
|
||||
distro: data.distro,
|
||||
release: data.release,
|
||||
codename: data.codename,
|
||||
kernel: data.kernel,
|
||||
arch: data.arch
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var diskLayout = () => {
|
||||
this.infos.storage.physical_disks = []
|
||||
|
||||
return sysinfo.diskLayout()
|
||||
.then(disks => {
|
||||
disks.forEach((disk) => {
|
||||
this.infos.storage.physical_disks.push({
|
||||
device: disk.device,
|
||||
type: disk.type,
|
||||
name: disk.name,
|
||||
interfaceType: disk.interfaceType,
|
||||
vendor: disk.vendor
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getBaseboard()
|
||||
.then(getCPU)
|
||||
.then(getOsInfo)
|
||||
.then(diskLayout)
|
||||
.catch(e => {
|
||||
debug(`Error when trying to retrieve static informations`, e)
|
||||
})
|
||||
}
|
||||
|
||||
dockerSummary(cb = () => {}) {
|
||||
sysinfo.dockerContainers('all')
|
||||
.then(containers => {
|
||||
var non_exited_containers = containers.filter(container => container.state != 'exited')
|
||||
var new_containers = []
|
||||
|
||||
async.forEach(non_exited_containers, (container, next) => {
|
||||
sysinfo.dockerContainerStats(container.id)
|
||||
.then(stats => {
|
||||
var meta = container
|
||||
|
||||
stats[0].cpu_percent = (stats[0].cpu_percent).toFixed(1)
|
||||
stats[0].mem_percent = (stats[0].mem_percent).toFixed(1)
|
||||
stats[0].netIO.tx = (stats[0].netIO.tx / DEFAULT_CONVERSION).toFixed(1)
|
||||
stats[0].netIO.rx = (stats[0].netIO.rx / DEFAULT_CONVERSION).toFixed(1)
|
||||
|
||||
stats[0].blockIO.w = (stats[0].blockIO.w / DEFAULT_CONVERSION).toFixed(1)
|
||||
stats[0].blockIO.r = (stats[0].blockIO.r / DEFAULT_CONVERSION).toFixed(1)
|
||||
|
||||
meta.stats = Array.isArray(stats) == true ? stats[0] : null
|
||||
new_containers.push(meta)
|
||||
next()
|
||||
})
|
||||
.catch(e => {
|
||||
debug(e)
|
||||
next()
|
||||
})
|
||||
}, (err) => {
|
||||
if (err)
|
||||
debug(err)
|
||||
this.infos.containers = new_containers.sort((a, b) => {
|
||||
var textA = a.name.toUpperCase();
|
||||
var textB = b.name.toUpperCase();
|
||||
return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
|
||||
})
|
||||
return cb()
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
debug(e)
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
servicesSummary() {
|
||||
sysinfo.services('*')
|
||||
.then(services => {
|
||||
this.infos.services.running = services.filter(service => service.running === true)
|
||||
this.infos.services.stopped = services.filter(service => service.running === false)
|
||||
})
|
||||
.catch(e => {
|
||||
debug(e)
|
||||
})
|
||||
}
|
||||
|
||||
processesSummary(cb) {
|
||||
psList()
|
||||
.then(processes => {
|
||||
this.infos.processes.cpu_sorted = processes
|
||||
.filter(a => !(a.cmd.includes('SystemInfo') && a.cmd.includes('PM2')))
|
||||
.sort((a, b) => b.cpu - a.cpu).slice(0, 5)
|
||||
this.infos.processes.mem_sorted = processes
|
||||
.filter(a => !(a.cmd.includes('SystemInfo') && a.cmd.includes('PM2')))
|
||||
.sort((a, b) => b.memory - a.memory).slice(0, 5)
|
||||
return cb()
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error when retrieving process list`, e)
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
graphicsInformations(cb) {
|
||||
sysinfo.graphics()
|
||||
.then(data => {
|
||||
let cg1 = data.controllers[0]
|
||||
if (!cg1) return cb()
|
||||
|
||||
this.infos.graphics = {
|
||||
model: cg1.model,
|
||||
driverVersion: cg1.driverVersion,
|
||||
memTotal: cg1.memoryTotal,
|
||||
memUsed: cg1.memoryUsed,
|
||||
temperature: cg1.temperatureGpu
|
||||
}
|
||||
return cb()
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while retrieving graphics informations`)
|
||||
console.error(e)
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
cpuStatsWorker() {
|
||||
var cpuTempCollection
|
||||
|
||||
(cpuTempCollection = () => {
|
||||
sysinfo.cpuTemperature()
|
||||
.then(data => {
|
||||
this.infos.cpu.temperature = data.main
|
||||
setTimeout(cpuTempCollection.bind(this), 2000)
|
||||
})
|
||||
.catch(e => {
|
||||
setTimeout(cpuTempCollection.bind(this), 2000)
|
||||
})
|
||||
})()
|
||||
|
||||
function fetch () {
|
||||
const startMeasure = computeUsage()
|
||||
|
||||
setTimeout(_ => {
|
||||
var endMeasure = computeUsage()
|
||||
|
||||
var idleDifference = endMeasure.idle - startMeasure.idle
|
||||
var totalDifference = endMeasure.total - startMeasure.total
|
||||
|
||||
var percentageCPU = (10000 - Math.round(10000 * idleDifference / totalDifference)) / 100
|
||||
this.infos.cpu.usage = (percentageCPU).toFixed(1)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function computeUsage () {
|
||||
let totalIdle = 0
|
||||
let totalTick = 0
|
||||
const cpus = os.cpus()
|
||||
|
||||
for (var i = 0, len = cpus.length; i < len; i++) {
|
||||
var cpu = cpus[i]
|
||||
for (let type in cpu.times) {
|
||||
totalTick += cpu.times[type]
|
||||
}
|
||||
totalIdle += cpu.times.idle
|
||||
}
|
||||
|
||||
return {
|
||||
idle: parseInt(totalIdle / cpus.length),
|
||||
total: parseInt(totalTick / cpus.length)
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(fetch.bind(this), 1000)
|
||||
fetch.bind(this)()
|
||||
}
|
||||
|
||||
memStats(cb) {
|
||||
sysinfo.mem()
|
||||
.then(data => {
|
||||
this.infos.mem.total = (data.total / DEFAULT_CONVERSION).toFixed()
|
||||
this.infos.mem.free = (data.free / DEFAULT_CONVERSION).toFixed()
|
||||
this.infos.mem.active = (data.active / DEFAULT_CONVERSION).toFixed()
|
||||
this.infos.mem.available = (data.available / DEFAULT_CONVERSION).toFixed()
|
||||
return cb()
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while retrieving memory info`)
|
||||
console.error(e)
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
networkConnectionsWorker() {
|
||||
var retrieveConn
|
||||
|
||||
(retrieveConn = () => {
|
||||
sysinfo.networkConnections()
|
||||
.then(conns => {
|
||||
this.infos.connections = conns
|
||||
.filter(conn => conn.localport != '443' && conn.peerport != '443')
|
||||
.map(conn => `${conn.localaddress}:${conn.localport}-${conn.peeraddress}:${conn.peerport}-${conn.proc ? conn.proc : 'unknown'}`)
|
||||
setTimeout(retrieveConn.bind(this), 10 * 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while retrieving filesystems info`)
|
||||
console.error(e)
|
||||
setTimeout(retrieveConn.bind(this), 10 * 1000)
|
||||
})
|
||||
})();
|
||||
}
|
||||
|
||||
disksStatsWorker() {
|
||||
var rx = 0
|
||||
var wx = 0
|
||||
var started = false
|
||||
var fsSizeCollection, ioCollection
|
||||
|
||||
(fsSizeCollection = () => {
|
||||
sysinfo.fsSize()
|
||||
.then(fss => {
|
||||
var fse = fss.filter(fs => (fs.size / (1024 * 1024)) > 200)
|
||||
this.infos.storage.filesystems = fse
|
||||
setTimeout(fsSizeCollection.bind(this), 30 * 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while retrieving filesystem infos (FSSIZE)`, e)
|
||||
setTimeout(fsSizeCollection.bind(this), 10 * 1000)
|
||||
})
|
||||
})();
|
||||
|
||||
(ioCollection = () => {
|
||||
sysinfo.fsStats()
|
||||
.then(fs_stats => {
|
||||
var new_rx = fs_stats.rx
|
||||
var new_wx = fs_stats.wx
|
||||
|
||||
var read = ((new_rx - rx) / DEFAULT_CONVERSION).toFixed(3)
|
||||
var write = ((new_wx - wx) / DEFAULT_CONVERSION).toFixed(3)
|
||||
|
||||
if (started == true) {
|
||||
this.infos.storage.io.read.add(parseFloat(read))
|
||||
this.infos.storage.io.write.add(parseFloat(write))
|
||||
}
|
||||
|
||||
rx = new_rx
|
||||
wx = new_wx
|
||||
started = true
|
||||
setTimeout(ioCollection.bind(this), 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error while getting network statistics`, e)
|
||||
setTimeout(ioCollection.bind(this), 1000)
|
||||
})
|
||||
})();
|
||||
}
|
||||
|
||||
fdStatsWorker() {
|
||||
var getFDOpened = () => {
|
||||
fs.readFile('/proc/sys/fs/file-nr', (err, out) => {
|
||||
if (err) return
|
||||
const output = out.toString().trim()
|
||||
const parsed = output.split('\t')
|
||||
if (parsed.length !== 3) return
|
||||
this.infos.fd.opened = parseInt(parsed[0])
|
||||
this.infos.fd.max = parseInt(parsed[2])
|
||||
})
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
getFDOpened()
|
||||
}, 20 * 1000)
|
||||
|
||||
getFDOpened()
|
||||
}
|
||||
|
||||
networkStatsWorker() {
|
||||
var latencyCollection, networkStatsCollection
|
||||
|
||||
// (latencyCollection = () => {
|
||||
// sysinfo.inetLatency()
|
||||
// .then(latency => {
|
||||
// this.infos.network.latency.add(latency)
|
||||
// setTimeout(latencyCollection.bind(this), 2000)
|
||||
// })
|
||||
// .catch(e => {
|
||||
// debug(e)
|
||||
// setTimeout(latencyCollection.bind(this), 2000)
|
||||
// })
|
||||
// })()
|
||||
|
||||
sysinfo.networkInterfaceDefault((net_interface) => {
|
||||
var started = false
|
||||
var rx = 0
|
||||
var tx = 0
|
||||
var rx_e = 0
|
||||
var tx_e = 0
|
||||
var rx_d = 0
|
||||
var tx_d = 0;
|
||||
|
||||
(networkStatsCollection = () => {
|
||||
sysinfo.networkStats(net_interface)
|
||||
.then((net) => {
|
||||
var new_rx = (net[0].rx_bytes - rx) / DEFAULT_CONVERSION
|
||||
var new_tx = (net[0].tx_bytes - tx) / DEFAULT_CONVERSION
|
||||
rx = net[0].rx_bytes
|
||||
tx = net[0].tx_bytes
|
||||
|
||||
var new_rx_e = (net[0].rx_errors - rx_e) / DEFAULT_CONVERSION
|
||||
var new_tx_e = (net[0].tx_errors - tx_e) / DEFAULT_CONVERSION
|
||||
rx_e = net[0].rx_errors
|
||||
tx_e = net[0].tx_errors
|
||||
|
||||
var new_rx_d = (net[0].rx_dropped - rx_d) / DEFAULT_CONVERSION
|
||||
var new_tx_d = (net[0].tx_dropped - tx_d) / DEFAULT_CONVERSION
|
||||
rx_d = net[0].rx_dropped
|
||||
tx_d = net[0].tx_dropped
|
||||
|
||||
if (started == true) {
|
||||
this.infos.network.rx_5.add(new_rx)
|
||||
this.infos.network.tx_5.add(new_tx)
|
||||
this.infos.network.rx_errors_60.add(new_rx_e)
|
||||
this.infos.network.tx_errors_60.add(new_tx_e)
|
||||
this.infos.network.rx_dropped_60.add(new_rx_d)
|
||||
this.infos.network.tx_dropped_60.add(new_tx_d)
|
||||
}
|
||||
started = true
|
||||
setTimeout(networkStatsCollection.bind(this), 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`Error on retrieving network stats`, e)
|
||||
setTimeout(networkStatsCollection.bind(this), 900)
|
||||
})
|
||||
})()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SystemInfo
|
||||
|
||||
if (require.main === module) {
|
||||
var sys = new SystemInfo()
|
||||
sys.startCollection()
|
||||
}
|
||||
Binary file not shown.
@ -1,63 +0,0 @@
|
||||
'use strict';
|
||||
const util = require('util');
|
||||
const path = require('path');
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const TEN_MEGABYTES = 1000 * 1000 * 10;
|
||||
const execFile = util.promisify(childProcess.execFile);
|
||||
|
||||
const windows = async () => {
|
||||
// Source: https://github.com/MarkTiedemann/fastlist
|
||||
const bin = path.join(__dirname, 'fastlist.exe');
|
||||
|
||||
const {stdout} = await execFile(bin, {maxBuffer: TEN_MEGABYTES});
|
||||
|
||||
return stdout
|
||||
.trim()
|
||||
.split('\r\n')
|
||||
.map(line => line.split('\t'))
|
||||
.map(([name, pid, ppid]) => ({
|
||||
name,
|
||||
pid: Number.parseInt(pid, 10),
|
||||
ppid: Number.parseInt(ppid, 10)
|
||||
}));
|
||||
};
|
||||
|
||||
const main = async (options = {}) => {
|
||||
const flags = (options.all === false ? '' : 'a') + 'wwxo';
|
||||
const ret = {};
|
||||
|
||||
await Promise.all(['comm', 'args', 'ppid', 'uid', '%cpu', '%mem'].map(async cmd => {
|
||||
const {stdout} = await execFile('ps', [flags, `pid,${cmd}`], {maxBuffer: TEN_MEGABYTES});
|
||||
|
||||
for (let line of stdout.trim().split('\n').slice(1)) {
|
||||
line = line.trim();
|
||||
const [pid] = line.split(' ', 1);
|
||||
const val = line.slice(pid.length + 1).trim();
|
||||
|
||||
if (ret[pid] === undefined) {
|
||||
ret[pid] = {};
|
||||
}
|
||||
|
||||
ret[pid][cmd] = val;
|
||||
}
|
||||
}));
|
||||
|
||||
// Filter out inconsistencies as there might be race
|
||||
// issues due to differences in `ps` between the spawns
|
||||
return Object.entries(ret)
|
||||
.filter(([, value]) => value.comm && value.args && value.ppid && value.uid && value['%cpu'] && value['%mem'])
|
||||
.map(([key, value]) => ({
|
||||
pid: Number.parseInt(key, 10),
|
||||
name: path.basename(value.comm),
|
||||
cmd: value.args,
|
||||
ppid: Number.parseInt(value.ppid, 10),
|
||||
uid: Number.parseInt(value.uid, 10),
|
||||
cpu: Number.parseFloat(value['%cpu']),
|
||||
memory: Number.parseFloat(value['%mem'])
|
||||
}));
|
||||
};
|
||||
|
||||
module.exports = process.platform === 'win32' ? windows : main;
|
||||
// TODO: remove this in the next major version
|
||||
module.exports.default = module.exports;
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "pm2",
|
||||
"preferGlobal": true,
|
||||
"version": "4.5.1",
|
||||
"version": "5.0.0",
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"directories": {
|
||||
"bin": "./bin",
|
||||
@ -201,9 +201,6 @@
|
||||
"mocha": "^7.1.0",
|
||||
"should": "^13"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"systeminformation": "^4.32"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Unitech/pm2/issues"
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user