express-status-monitor
Version:
Realtime Monitoring for Express-based Node applications
336 lines (284 loc) • 10.3 kB
JavaScript
/*
eslint-disable no-plusplus, no-var, strict, vars-on-top, prefer-template,
func-names, prefer-arrow-callback, no-loop-func
*/
/* global Chart, location, document, port, socketPath, parseInt, io */
;
Chart.defaults.global.defaultFontSize = 8;
Chart.defaults.global.animation.duration = 500;
Chart.defaults.global.legend.display = false;
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 defaultSpan = 0;
var spans = [];
var statusCodesColors = ['#75D701', '#47b8e0', '#ffc952', '#E53A40'];
var defaultDataset = {
label: '',
data: [],
lineTension: 0.2,
pointRadius: 0,
};
var defaultOptions = {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
xAxes: [
{
type: 'time',
time: {
unitStepSize: 30,
},
gridLines: {
display: false,
},
},
],
},
tooltips: {
enabled: false,
},
responsive: true,
maintainAspectRatio: false,
animation: false,
};
var createChart = function (ctx, dataset) {
return new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: dataset,
},
options: defaultOptions,
});
};
var addTimestamp = function (point) {
return point.timestamp;
};
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);
var statusCodesChart = new Chart(statusCodesChartCtx, {
type: 'line',
data: {
labels: [],
datasets: [
Object.create(defaultDataset),
Object.create(defaultDataset),
Object.create(defaultDataset),
Object.create(defaultDataset),
],
},
options: defaultOptions,
});
statusCodesChart.data.datasets.forEach(function (dataset, index) {
dataset.borderColor = statusCodesColors[index];
});
var charts = [
cpuChart,
memChart,
loadChart,
responseTimeChart,
rpsChart,
statusCodesChart,
heapChart,
eventLoopChart,
];
var onSpanChange = function (e) {
e.target.classList.add('active');
defaultSpan = parseInt(e.target.id, 10);
var otherSpans = document.getElementsByTagName('span');
for (var i = 0; i < otherSpans.length; i++) {
if (otherSpans[i] !== e.target) otherSpans[i].classList.remove('active');
}
socket.emit('esm_change');
};
socket.on('esm_start', function (data) {
// Remove last element of Array because it contains malformed responses data.
// To keep consistency we also remove os data.
data[defaultSpan].responses.pop();
data[defaultSpan].os.pop();
var lastOsMetric = data[defaultSpan].os[data[defaultSpan].os.length - 1];
cpuStat.textContent = '0.0%';
if (lastOsMetric) {
cpuStat.textContent = lastOsMetric.cpu.toFixed(1) + '%';
}
cpuChart.data.datasets[0].data = data[defaultSpan].os.map(function (point) {
return point.cpu;
});
cpuChart.data.labels = data[defaultSpan].os.map(addTimestamp);
memStat.textContent = '0.0MB';
if (lastOsMetric) {
memStat.textContent = lastOsMetric.memory.toFixed(1) + 'MB';
}
memChart.data.datasets[0].data = data[defaultSpan].os.map(function (point) {
return point.memory;
});
memChart.data.labels = data[defaultSpan].os.map(addTimestamp);
loadStat.textContent = '0.00';
if (lastOsMetric) {
loadStat.textContent = lastOsMetric.load[defaultSpan].toFixed(2);
}
loadChart.data.datasets[0].data = data[defaultSpan].os.map(function (point) {
return point.load[0];
});
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) {
if (point.loop) {
return point.loop.sum;
}
return 0;
});
eventLoopChart.data.labels = data[defaultSpan].os.map(addTimestamp);
var lastResponseMetric = data[defaultSpan].responses[data[defaultSpan].responses.length - 1];
responseTimeStat.textContent = '0.00ms';
if (lastResponseMetric) {
responseTimeStat.textContent = lastResponseMetric.mean.toFixed(2) + 'ms';
}
responseTimeChart.data.datasets[0].data = data[defaultSpan].responses.map(function (point) {
return point.mean;
});
responseTimeChart.data.labels = data[defaultSpan].responses.map(addTimestamp);
for (var i = 0; i < 4; i++) {
statusCodesChart.data.datasets[i].data = data[defaultSpan].responses.map(function (point) {
return point[i + 2];
});
}
statusCodesChart.data.labels = data[defaultSpan].responses.map(addTimestamp);
if (data[defaultSpan].responses.length >= 2) {
var deltaTime =
lastResponseMetric.timestamp -
data[defaultSpan].responses[data[defaultSpan].responses.length - 2].timestamp;
if (deltaTime < 1) deltaTime = 1000;
rpsStat.textContent = ((lastResponseMetric.count / deltaTime) * 1000).toFixed(2);
rpsChart.data.datasets[0].data = data[defaultSpan].responses.map(function (point) {
return (point.count / deltaTime) * 1000;
});
rpsChart.data.labels = data[defaultSpan].responses.map(addTimestamp);
}
charts.forEach(function (chart) {
chart.update();
});
var spanControls = document.getElementById('span-controls');
if (data.length !== spans.length) {
data.forEach(function (span, index) {
spans.push({
retention: span.retention,
interval: span.interval,
});
var spanNode = document.createElement('span');
var textNode = document.createTextNode((span.retention * span.interval) / 60 + 'M'); // eslint-disable-line
spanNode.appendChild(textNode);
spanNode.setAttribute('id', index);
spanNode.onclick = onSpanChange;
spanControls.appendChild(spanNode);
});
document.getElementsByTagName('span')[0].classList.add('active');
}
});
socket.on('esm_stats', function (data) {
console.log(data);
if (
data.retention === spans[defaultSpan].retention &&
data.interval === spans[defaultSpan].interval
) {
var os = data.os;
var responses = data.responses;
cpuStat.textContent = '0.0%';
if (os) {
cpuStat.textContent = os.cpu.toFixed(1) + '%';
cpuChart.data.datasets[0].data.push(os.cpu);
cpuChart.data.labels.push(os.timestamp);
}
memStat.textContent = '0.0MB';
if (os) {
memStat.textContent = os.memory.toFixed(1) + 'MB';
memChart.data.datasets[0].data.push(os.memory);
memChart.data.labels.push(os.timestamp);
}
loadStat.textContent = '0';
if (os) {
loadStat.textContent = os.load[0].toFixed(2);
loadChart.data.datasets[0].data.push(os.load[0]);
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 && os.loop) {
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';
responseTimeChart.data.datasets[0].data.push(responses.mean);
responseTimeChart.data.labels.push(responses.timestamp);
}
if (responses) {
var deltaTime = responses.timestamp - rpsChart.data.labels[rpsChart.data.labels.length - 1];
if (deltaTime < 1) deltaTime = 1000;
rpsStat.textContent = ((responses.count / deltaTime) * 1000).toFixed(2);
rpsChart.data.datasets[0].data.push((responses.count / deltaTime) * 1000);
rpsChart.data.labels.push(responses.timestamp);
}
if (responses) {
for (var i = 0; i < 4; i++) {
statusCodesChart.data.datasets[i].data.push(data.responses[i + 2]);
}
statusCodesChart.data.labels.push(data.responses.timestamp);
}
charts.forEach(function (chart) {
if (spans[defaultSpan].retention < chart.data.labels.length) {
chart.data.datasets.forEach(function (dataset) {
dataset.data.shift();
});
chart.data.labels.shift();
}
chart.update();
});
}
});