diff --git a/examples/index.js b/examples/index.js
index 42f9997..1aad381 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -3,10 +3,14 @@
const express = require('express');
const app = express();
+const port = process.env.PORT || 3000;
app.use(require('../index')({ path: '/' }));
app.use(require('express-favicon-short-circuit'));
-app.listen(3000, () => {
- console.log('listening on http://0.0.0.0:3000');
+// Example route throwing requested status code
+app.get('/return-status/:statusCode', (req, res) => res.sendStatus(req.params.statusCode));
+
+app.listen(port, () => {
+ console.log(`Listening on http://0.0.0.0:${port}`);
});
diff --git a/examples/package.json b/examples/package.json
index 4143826..8863bba 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -14,10 +14,12 @@
"license": "MIT",
"dependencies": {
"express": "^4.14.0",
- "express-favicon-short-circuit": "^1.1.0"
+ "express-favicon-short-circuit": "^1.1.0",
+ "request": "^2.74.0"
},
"scripts": {
- "start": "node index.js"
+ "start": "node index.js",
+ "benchmark": "node tester.js"
},
"repository": {
"type": "git",
diff --git a/examples/tester.js b/examples/tester.js
new file mode 100644
index 0000000..1aa0bb3
--- /dev/null
+++ b/examples/tester.js
@@ -0,0 +1,12 @@
+const request = require('request');
+
+const requestUrl = 'http://localhost:3000/return-status/';
+const interval = 50;
+
+const makeDummyCall = () => setTimeout(() => {
+ const code = 200 + Math.random() * 399;
+ request.get(`${requestUrl}${code}`);
+ makeDummyCall();
+}, interval);
+
+makeDummyCall();
diff --git a/package.json b/package.json
index f25be63..6a8cb31 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "express-status-monitor",
- "version": "0.1.2",
+ "version": "0.1.3",
"description": "Realtime Monitoring for Express-based Node applications",
"main": "index.js",
"keywords": [
diff --git a/src/public/index.html b/src/public/index.html
index 783de47..f9cba41 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -60,6 +60,18 @@
-
+
+
+
Status Codes
+ 2xx
+ 3xx
+ 4xx
+ 5xx
+
+
+
+
+
diff --git a/src/public/javascripts/app.js b/src/public/javascripts/app.js
index 7e464f3..5190bdc 100644
--- a/src/public/javascripts/app.js
+++ b/src/public/javascripts/app.js
@@ -10,6 +10,7 @@ Chart.defaults.global.elements.line.borderWidth = 2;
var socket = io(location.protocol + '//' + location.hostname + ':' + location.port);
var defaultSpan = 0;
var spans = [];
+var statusCodesColors = ['#75D701', '#47b8e0', '#ffc952', '#E53A40'];
var defaultDataset = {
label: '',
@@ -48,7 +49,7 @@ var createChart = function (ctx, dataset) {
type: 'line',
data: {
labels: [],
- datasets: dataset
+ datasets: dataset,
},
options: defaultOptions
});
@@ -63,6 +64,7 @@ var memDataset = [Object.create(defaultDataset)];
var loadDataset = [Object.create(defaultDataset)];
var responseTimeDataset = [Object.create(defaultDataset)];
var rpsDataset = [Object.create(defaultDataset)];
+var statusCodesDataset = [Object.create(defaultDataset)];
var cpuStat = document.getElementById('cpuStat');
var memStat = document.getElementById('memStat');
@@ -75,14 +77,32 @@ var memChartCtx = document.getElementById("memChart");
var loadChartCtx = document.getElementById("loadChart");
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 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];
+var charts = [cpuChart, memChart, loadChart, responseTimeChart, rpsChart, statusCodesChart];
var onSpanChange = function (e) {
e.target.classList.add('active');
@@ -146,8 +166,16 @@ socket.on('start', function (data) {
});
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;
@@ -181,6 +209,7 @@ socket.on('start', function (data) {
socket.on('stats', function (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) {
@@ -203,8 +232,6 @@ socket.on('stats', function (data) {
loadChart.data.labels.push(os.timestamp);
}
- var responses = data.responses;
-
responseTimeStat.textContent = '0.00ms';
if (responses) {
responseTimeStat.textContent = responses.mean.toFixed(2) + 'ms';
@@ -214,17 +241,27 @@ socket.on('stats', function (data) {
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[0].data.shift();
+ chart.data.datasets.forEach(function(dataset) {
+ dataset.data.shift();
+ });
+
chart.data.labels.shift();
}
-
chart.update();
});
}
diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css
index 8c80bb3..b3e4eef 100644
--- a/src/public/stylesheets/style.css
+++ b/src/public/stylesheets/style.css
@@ -1,70 +1,103 @@
* {
- font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
+ font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}
h1 {
- font-size: 3em;
- color: #222222;
- margin: 0;
+ font-size: 3em;
+ color: #222;
+ margin: 0;
}
h5 {
- margin: 0;
- color: #888888;
+ margin: 0;
+ color: #888;
+}
+
+h6 {
+ margin: 0;
}
p {
- font-size: 0.7em;
- color: #888888;
+ font-size: 0.7em;
+ color: #888;
}
span {
- cursor: pointer;
- font-size: 10px;
- margin-left: 5px;
- border: 1px solid #DDD;
- padding: 3px 10px 4px 10px;
+ cursor: pointer;
+ font-size: 10px;
+ margin-left: 5px;
+ border: 1px solid #DDD;
+ padding: 3px 10px 4px 10px;
}
canvas {
- width: 400px;
- height: 100px;
+ width: 400px;
+ height: 100px;
}
.content {
- width: 600px;
- margin: auto;
+ width: 600px;
+ margin: auto;
}
.active {
- background: #eeeeee;
+ background: #eeeeee;
}
.stats-column {
- flex: 0 0 200px;
+ flex: 0 0 200px;
}
.container {
- display: flex;
- flex-direction: row;
- margin-top: 20px;
- height: 100px;
+ display: flex;
+ flex-direction: row;
+ margin-top: 20px;
+ height: 100px;
}
.chart-container {
- width: 400px;
- height: 100px;
+ width: 400px;
+ height: 100px;
}
.footer {
- position: fixed;
- margin: auto;
- text-align: center;
- left: 0;
- right: 0;
- bottom: 0;
+ position: fixed;
+ margin: auto;
+ text-align: center;
+ left: 0;
+ right: 0;
+ bottom: 0;
}
.span-controls {
- float: right;
+ float: right;
+}
+
+.status-code {
+ margin-top: 2px;
+}
+
+.status-code:before {
+ content: '';
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 8px;
+ margin-right: 10px;
+}
+
+.status-code-2xx:before {
+ background-color: #75D701;
+}
+
+.status-code-3xx:before {
+ background-color: #47b8e0;
+}
+
+.status-code-4xx:before {
+ background-color: #ffc952;
+}
+
+.status-code-5xx:before {
+ background-color: #E53A40;
}