diff --git a/README.md b/README.md
index 26f89d1..5609bfc 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,8 @@ chartVisibility: {
cpu: true,
mem: true,
load: true,
+ eventLoop: true,
+ heap: true,
responseTime: true,
rps: true,
statusCodes: true
diff --git a/package-lock.json b/package-lock.json
index 2e74897..d1e9b9b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1336,6 +1336,14 @@
"tslib": "^1.10.0"
}
},
+ "event-loop-stats": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz",
+ "integrity": "sha512-h/leAlXqoEf+D9w1dnFG0srR5vfIq59rLm9PHzcl3/GwFppd+UR46UMuLdp/mvJvuA+MjSd/dNShmuM2/dPFFw==",
+ "requires": {
+ "nan": "^2.14.0"
+ }
+ },
"execa": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
@@ -2475,6 +2483,11 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+ },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -4925,4 +4938,4 @@
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 58d58af..e5efdc8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "express-status-monitor",
- "version": "1.2.11",
+ "version": "1.3.0",
"description": "Realtime Monitoring for Express-based Node applications",
"main": "index.js",
"keywords": [
@@ -59,6 +59,7 @@
"dependencies": {
"axios": "0.19.2",
"debug": "4.1.1",
+ "event-loop-stats": "1.2.0",
"handlebars": "^4.7.6",
"on-headers": "1.0.2",
"pidusage": "2.0.18",
diff --git a/src/helpers/default-config.js b/src/helpers/default-config.js
index 98f1bd4..563acf6 100644
--- a/src/helpers/default-config.js
+++ b/src/helpers/default-config.js
@@ -24,10 +24,12 @@ module.exports = {
cpu: true,
mem: true,
load: true,
+ heap: true,
+ eventLoop: true,
responseTime: true,
rps: true,
statusCodes: true,
},
ignoreStartsWith: '/admin',
- healthChecks: []
+ healthChecks: [],
};
diff --git a/src/helpers/gather-os-metrics.js b/src/helpers/gather-os-metrics.js
index f6920f3..f1b8b8b 100644
--- a/src/helpers/gather-os-metrics.js
+++ b/src/helpers/gather-os-metrics.js
@@ -1,5 +1,7 @@
const pidusage = require('pidusage');
const os = require('os');
+const v8 = require('v8');
+const eventLoopStats = require('event-loop-stats');
const sendMetrics = require('./send-metrics');
const debug = require('debug')('express-status-monitor');
@@ -26,9 +28,11 @@ module.exports = (io, span) => {
stat.memory = stat.memory / 1024 / 1024;
stat.load = os.loadavg();
stat.timestamp = Date.now();
+ stat.heap = v8.getHeapStatistics();
+ stat.loop = eventLoopStats.sense();
span.os.push(stat);
- if (!span.responses[0] || last.timestamp + (span.interval * 1000) < Date.now()) {
+ if (!span.responses[0] || last.timestamp + span.interval * 1000 < Date.now()) {
span.responses.push(defaultResponse);
}
diff --git a/src/helpers/socket-io-init.js b/src/helpers/socket-io-init.js
index e060e04..7e67977 100644
--- a/src/helpers/socket-io-init.js
+++ b/src/helpers/socket-io-init.js
@@ -12,7 +12,7 @@ const addSocketEvents = (socket, config) => {
socket.on('esm_change', () => {
socket.emit('esm_start', config.spans);
});
-}
+};
module.exports = (server, config) => {
if (io === null || io === undefined) {
@@ -22,10 +22,11 @@ module.exports = (server, config) => {
io = socketIo(server);
}
- io.on('connection', socket => {
+ io.on('connection', (socket) => {
if (config.authorize) {
- config.authorize(socket)
- .then(authorized => {
+ config
+ .authorize(socket)
+ .then((authorized) => {
if (!authorized) socket.disconnect('unauthorized');
else addSocketEvents(socket, config);
})
@@ -35,7 +36,7 @@ module.exports = (server, config) => {
}
});
- config.spans.forEach(span => {
+ config.spans.forEach((span) => {
span.os = [];
span.responses = [];
const interval = setInterval(() => gatherOsMetrics(io, span), span.interval * 1000);
diff --git a/src/public/index.html b/src/public/index.html
index 5c8ed73..26ed909 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -1,97 +1,114 @@
-
- {{title}}
-
-
-
-
-
-
-
-
-
-
CPU Usage
- - %
-
-
-
-
-
-
-
-
Memory Usage
- - %
-
-
-
-
-
-
-
-
One Minute Load Avg
- -
-
-
-
-
-
-
-
-
Response Time
- -
-
-
-
-
-
-
-
-
Requests per Second
- -
-
-
-
-
-
-
-
-
Status Codes
- 2xx
- 3xx
- 4xx
- 5xx
-
-
-
-
-
-
- {{#each healthCheckResults}}
-
-
-
+
+
{{title}}
+
+
+
+
+
+
+
+
+
+
CPU Usage
+ - %
+
+
+
-
-
{{status}}
+
+
+
+
Memory Usage
+ - %
+
+
+
+
+
+
+
+
Heap Usage
+ - %
+
+
+
+
+
+
+
+
One Minute Load Avg
+ -
+
+
+
+
+
+
+
+
Spent in Event Loop
+ ms
+
+
+
+
+
+
+
+
Response Time
+ -
+
+
+
+
+
+
+
+
Requests per Second
+ -
+
+
+
+
+
+
+
+
Status Codes
+ 2xx
+ 3xx
+ 4xx
+ 5xx
+
+
+
+
+
+
+ {{#each healthCheckResults}}
+
+ {{/each}}
+
+
- {{/each}}
-
-
-
-
-
+
+
diff --git a/src/public/javascripts/app.js b/src/public/javascripts/app.js
index 6baa930..d699a10 100644
--- a/src/public/javascripts/app.js
+++ b/src/public/javascripts/app.js
@@ -13,7 +13,9 @@ Chart.defaults.global.elements.line.backgroundColor = 'rgba(0,0,0,0)';
Chart.defaults.global.elements.line.borderColor = 'rgba(0,0,0,0.9)';
Chart.defaults.global.elements.line.borderWidth = 2;
-var socket = io(location.protocol + '//' + location.hostname + ':' + (port || location.port), { path: socketPath });
+var socket = io(location.protocol + '//' + location.hostname + ':' + (port || location.port), {
+ path: socketPath,
+});
var defaultSpan = 0;
var spans = [];
var statusCodesColors = ['#75D701', '#47b8e0', '#ffc952', '#E53A40'];
@@ -27,20 +29,24 @@ var defaultDataset = {
var defaultOptions = {
scales: {
- yAxes: [{
- ticks: {
- beginAtZero: true,
- },
- }],
- xAxes: [{
- type: 'time',
- time: {
- unitStepSize: 30,
+ yAxes: [
+ {
+ ticks: {
+ beginAtZero: true,
+ },
},
- gridLines: {
- display: false,
+ ],
+ xAxes: [
+ {
+ type: 'time',
+ time: {
+ unitStepSize: 30,
+ },
+ gridLines: {
+ display: false,
+ },
},
- }],
+ ],
},
tooltips: {
enabled: false,
@@ -68,24 +74,32 @@ var addTimestamp = function (point) {
var cpuDataset = [Object.create(defaultDataset)];
var memDataset = [Object.create(defaultDataset)];
var loadDataset = [Object.create(defaultDataset)];
+var heapDataset = [Object.create(defaultDataset)];
+var eventLoopDataset = [Object.create(defaultDataset)];
var responseTimeDataset = [Object.create(defaultDataset)];
var rpsDataset = [Object.create(defaultDataset)];
var cpuStat = document.getElementById('cpuStat');
var memStat = document.getElementById('memStat');
var loadStat = document.getElementById('loadStat');
+var heapStat = document.getElementById('heapStat');
+var eventLoopStat = document.getElementById('eventLoopStat');
var responseTimeStat = document.getElementById('responseTimeStat');
var rpsStat = document.getElementById('rpsStat');
var cpuChartCtx = document.getElementById('cpuChart');
var memChartCtx = document.getElementById('memChart');
var loadChartCtx = document.getElementById('loadChart');
+var heapChartCtx = document.getElementById('heapChart');
+var eventLoopChartCtx = document.getElementById('eventLoopChart');
var responseTimeChartCtx = document.getElementById('responseTimeChart');
var rpsChartCtx = document.getElementById('rpsChart');
var statusCodesChartCtx = document.getElementById('statusCodesChart');
var cpuChart = createChart(cpuChartCtx, cpuDataset);
var memChart = createChart(memChartCtx, memDataset);
+var heapChart = createChart(heapChartCtx, heapDataset);
+var eventLoopChart = createChart(eventLoopChartCtx, eventLoopDataset);
var loadChart = createChart(loadChartCtx, loadDataset);
var responseTimeChart = createChart(responseTimeChartCtx, responseTimeDataset);
var rpsChart = createChart(rpsChartCtx, rpsDataset);
@@ -107,7 +121,16 @@ statusCodesChart.data.datasets.forEach(function (dataset, index) {
dataset.borderColor = statusCodesColors[index];
});
-var charts = [cpuChart, memChart, loadChart, responseTimeChart, rpsChart, statusCodesChart];
+var charts = [
+ cpuChart,
+ memChart,
+ loadChart,
+ responseTimeChart,
+ rpsChart,
+ statusCodesChart,
+ heapChart,
+ eventLoopChart,
+];
var onSpanChange = function (e) {
e.target.classList.add('active');
@@ -160,6 +183,16 @@ socket.on('esm_start', function (data) {
});
loadChart.data.labels = data[defaultSpan].os.map(addTimestamp);
+ heapChart.data.datasets[0].data = data[defaultSpan].os.map(function (point) {
+ return point.heap.used_heap_size / 1024 / 1024;
+ });
+ heapChart.data.labels = data[defaultSpan].os.map(addTimestamp);
+
+ eventLoopChart.data.datasets[0].data = data[defaultSpan].os.map(function (point) {
+ return point.loop.sum;
+ });
+ eventLoopChart.data.labels = data[defaultSpan].os.map(addTimestamp);
+
var lastResponseMetric = data[defaultSpan].responses[data[defaultSpan].responses.length - 1];
responseTimeStat.textContent = '0.00ms';
@@ -180,7 +213,8 @@ socket.on('esm_start', function (data) {
statusCodesChart.data.labels = data[defaultSpan].responses.map(addTimestamp);
if (data[defaultSpan].responses.length >= 2) {
- var deltaTime = lastResponseMetric.timestamp -
+ var deltaTime =
+ lastResponseMetric.timestamp -
data[defaultSpan].responses[data[defaultSpan].responses.length - 2].timestamp;
if (deltaTime < 1) deltaTime = 1000;
@@ -205,7 +239,7 @@ socket.on('esm_start', function (data) {
});
var spanNode = document.createElement('span');
- var textNode = document.createTextNode(((span.retention * span.interval) / 60) + 'M');
+ var textNode = document.createTextNode((span.retention * span.interval) / 60 + 'M');
spanNode.appendChild(textNode);
spanNode.setAttribute('id', index);
@@ -217,8 +251,12 @@ socket.on('esm_start', function (data) {
});
socket.on('esm_stats', function (data) {
- if (data.retention === spans[defaultSpan].retention &&
- data.interval === spans[defaultSpan].interval) {
+ console.log(data);
+
+ if (
+ data.retention === spans[defaultSpan].retention &&
+ data.interval === spans[defaultSpan].interval
+ ) {
var os = data.os;
var responses = data.responses;
@@ -243,6 +281,20 @@ socket.on('esm_stats', function (data) {
loadChart.data.labels.push(os.timestamp);
}
+ heapStat.textContent = '0';
+ if (os) {
+ heapStat.textContent = (os.heap.used_heap_size / 1024 / 1024).toFixed(1) + 'MB';
+ heapChart.data.datasets[0].data.push(os.heap.used_heap_size / 1024 / 1024);
+ heapChart.data.labels.push(os.timestamp);
+ }
+
+ eventLoopStat.textContent = '0';
+ if (os) {
+ eventLoopStat.textContent = os.loop.sum.toFixed(2) + 'ms';
+ eventLoopChart.data.datasets[0].data.push(os.loop.sum);
+ eventLoopChart.data.labels.push(os.timestamp);
+ }
+
responseTimeStat.textContent = '0.00ms';
if (responses) {
responseTimeStat.textContent = responses.mean.toFixed(2) + 'ms';