a541985e by Rafal Wilinski

:mailbox: Add support for Response Times

1 parent 5837765c
...@@ -50,33 +50,40 @@ ...@@ -50,33 +50,40 @@
50 <div class="container"> 50 <div class="container">
51 <div class="stats-column"> 51 <div class="stats-column">
52 <h5>CPU Usage</h5> 52 <h5>CPU Usage</h5>
53 <h1 id="cpuStat">0.03%</h1> 53 <h1 id="cpuStat">- %</h1>
54 </div> 54 </div>
55 <canvas id="cpuChart" width="400" height="100"></canvas> 55 <canvas id="cpuChart" width="400" height="100"></canvas>
56 </div> 56 </div>
57 <div class="container"> 57 <div class="container">
58 <div class="stats-column"> 58 <div class="stats-column">
59 <h5>Memory Usage</h5> 59 <h5>Memory Usage</h5>
60 <h1 id="memStat">0.03%</h1> 60 <h1 id="memStat">- %</h1>
61 </div> 61 </div>
62 <canvas id="memChart" width="400" height="100"></canvas> 62 <canvas id="memChart" width="400" height="100"></canvas>
63 </div> 63 </div>
64 <div class="container"> 64 <div class="container">
65 <div class="stats-column"> 65 <div class="stats-column">
66 <h5>Requests per second</h5> 66 <h5>Requests per second</h5>
67 <h1 id="rpsStat">1.45</h1> 67 <h1 id="rpsStat">-</h1>
68 </div> 68 </div>
69 <canvas id="rpsChart" width="400" height="100"></canvas> 69 <canvas id="rpsChart" width="400" height="100"></canvas>
70 </div> 70 </div>
71 <div class="Status Codes"> 71 <div class="container">
72 <div class="stats-column">
73 <h5>Response Time Mean</h5>
74 <h1 id="responseTimeStat">- ms</h1>
75 </div>
76 <canvas id="responseTimeChart" width="400" height="100"></canvas>
77 </div>
78 <div class="container">
72 <div class="stats-column"> 79 <div class="stats-column">
73 <h5>Response status codes</h5> 80 <h5>Response status codes</h5>
74 <h1 id="codeStat">OK</h1> 81 <h1 id="statusCodeStat">-</h1>
75 </div> 82 </div>
76 <canvas id="codeChart" width="400" height="100"></canvas> 83 <canvas id="statusCodeChart" width="400" height="100"></canvas>
77 </div> 84 </div>
78 <div class="footer"> 85 <div class="footer">
79 <p>Powered by Socket.io</p> 86 <p>Powered by Socket.io & Chart.js</p>
80 </div> 87 </div>
81 </div> 88 </div>
82 <script> 89 <script>
...@@ -95,12 +102,35 @@ ...@@ -95,12 +102,35 @@
95 lineTension: 0.3, 102 lineTension: 0.3,
96 pointRadius: 0, 103 pointRadius: 0,
97 }]; 104 }];
105
98 var memDataset = [{ 106 var memDataset = [{
99 label: 'Memory Usage in MB', 107 label: 'Memory Usage in MB',
100 data: [], 108 data: [],
101 lineTension: 0.6, 109 lineTension: 0.6,
102 pointRadius: 0, 110 pointRadius: 0,
103 }]; 111 }];
112
113 var responseTimeDataset = [{
114 label: 'Mean Response time',
115 data: [],
116 lineTension: 0.6,
117 pointRadius: 0,
118 }];
119
120 var rpsDataset = [{
121 label: 'Requests per second',
122 data: [],
123 lineTension: 0.6,
124 pointRadius: 0,
125 }];
126
127 var statusCodesDataset = [{
128 label: 'Memory Usage in MB',
129 data: [],
130 lineTension: 0.6,
131 pointRadius: 0,
132 }];
133
104 var defaultOptions = { 134 var defaultOptions = {
105 scales: { 135 scales: {
106 yAxes: [{ 136 yAxes: [{
...@@ -124,8 +154,14 @@ ...@@ -124,8 +154,14 @@
124 }; 154 };
125 155
126 var cpuStat = document.getElementById('cpuStat'); 156 var cpuStat = document.getElementById('cpuStat');
157 var memStat = document.getElementById('memStat');
158 var responseTimeStat = document.getElementById('responseTimeStat');
159 var rpsStat = document.getElementById('rpsStat');
127 var cpuChartCtx = document.getElementById("cpuChart"); 160 var cpuChartCtx = document.getElementById("cpuChart");
128 var memChartCtx = document.getElementById("memChart"); 161 var memChartCtx = document.getElementById("memChart");
162 var rpsChartCtx = document.getElementById("rpsChart");
163 var responseTimeChartCtx = document.getElementById("responseTimeChart");
164 var statusCodeChartCtx = document.getElementById("statusCodeChart");
129 var cpuChart = new Chart(cpuChartCtx, { 165 var cpuChart = new Chart(cpuChartCtx, {
130 type: 'line', 166 type: 'line',
131 data: { 167 data: {
...@@ -144,17 +180,55 @@ ...@@ -144,17 +180,55 @@
144 options: defaultOptions 180 options: defaultOptions
145 }); 181 });
146 182
183 var rpsChart = new Chart(rpsChartCtx, {
184 type: 'line',
185 data: {
186 labels: labels,
187 datasets: responseTimeDataset,
188 },
189 options: defaultOptions
190 });
191
192 var responseTimeChart = new Chart(responseTimeChartCtx, {
193 type: 'line',
194 data: {
195 labels: labels,
196 datasets: responseTimeDataset,
197 },
198 options: defaultOptions
199 });
200
201 var statusCodeChart = new Chart(statusCodeChartCtx, {
202 type: 'line',
203 data: {
204 labels: labels,
205 datasets: statusCodesDataset,
206 },
207 options: defaultOptions
208 });
209
147 socket.on('stats', function (data) { 210 socket.on('stats', function (data) {
148 console.log(data); 211 console.log(data);
149 cpuStat.textContent = data.osStats[data.osStats.length - 1].cpu.toFixed(1) + '%'; 212 cpuStat.textContent = data.osStats[data.osStats.length - 1].cpu.toFixed(1) + '%';
150 cpuChart.data.datasets[0].data = data.osStats.map((point) => point.cpu); 213 cpuChart.data.datasets[0].data = data.osStats.map(function(point) { return point.cpu; });
151 cpuChart.data.labels = data.osStats.map((point) => point.timestamp); 214 cpuChart.data.labels = data.osStats.map(function(point) { return point.timestamp; });
152 cpuChart.update(); 215 cpuChart.update();
153 216
154 memStat.textContent = data.osStats[data.osStats.length - 1].memory.toFixed(1) + 'MB'; 217 memStat.textContent = data.osStats[data.osStats.length - 1].memory.toFixed(1) + 'MB';
155 memChart.data.datasets[0].data = data.osStats.map((point) => point.memory); 218 memChart.data.datasets[0].data = data.osStats.map(function(point) { return point.memory; });
156 memChart.data.labels = data.osStats.map((point) => point.timestamp); 219 memChart.data.labels = data.osStats.map(function(point) { return point.timestamp; });
157 memChart.update(); 220 memChart.update();
221
222 if (data.responses.length > 0) {
223 responseTimeStat.textContent = data.responses[data.responses.length - 1].mean.toFixed(1) + 'ms';
224 responseTimeChart.data.datasets[0].data = data.responses.map(function (point) {
225 return point.mean;
226 });
227 responseTimeChart.data.labels = data.responses.map(function (point) {
228 return point.timestamp;
229 });
230 responseTimeChart.update();
231 }
158 }); 232 });
159 </script> 233 </script>
160 </body> 234 </body>
......
...@@ -4,33 +4,35 @@ ...@@ -4,33 +4,35 @@
4 const path = require('path'); 4 const path = require('path');
5 const onHeaders = require('on-headers'); 5 const onHeaders = require('on-headers');
6 const pidusage = require('pidusage'); 6 const pidusage = require('pidusage');
7 const responseTimes = [];
8 const osStats = [];
9 7
10 const defaultConfig = { 8 const defaultConfig = {
11 socketPort: 41338, 9 socketPort: 41338,
12 path: '/status', 10 path: '/status',
11 spans: [{
13 interval: 1, 12 interval: 1,
14 retention: 100, 13 retention: 60
14 }]
15 }; 15 };
16 16
17 const gatherOsMetrics = (io, config) => { 17 const gatherOsMetrics = (io, span) => {
18 pidusage.stat(process.pid, (err, stat) => { 18 pidusage.stat(process.pid, (err, stat) => {
19 stat.timestamp = Date.now(); 19 stat.timestamp = Date.now();
20
20 // Convert from B to MB 21 // Convert from B to MB
21 stat.memory = stat.memory / 1024 / 1024; 22 stat.memory = stat.memory / 1024 / 1024;
22 23
23 osStats.push(stat); 24 span.osStats.push(stat);
24 if (osStats.length >= config.retention) osStats.shift(); 25 if (span.osStats.length >= span.retention) span.osStats.shift();
26 if (span.responses[0] && span.responses[0].timestamp + (span.interval * span.retention * 1000) < Date.now()) span.responses.shift();
25 27
26 sendMetrics(io); 28 sendMetrics(io, span);
27 }); 29 });
28 }; 30 };
29 31
30 const sendMetrics = (io) => { 32 const sendMetrics = (io, span) => {
31 io.emit('stats', { 33 io.emit('stats', {
32 osStats, 34 osStats: span.osStats,
33 responseTimes 35 responses: span.responses,
34 }); 36 });
35 }; 37 };
36 38
...@@ -47,13 +49,21 @@ ...@@ -47,13 +49,21 @@
47 config.socketPort = defaultConfig.socketPort; 49 config.socketPort = defaultConfig.socketPort;
48 } 50 }
49 51
50 if (config.interval === undefined || !config instanceof Number) { 52 if (config.spans === undefined || !config instanceof Array) {
51 config.interval = defaultConfig.interval; 53 config.spans = defaultConfig.span;
52 } 54 }
53 55
54 const io = require('socket.io')(config.socketPort); 56 const io = require('socket.io')(config.socketPort);
55 57
56 setInterval(() => gatherOsMetrics(io, config), config.interval * 1000); 58 io.on('connection', (socket) => {
59 console.log('User connected! ' + socket);
60 });
61
62 config.spans.forEach((span) => {
63 span.osStats = [];
64 span.responses = [];
65 setInterval(() => gatherOsMetrics(io, span), span.interval * 1000);
66 });
57 67
58 return (req, res, next) => { 68 return (req, res, next) => {
59 const startTime = process.hrtime(); 69 const startTime = process.hrtime();
...@@ -61,14 +71,27 @@ ...@@ -61,14 +71,27 @@
61 res.sendFile(path.join(__dirname + '/index.html')); 71 res.sendFile(path.join(__dirname + '/index.html'));
62 } else { 72 } else {
63 onHeaders(res, () => { 73 onHeaders(res, () => {
64 var diff = process.hrtime(startTime); 74 const diff = process.hrtime(startTime);
65 var responseTime = diff[0] * 1e3 + diff[1] * 1e-6; 75 const responseTime = diff[0] * 1e3 + diff[1] * 1e-6;
66 76 const category = Math.floor(res.statusCode / 100);
67 responseTimes.push({ 77
68 endpoint: req.path, 78 config.spans.forEach((span) => {
69 responseTime, 79 if (span.responses[span.responses.length - 1] !== undefined &&
80 span.responses[span.responses.length - 1].timestamp / 1000 + span.interval > Date.now() / 1000) {
81 span.responses[span.responses.length - 1][category]++;
82 span.responses[span.responses.length - 1].count++;
83 } else {
84 span.responses.push({
85 '2': category === 2 ? 1 : 0,
86 '3': category === 3 ? 1 : 0,
87 '4': category === 4 ? 1 : 0,
88 '5': category === 5 ? 1 : 0,
89 count: 1,
90 mean: responseTime,
70 timestamp: Date.now() 91 timestamp: Date.now()
71 }); 92 });
93 }
94 });
72 }); 95 });
73 96
74 next(); 97 next();
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!