optimization fsStats, disksIO, networkStats

This commit is contained in:
Sebastian Hildebrandt 2016-11-14 11:49:46 +01:00
parent 78c400f051
commit fedebe797a
5 changed files with 297 additions and 216 deletions

View File

@ -89,6 +89,7 @@ Other changes
| Version | Date | Comment | | Version | Date | Comment |
| -------------- | -------------- | -------- | | -------------- | -------------- | -------- |
| 3.10.1 | 2016-11-14 | optimization fsStats, disksIO, networkStats |
| 3.10.0 | 2016-11-12 | added blockDevices, fixed fsSize, added file system type | | 3.10.0 | 2016-11-12 | added blockDevices, fixed fsSize, added file system type |
| 3.9.0 | 2016-11-11 | added MAC address to networkInterfaces, fixed currentLoad | | 3.9.0 | 2016-11-11 | added MAC address to networkInterfaces, fixed currentLoad |
| 3.8.1 | 2016-11-04 | updated docs | | 3.8.1 | 2016-11-04 | updated docs |

View File

@ -132,6 +132,49 @@ exports.blockDevices = blockDevices;
// -------------------------- // --------------------------
// FS - speed // FS - speed
function calcFsSpeed(rx, wx) {
let result = {
rx: 0,
wx: 0,
tx: 0,
rx_sec: -1,
wx_sec: -1,
tx_sec: -1,
ms: 0
};
if (_fs_speed && _fs_speed.ms) {
result.rx = rx;
result.wx = wx;
result.tx = result.rx + result.wx;
result.ms = Date.now() - _fs_speed.ms;
result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
result.tx_sec = result.rx_sec + result.wx_sec;
_fs_speed.rx_sec = result.rx_sec;
_fs_speed.wx_sec = result.wx_sec;
_fs_speed.tx_sec = result.tx_sec;
_fs_speed.bytes_read = result.rx;
_fs_speed.bytes_write = result.wx;
_fs_speed.bytes_overall = result.rx + result.wx;
_fs_speed.ms = Date.now();
_fs_speed.last_ms = result.ms;
} else {
result.rx = rx;
result.wx = wx;
result.tx = result.rx + result.wx;
_fs_speed.rx_sec = -1;
_fs_speed.wx_sec = -1;
_fs_speed.tx_sec = -1;
_fs_speed.bytes_read = result.rx;
_fs_speed.bytes_write = result.wx;
_fs_speed.bytes_overall = result.rx + result.wx;
_fs_speed.ms = Date.now();
_fs_speed.last_ms = 0;
}
return result;
}
function fsStats(callback) { function fsStats(callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -142,7 +185,7 @@ function fsStats(callback) {
reject(error); reject(error);
} }
var result = { let result = {
rx: 0, rx: 0,
wx: 0, wx: 0,
tx: 0, tx: 0,
@ -152,89 +195,75 @@ function fsStats(callback) {
ms: 0 ms: 0
}; };
if (_linux) { let rx = 0;
// exec("df -k | grep /dev/", function(error, stdout) { let wx = 0;
exec("lsblk | grep /", function (error, stdout) { if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) {
if (!error) { if (_linux) {
let lines = stdout.toString().split('\n'); // exec("df -k | grep /dev/", function(error, stdout) {
let fs_filter = []; exec("lsblk | grep /", function (error, stdout) {
lines.forEach(function (line) { if (!error) {
if (line != '') { let lines = stdout.toString().split('\n');
line = line.replace(/[├─│└]+/g, "").trim().split(' '); let fs_filter = [];
if (fs_filter.indexOf(line[0]) == -1) fs_filter.push(line[0]) lines.forEach(function (line) {
} if (line != '') {
}); line = line.replace(/[├─│└]+/g, "").trim().split(' ');
if (fs_filter.indexOf(line[0]) == -1) fs_filter.push(line[0])
let output = fs_filter.join('|');
exec("cat /proc/diskstats | egrep '" + output + "'", function (error, stdout) {
if (!error) {
let lines = stdout.toString().split('\n');
lines.forEach(function (line) {
line = line.trim();
if (line != '') {
line = line.replace(/ +/g, " ").split(' ');
result.rx += parseInt(line[5]) * 512;
result.wx += parseInt(line[9]) * 512;
}
});
result.tx = result.rx + result.wx;
if (_fs_speed && _fs_speed.ms) {
result.ms = Date.now() - _fs_speed.ms;
result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
result.tx_sec = result.rx_sec + result.wx_sec;
} else {
result.rx_sec = -1;
result.wx_sec = -1;
result.tx_sec = -1;
} }
_fs_speed.bytes_read = result.rx; });
_fs_speed.bytes_write = result.wx;
_fs_speed.bytes_overall = result.rx + result.wx; let output = fs_filter.join('|');
_fs_speed.ms = Date.now(); exec("cat /proc/diskstats | egrep '" + output + "'", function (error, stdout) {
} if (!error) {
let lines = stdout.toString().split('\n');
lines.forEach(function (line) {
line = line.trim();
if (line != '') {
line = line.replace(/ +/g, " ").split(' ');
rx += parseInt(line[5]) * 512;
wx += parseInt(line[9]) * 512;
}
});
result = calcFsSpeed(rx, wx);
}
if (callback) { callback(result) }
resolve(result);
})
} else {
if (callback) { callback(result) } if (callback) { callback(result) }
resolve(result); resolve(result);
}) }
} else { })
}
if (_darwin) {
exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $3, $10}'", function (error, stdout) {
if (!error) {
let lines = stdout.toString().split('\n');
lines.forEach(function (line) {
line = line.trim();
if (line != '') {
line = line.split(' ');
rx += parseInt(line[0]);
wx += parseInt(line[1]);
}
});
result = calcFsSpeed(rx, wx);
}
if (callback) { callback(result) } if (callback) { callback(result) }
resolve(result); resolve(result);
} })
}) }
} } else {
if (_darwin) { result.ms = _fs_speed.last_ms;
exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $3, $10}'", function (error, stdout) { result.rx = _fs_speed.bytes_read;
if (!error) { result.wx = _fs_speed.bytes_write;
let lines = stdout.toString().split('\n'); result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write;
lines.forEach(function (line) { result.rx_sec = _fs_speed.rx_sec;
line = line.trim(); result.wx_sec = _fs_speed.wx_sec;
if (line != '') { result.tx_sec = _fs_speed.tx_sec;
line = line.split(' '); if (callback) { callback(result) }
resolve(result);
result.rx += parseInt(line[0]);
result.wx += parseInt(line[1]);
}
});
result.tx = result.rx + result.wx;
if (_fs_speed && _fs_speed.ms) {
result.ms = Date.now() - _fs_speed.ms;
result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
result.tx_sec = result.rx_sec + result.wx_sec;
} else {
result.rx_sec = -1;
result.wx_sec = -1;
result.tx_sec = -1;
}
_fs_speed.bytes_read = result.rx;
_fs_speed.bytes_write = result.wx;
_fs_speed.bytes_overall = result.rx + result.wx;
_fs_speed.ms = Date.now();
}
if (callback) { callback(result) }
resolve(result);
})
} }
}); });
}); });
@ -242,6 +271,46 @@ function fsStats(callback) {
exports.fsStats = fsStats; exports.fsStats = fsStats;
function calcDiskIO(rIO, wIO) {
let result = {
rIO: 0,
wIO: 0,
tIO: 0,
rIO_sec: -1,
wIO_sec: -1,
tIO_sec: -1,
ms: 0
};
if (_disk_io && _disk_io.ms) {
result.rIO = rIO;
result.wIO = wIO;
result.tIO = rIO + wIO;
result.ms = Date.now() - _disk_io.ms;
result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
result.tIO_sec = result.rIO_sec + result.wIO_sec;
_disk_io.rIO = rIO;
_disk_io.wIO = wIO;
_disk_io.rIO_sec = result.rIO_sec;
_disk_io.wIO_sec = result.wIO_sec;
_disk_io.tIO_sec = result.tIO_sec;
_disk_io.last_ms = result.ms;
_disk_io.ms = Date.now();
} else {
result.rIO = rIO;
result.wIO = wIO;
result.tIO = rIO + wIO;
_disk_io.rIO = rIO;
_disk_io.wIO = wIO;
_disk_io.rIO_sec = -1;
_disk_io.wIO_sec = -1;
_disk_io.tIO_sec = -1;
_disk_io.last_ms = 0;
_disk_io.ms = Date.now();
}
return result;
}
function disksIO(callback) { function disksIO(callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -252,7 +321,7 @@ function disksIO(callback) {
reject(error); reject(error);
} }
var result = { let result = {
rIO: 0, rIO: 0,
wIO: 0, wIO: 0,
tIO: 0, tIO: 0,
@ -261,81 +330,67 @@ function disksIO(callback) {
tIO_sec: -1, tIO_sec: -1,
ms: 0 ms: 0
}; };
if (_linux) { let rIO = 0;
// prints Block layer statistics for all mounted volumes let wIO = 0;
// var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
// var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
let cmd = "for mount in `lsblk | grep ' disk ' | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
exec(cmd, function (error, stdout) { if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) {
if (!error) { if (_linux) {
let lines = stdout.split('\n'); // prints Block layer statistics for all mounted volumes
lines.forEach(function (line) { // var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
// ignore empty lines // var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
if (!line) return; let cmd = "for mount in `lsblk | grep ' disk ' | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
// sum r/wIO of all disks to compute all disks IO exec(cmd, function (error, stdout) {
let stats = line.split(';'); if (!error) {
result.rIO += parseInt(stats[0]); let lines = stdout.split('\n');
result.wIO += parseInt(stats[4]); lines.forEach(function (line) {
}); // ignore empty lines
result.tIO = result.rIO + result.wIO; if (!line) return;
if (_disk_io && _disk_io.ms) {
result.ms = Date.now() - _disk_io.ms; // sum r/wIO of all disks to compute all disks IO
result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000); let stats = line.split(';');
result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000); rIO += parseInt(stats[0]);
result.tIO_sec = result.rIO_sec + result.wIO_sec; wIO += parseInt(stats[4]);
});
result = calcDiskIO(rIO, wIO);
if (callback) { callback(result) }
resolve(result);
} else { } else {
result.rIO_sec = -1; if (callback) { callback(result) }
result.wIO_sec = -1; resolve(result);
result.tIO_sec = -1;
} }
_disk_io.rIO = result.rIO; });
_disk_io.wIO = result.wIO; }
_disk_io.tIO = result.tIO; if (_darwin) {
_disk_io.ms = Date.now(); exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $1, $11}'", function (error, stdout) {
if (!error) {
let lines = stdout.toString().split('\n');
lines.forEach(function (line) {
line = line.trim();
if (line != '') {
line = line.split(' ');
rIO += parseInt(line[1]);
wIO += parseInt(line[0]);
}
});
result = calcDiskIO(rIO, wIO);
}
if (callback) { callback(result) } if (callback) { callback(result) }
resolve(result); resolve(result);
} else { })
if (callback) { callback(result) } }
resolve(result); } else {
} result.rIO = _disk_io.rIO;
}); result.wIO = _disk_io.wIO;
} result.tIO = _disk_io.rIO + _disk_io.wIO;
if (_darwin) { result.ms = _disk_io.last_ms;
exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $1, $11}'", function (error, stdout) { result.rIO_sec = _disk_io.rIO_sec;
if (!error) { result.wIO_sec = _disk_io.wIO_sec;
let lines = stdout.toString().split('\n'); result.tIO_sec = _disk_io.tIO_sec;
lines.forEach(function (line) { if (callback) { callback(result) }
line = line.trim(); resolve(result);
if (line != '') {
line = line.split(' ');
result.rIO += parseInt(line[1]);
result.wIO += parseInt(line[0]);
}
});
result.tIO = result.rIO + result.wIO;
if (_disk_io && _disk_io.ms) {
result.ms = Date.now() - _disk_io.ms;
result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
result.tIO_sec = result.rIO_sec + result.wIO_sec;
} else {
result.rIO_sec = -1;
result.wIO_sec = -1;
result.tIO_sec = -1;
}
_disk_io.rIO = result.rIO;
_disk_io.wIO = result.wIO;
_disk_io.tIO = result.tIO;
_disk_io.ms = Date.now();
}
if (callback) { callback(result) }
resolve(result);
})
} }
}); });
}); });

View File

@ -81,6 +81,7 @@
// -------------------------------- // --------------------------------
// //
// version date comment // version date comment
// 3.10.1 2016-11-14 optimization fsStats, disksIO, networkStats
// 3.10.0 2016-11-12 added blockDevices, fixed fsSize, added file system type // 3.10.0 2016-11-12 added blockDevices, fixed fsSize, added file system type
// 3.9.0 2016-11-11 added MAC address to networkInterfaces, fixed currentLoad // 3.9.0 2016-11-11 added MAC address to networkInterfaces, fixed currentLoad
// 3.8.1 2016-11-04 updated docs // 3.8.1 2016-11-04 updated docs

View File

@ -108,7 +108,7 @@ function networkInterfaces(callback) {
mac = details.mac mac = details.mac
}); });
let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : null; let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : null;
result.push({ iface: dev, ip4: ip4, ip6: ip6, mac : mac, internal: internal }) result.push({ iface: dev, ip4: ip4, ip6: ip6, mac: mac, internal: internal })
} }
} }
if (callback) { callback(result) } if (callback) { callback(result) }
@ -122,26 +122,43 @@ exports.networkInterfaces = networkInterfaces;
// -------------------------- // --------------------------
// NET - Speed // NET - Speed
function calcNetworkSpeed(iface, rx, tx) { function calcNetworkSpeed(iface, rx, tx, operstate) {
let rx_sec = -1; let result = {
let tx_sec = -1; iface: iface,
let ms = 0; operstate: operstate,
rx: 0,
tx: 0,
rx_sec: -1,
tx_sec: -1,
ms: 0
};
if (_network[iface] && _network[iface].ms) { if (_network[iface] && _network[iface].ms) {
ms = Date.now() - _network[iface].ms; result.rx = rx;
rx_sec = (rx - _network[iface].rx) / (ms / 1000); result.tx = tx;
tx_sec = (tx - _network[iface].tx) / (ms / 1000); result.ms = Date.now() - _network[iface].ms;
result.rx_sec = (rx - _network[iface].rx) / (result.ms / 1000);
result.tx_sec = (tx - _network[iface].tx) / (result.ms / 1000);
_network[iface].rx = rx;
_network[iface].tx = tx;
_network[iface].rx_sec = result.rx_sec;
_network[iface].tx_sec = result.tx_sec;
_network[iface].ms = Date.now();
_network[iface].last_ms = result.ms;
} else { } else {
_network[iface] = {}; result.rx = rx;
result.tx = tx;
if (!_network[iface]) _network[iface] = {};
_network[iface].rx = rx;
_network[iface].tx = tx;
_network[iface].rx_sec = -1;
_network[iface].tx_sec = -1;
_network[iface].ms = Date.now();
_network[iface].last_ms = 0;
} }
_network[iface].rx = rx; return result;
_network[iface].tx = tx;
_network[iface].ms = Date.now();
return ({
rx_sec: rx_sec,
tx_sec: tx_sec,
ms: ms
})
} }
function networkStats(iface, callback) { function networkStats(iface, callback) {
// fallback - if only callback is given // fallback - if only callback is given
@ -171,63 +188,70 @@ function networkStats(iface, callback) {
ms: 0 ms: 0
}; };
let cmd, lines, stats, speed; let operstate = 'unknown';
let rx = 0;
let tx = 0;
if (_linux) { let cmd, lines, stats;
if (fs.existsSync('/sys/class/net/' + iface)) { if (!_network[iface] || (_network[iface] && !_network[iface].ms) || (_network[iface] && _network[iface].ms && Date.now() - _network[iface].ms >= 500)) {
cmd = if (_linux) {
"cat /sys/class/net/" + iface + "/operstate; " + if (fs.existsSync('/sys/class/net/' + iface)) {
"cat /sys/class/net/" + iface + "/statistics/rx_bytes; " + cmd =
"cat /sys/class/net/" + iface + "/statistics/tx_bytes; "; "cat /sys/class/net/" + iface + "/operstate; " +
exec(cmd, function (error, stdout) { "cat /sys/class/net/" + iface + "/statistics/rx_bytes; " +
if (!error) { "cat /sys/class/net/" + iface + "/statistics/tx_bytes; ";
lines = stdout.toString().split('\n'); exec(cmd, function (error, stdout) {
result.operstate = lines[0].trim(); if (!error) {
result.rx = parseInt(lines[1]); lines = stdout.toString().split('\n');
result.tx = parseInt(lines[2]); operstate = lines[0].trim();
rx = parseInt(lines[1]);
tx = parseInt(lines[2]);
speed = calcNetworkSpeed(iface, result.rx, result.tx); result = calcNetworkSpeed(iface, rx, tx, operstate);
result.rx_sec = speed.rx_sec;
result.tx_sec = speed.tx_sec;
result.ms = speed.ms;
}
if (callback) { callback(result) }
resolve(result);
});
} else {
if (callback) { callback(result) }
resolve(result);
}
}
if (_darwin) {
cmd = "ifconfig " + iface + " | grep 'status'";
exec(cmd, function (error, stdout) {
result.operstate = (stdout.toString().split(':')[1] || '').trim();
result.operstate = (result.operstate || '').toLowerCase();
result.operstate = (result.operstate == 'active' ? 'up' : (result.operstate == 'inactive' ? 'down' : 'unknown'));
cmd = "netstat -bI " + iface;
exec(cmd, function (error, stdout) {
if (!error) {
lines = stdout.toString().split('\n');
// if there is less than 2 lines, no information for this interface was found
if (lines.length > 1 && lines[1].trim() != '') {
// skip header line
// use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address
stats = lines[1].replace(/ +/g, " ").split(' ');
result.rx = parseInt(stats[6]);
result.tx = parseInt(stats[9]);
speed = calcNetworkSpeed(iface, result.rx, result.tx);
result.rx_sec = speed.rx_sec;
result.tx_sec = speed.tx_sec;
} }
} if (callback) { callback(result) }
resolve(result);
});
} else {
if (callback) { callback(result) } if (callback) { callback(result) }
resolve(result); resolve(result);
}
}
if (_darwin) {
cmd = "ifconfig " + iface + " | grep 'status'";
exec(cmd, function (error, stdout) {
result.operstate = (stdout.toString().split(':')[1] || '').trim();
result.operstate = (result.operstate || '').toLowerCase();
result.operstate = (result.operstate == 'active' ? 'up' : (result.operstate == 'inactive' ? 'down' : 'unknown'));
cmd = "netstat -bI " + iface;
exec(cmd, function (error, stdout) {
if (!error) {
lines = stdout.toString().split('\n');
// if there is less than 2 lines, no information for this interface was found
if (lines.length > 1 && lines[1].trim() != '') {
// skip header line
// use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address
stats = lines[1].replace(/ +/g, " ").split(' ');
rx = parseInt(stats[6]);
tx = parseInt(stats[9]);
result = calcNetworkSpeed(iface, rx, tx, operstate);
}
}
if (callback) { callback(result) }
resolve(result);
});
}); });
}); }
} else {
result.rx = _network[iface].rx;
result.tx = _network[iface].tx;
result.rx_sec = _network[iface].rx_sec;
result.tx_sec = _network[iface].tx_sec;
result.ms = _network[iface].last_ms;
if (callback) { callback(result) }
resolve(result);
} }
}); });
}); });

View File

@ -84,7 +84,7 @@ function getLoad() {
} }
if (_linux) { if (_linux) {
let now = Date.now() - _current_cpu.ms; let now = Date.now() - _current_cpu.ms;
if (now >= 1000) { if (now >= 500) {
_current_cpu.ms = Date.now(); _current_cpu.ms = Date.now();
exec("cat /proc/stat | grep 'cpu '", function (error, stdout) { exec("cat /proc/stat | grep 'cpu '", function (error, stdout) {
if (!error) { if (!error) {