downgraded to express@^3.23 and socket.io@^0.9.16
Showing
10 changed files
with
299 additions
and
279 deletions
1 | ### Issues with HTTPS (including Heroku) has been resolved, module is working again | 1 | ### Issues with HTTPS (including Heroku) has been resolved, module is working again |
2 | # express-status-monitor | 2 | # express-status-monitor |
3 | Simple, self-hosted module based on Socket.io and Chart.js to report realtime server metrics for Express-based node servers. More Node frameworks coming soon. | 3 | Simple, self-hosted module based on Socket.io and Chart.js to report realtime server metrics for Express-based node servers. More Node frameworks coming soon. |
4 | 4 | ||
5 |  | 5 |  |
6 | 6 | ||
7 | ## Installation & setup | 7 | ## Installation & setup |
8 | 1. Run `npm install express-status-monitor --save` | 8 | 1. Run `npm install express-status-monitor --save` |
9 | 2. Before any other middleware or router add following line: | 9 | 2. Before any other middleware or router add following line: |
10 | `app.use(require('express-status-monitor')());` | 10 | `app.use(require('express-status-monitor')());` |
11 | 3. Run server and go to `/status` | 11 | 3. Run server and go to `/status` |
12 | 12 | ||
13 | ## Run examples | 13 | ## Run examples |
14 | 14 | ||
15 | 1. Go to `examples/` | 15 | 1. Go to `examples/` |
16 | 2. Run `npm install` | 16 | 2. Run `npm install` |
17 | 3. Run server `node index.js` | 17 | 3. Run server `node index.js` |
18 | 4. Go to `http://0.0.0.0:3000` | 18 | 4. Go to `http://0.0.0.0:3000` |
19 | 19 | ||
20 | ## Options | 20 | ## Options |
21 | 21 | ||
22 | Monitor can be configured by passing options object into `expressMonitor` constructor. | 22 | Monitor can be configured by passing options object into `expressMonitor` constructor. |
23 | 23 | ||
24 | Default config: | 24 | Default config: |
25 | ``` | 25 | ``` |
26 | title: 'Express Status', // Default title | 26 | title: 'Express Status', // Default title |
27 | path: '/status', | 27 | path: '/status', |
28 | spans: [{ | 28 | spans: [{ |
29 | interval: 1, // Every second | 29 | interval: 1, // Every second |
30 | retention: 60 // Keep 60 datapoints in memory | 30 | retention: 60 // Keep 60 datapoints in memory |
31 | }, { | 31 | }, { |
32 | interval: 5, // Every 5 seconds | 32 | interval: 5, // Every 5 seconds |
33 | retention: 60 | 33 | retention: 60 |
34 | }, { | 34 | }, { |
35 | interval: 15, // Every 15 seconds | 35 | interval: 15, // Every 15 seconds |
36 | retention: 60 | 36 | retention: 60 |
37 | }] | 37 | }] |
38 | 38 | ||
39 | ``` | 39 | ``` |
40 | 40 | ||
41 | ## License | 41 | ## License |
42 | 42 | ||
43 | [MIT License](https://opensource.org/licenses/MIT) © Rafal Wilinski | 43 | [MIT License](https://opensource.org/licenses/MIT) © Rafal Wilinski | ... | ... |
1 | const express = require('express'); | 1 | const express = require('express'); |
2 | const app = express(); | 2 | const app = express(); |
3 | 3 | const http = require('http'); | |
4 | const config = { | 4 | const io = require('socket.io'); |
5 | path: '/', | 5 | const config = { |
6 | title: 'Express Status', | 6 | uri: 'www.cert.coder.fi', |
7 | spans: [{ | 7 | port: 4000, |
8 | interval: 1, | 8 | path: '/', |
9 | retention: 60 | 9 | title: 'Express Status', |
10 | }, { | 10 | spans: [{ |
11 | interval: 5, | 11 | interval: 1, |
12 | retention: 60 | 12 | retention: 60 |
13 | }, { | 13 | }, { |
14 | interval: 15, | 14 | interval: 5, |
15 | retention: 60 | 15 | retention: 60 |
16 | }] | 16 | }, { |
17 | } | 17 | interval: 15, |
18 | 18 | retention: 60 | |
19 | app.use(require('../index')(config)); | 19 | }] |
20 | 20 | } | |
21 | app.listen(3000, () => { | 21 | |
22 | console.log('🌏 http://0.0.0.0:3000'); | 22 | var server = http.createServer(app); |
23 | }); | 23 | app.use(require('../index')(config, server)); |
24 | |||
25 | server.listen(config.port, config.uri, () => { | ||
26 | console.log('🌏 http://'+config.uri+':'+config.port); | ||
27 | }); | ... | ... |
1 | { | 1 | { |
2 | "name": "express-status-monitor-example", | 2 | "name": "express-status-monitor-example", |
3 | "version": "0.0.1", | 3 | "version": "0.0.1", |
4 | "description": "Examples", | 4 | "description": "Examples", |
5 | "main": "index.js", | 5 | "main": "index.js", |
6 | "author": "Rafal Wilinski raf.wilinski@gmail.com", | 6 | "author": "Rafal Wilinski raf.wilinski@gmail.com", |
7 | "contributors": [ | 7 | "contributors": [ |
8 | { | 8 | { |
9 | "name": "Julien Breux", | 9 | "name": "Julien Breux", |
10 | "email": "julien.breux@gmail.com", | 10 | "email": "julien.breux@gmail.com", |
11 | "url": "https://github.com/JulienBreux/" | 11 | "url": "https://github.com/JulienBreux/" |
12 | } | 12 | }, |
13 | ], | 13 | { |
14 | "license": "MIT", | 14 | "name": "Jabis Sevon", |
15 | "dependencies": { | 15 | "email": "jabis.is@gmail.com", |
16 | "express": "^4.14.0", | 16 | "url": "https://jscodex.com" |
17 | "on-headers": "^1.0.1", | 17 | } |
18 | "pidusage": "^1.0.4", | 18 | ], |
19 | "socket.io": "^1.4.8" | 19 | "license": "MIT", |
20 | } | 20 | "dependencies": { |
21 | } | 21 | "express": "^3.21.2", |
22 | "on-headers": "^1.0.1", | ||
23 | "pidusage": "^1.0.4", | ||
24 | "socket.io": "^0.9.16" | ||
25 | } | ||
26 | } | ... | ... |
examples/socket.io.min.js
0 → 100644
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
1 | (function () { | 1 | (function() { |
2 | 'use strict'; | 2 | 'use strict'; |
3 | 3 | ||
4 | const fs = require('fs'); | 4 | const fs = require('fs'); |
5 | const path = require('path'); | 5 | const path = require('path'); |
6 | const os = require('os'); | 6 | const os = require('os'); |
7 | const onHeaders = require('on-headers'); | 7 | const onHeaders = require('on-headers'); |
8 | const pidusage = require('pidusage'); | 8 | const pidusage = require('pidusage'); |
9 | let io; | 9 | let io; |
10 | 10 | ||
11 | const defaultConfig = { | 11 | const defaultConfig = { |
12 | title: 'Express Status', | 12 | uri: 'www.cert.coder.fi', |
13 | path: '/status', | 13 | port: 4000, |
14 | spans: [{ | 14 | title: 'Express Status', |
15 | interval: 1, | 15 | path: '/status', |
16 | retention: 60 | 16 | spans: [{ |
17 | }, { | 17 | interval: 1, |
18 | interval: 5, | 18 | retention: 60 |
19 | retention: 60 | 19 | }, { |
20 | }, { | 20 | interval: 5, |
21 | interval: 15, | 21 | retention: 60 |
22 | retention: 60 | 22 | }, { |
23 | }] | 23 | interval: 15, |
24 | }; | 24 | retention: 60 |
25 | 25 | }] | |
26 | const gatherOsMetrics = (io, span) => { | 26 | }; |
27 | const defaultResponse = { | 27 | |
28 | '2': 0, | 28 | const gatherOsMetrics = (io, span) => { |
29 | '3': 0, | 29 | const defaultResponse = { |
30 | '4': 0, | 30 | '2': 0, |
31 | '5': 0, | 31 | '3': 0, |
32 | count: 0, | 32 | '4': 0, |
33 | mean: 0, | 33 | '5': 0, |
34 | timestamp: Date.now() | 34 | count: 0, |
35 | }; | 35 | mean: 0, |
36 | 36 | timestamp: Date.now() | |
37 | pidusage.stat(process.pid, (err, stat) => { | 37 | }; |
38 | const last = span.responses[span.responses.length - 1]; | 38 | |
39 | // Convert from B to MB | 39 | pidusage.stat(process.pid, (err, stat) => { |
40 | stat.memory = stat.memory / 1024 / 1024; | 40 | const last = span.responses[span.responses.length - 1]; |
41 | stat.load = os.loadavg(); | 41 | // Convert from B to MB |
42 | stat.timestamp = Date.now(); | 42 | stat.memory = stat.memory / 1024 / 1024; |
43 | 43 | stat.load = os.loadavg(); | |
44 | span.os.push(stat); | 44 | stat.timestamp = Date.now(); |
45 | if (!span.responses[0] || last.timestamp + (span.interval * 1000) < Date.now()) span.responses.push(defaultResponse); | 45 | |
46 | 46 | span.os.push(stat); | |
47 | if (span.os.length >= span.retention) span.os.shift(); | 47 | if (!span.responses[0] || last.timestamp + (span.interval * 1000) < Date.now()) span.responses.push(defaultResponse); |
48 | if (span.responses[0] && span.responses.length > span.retention) span.responses.shift(); | 48 | |
49 | 49 | if (span.os.length >= span.retention) span.os.shift(); | |
50 | sendMetrics(io, span); | 50 | if (span.responses[0] && span.responses.length > span.retention) span.responses.shift(); |
51 | }); | 51 | |
52 | }; | 52 | sendMetrics(io, span); |
53 | 53 | }); | |
54 | const sendMetrics = (io, span) => { | 54 | }; |
55 | io.emit('stats', { | 55 | |
56 | os: span.os[span.os.length - 2], | 56 | const sendMetrics = (io, span) => { |
57 | responses: span.responses[span.responses.length - 2], | 57 | io.emit('stats', { |
58 | interval: span.interval, | 58 | os: span.os[span.os.length - 2], |
59 | retention: span.retention | 59 | responses: span.responses[span.responses.length - 2], |
60 | }); | 60 | interval: span.interval, |
61 | }; | 61 | retention: span.retention |
62 | 62 | }); | |
63 | const middlewareWrapper = (config) => { | 63 | }; |
64 | if (config === null || config === undefined) { | 64 | |
65 | config = defaultConfig; | 65 | const middlewareWrapper = (config, server) => { |
66 | } | 66 | if (config === null || config === undefined) { |
67 | 67 | config = defaultConfig; | |
68 | if (config.path === undefined || !config instanceof String) { | 68 | } |
69 | config.path = defaultConfig.path; | 69 | |
70 | } | 70 | if (config.path === undefined || !config instanceof String) { |
71 | 71 | config.path = defaultConfig.path; | |
72 | if (config.spans === undefined || !config instanceof Array) { | 72 | } |
73 | config.spans = defaultConfig.spans; | 73 | |
74 | } | 74 | if (config.spans === undefined || !config instanceof Array) { |
75 | 75 | config.spans = defaultConfig.spans; | |
76 | if (config.title === undefined || !config instanceof String) { | 76 | } |
77 | config.title = 'Express Status'; | 77 | |
78 | } | 78 | if (config.title === undefined || !config instanceof String) { |
79 | 79 | config.title = 'Express Status'; | |
80 | let renderedHtml; | 80 | } |
81 | fs.readFile(path.join(__dirname, '/index.html'), function(err, html){ | 81 | |
82 | renderedHtml = html.toString().replace(/{{title}}/g, config.title); | 82 | let renderedHtml; |
83 | }); | 83 | fs.readFile(path.join(__dirname, '/index.html'), function(err, html) { |
84 | 84 | renderedHtml = html.toString().replace(/{{title}}/g, config.title); | |
85 | return (req, res, next) => { | 85 | }); |
86 | if (io === null || io === undefined) { | 86 | |
87 | 87 | return (req, res, next) => { | |
88 | io = require('socket.io')(req.socket.server); | 88 | if (io === null || io === undefined) { |
89 | 89 | //console.log(req) | |
90 | io.on('connection', (socket) => { | 90 | io = require('socket.io').listen(server); |
91 | socket.emit('start', config.spans); | 91 | |
92 | socket.on('change', function() { socket.emit('start', config.spans); }); | 92 | io.on('connection', (socket) => { |
93 | }); | 93 | socket.emit('start', config.spans); |
94 | 94 | socket.on('change', function() { | |
95 | config.spans.forEach((span) => { | 95 | socket.emit('start', config.spans); |
96 | span.os = []; | 96 | }); |
97 | span.responses = []; | 97 | }); |
98 | setInterval(() => gatherOsMetrics(io, span), span.interval * 1000); | 98 | |
99 | }); | 99 | config.spans.forEach((span) => { |
100 | } | 100 | span.os = []; |
101 | 101 | span.responses = []; | |
102 | const startTime = process.hrtime(); | 102 | setInterval(() => gatherOsMetrics(io, span), span.interval * 1000); |
103 | if (req.path === config.path) { | 103 | }); |
104 | res.send(renderedHtml); | 104 | } |
105 | } else { | 105 | |
106 | onHeaders(res, () => { | 106 | const startTime = process.hrtime(); |
107 | const diff = process.hrtime(startTime); | 107 | if (req.path === config.path) { |
108 | const responseTime = diff[0] * 1e3 + diff[1] * 1e-6; | 108 | res.send(renderedHtml); |
109 | const category = Math.floor(res.statusCode / 100); | 109 | } else { |
110 | 110 | onHeaders(res, () => { | |
111 | config.spans.forEach((span) => { | 111 | const diff = process.hrtime(startTime); |
112 | const last = span.responses[span.responses.length - 1]; | 112 | const responseTime = diff[0] * 1e3 + diff[1] * 1e-6; |
113 | if (last !== undefined && | 113 | const category = Math.floor(res.statusCode / 100); |
114 | last.timestamp / 1000 + span.interval > Date.now() / 1000) { | 114 | |
115 | last[category]++; | 115 | config.spans.forEach((span) => { |
116 | last.count++; | 116 | const last = span.responses[span.responses.length - 1]; |
117 | last.mean = last.mean + ((responseTime - last.mean) / last.count); | 117 | if (last !== undefined && |
118 | } else { | 118 | last.timestamp / 1000 + span.interval > Date.now() / 1000) { |
119 | span.responses.push({ | 119 | last[category]++; |
120 | '2': category === 2 ? 1 : 0, | 120 | last.count++; |
121 | '3': category === 3 ? 1 : 0, | 121 | last.mean = last.mean + ((responseTime - last.mean) / last.count); |
122 | '4': category === 4 ? 1 : 0, | 122 | } else { |
123 | '5': category === 5 ? 1 : 0, | 123 | span.responses.push({ |
124 | count: 1, | 124 | '2': category === 2 ? 1 : 0, |
125 | mean: responseTime, | 125 | '3': category === 3 ? 1 : 0, |
126 | timestamp: Date.now() | 126 | '4': category === 4 ? 1 : 0, |
127 | }); | 127 | '5': category === 5 ? 1 : 0, |
128 | } | 128 | count: 1, |
129 | }); | 129 | mean: responseTime, |
130 | }); | 130 | timestamp: Date.now() |
131 | 131 | }); | |
132 | next(); | 132 | } |
133 | } | 133 | }); |
134 | }; | 134 | }); |
135 | }; | 135 | |
136 | 136 | next(); | |
137 | module.exports = middlewareWrapper; | 137 | } |
138 | 138 | }; | |
139 | }()); | 139 | }; |
140 | |||
141 | module.exports = middlewareWrapper; | ||
142 | |||
143 | }()); | ... | ... |
1 | MIT License | 1 | MIT License |
2 | 2 | ||
3 | Copyright (c) 2016 Rafal Wilinski | 3 | Copyright (c) 2016 Rafal Wilinski |
4 | 4 | ||
5 | Permission is hereby granted, free of charge, to any person obtaining a copy | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | of this software and associated documentation files (the "Software"), to deal | 6 | of this software and associated documentation files (the "Software"), to deal |
7 | in the Software without restriction, including without limitation the rights | 7 | in the Software without restriction, including without limitation the rights |
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | copies of the Software, and to permit persons to whom the Software is | 9 | copies of the Software, and to permit persons to whom the Software is |
10 | furnished to do so, subject to the following conditions: | 10 | furnished to do so, subject to the following conditions: |
11 | 11 | ||
12 | The above copyright notice and this permission notice shall be included in all | 12 | The above copyright notice and this permission notice shall be included in all |
13 | copies or substantial portions of the Software. | 13 | copies or substantial portions of the Software. |
14 | 14 | ||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | SOFTWARE. | 21 | SOFTWARE. |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | { | 1 | { |
2 | "name": "express-status-monitor", | 2 | "name": "express-status-monitor", |
3 | "version": "0.0.10", | 3 | "version": "0.0.10-dev", |
4 | "description": "Realtime Monitoring for Express-based Node applications", | 4 | "description": "Realtime Monitoring for Express-based Node applications", |
5 | "main": "app.js", | 5 | "main": "app.js", |
6 | "keywords": [ | 6 | "keywords": [ |
7 | "node", | 7 | "node", |
8 | "status", | 8 | "status", |
9 | "monitoring", | 9 | "monitoring", |
10 | "express", | 10 | "express", |
11 | "charts" | 11 | "charts" |
12 | ], | 12 | ], |
13 | "author": "Rafal Wilinski <raf.wilinski@gmail.com> (http://rwilinski.me)", | 13 | "author": "Rafal Wilinski <raf.wilinski@gmail.com> (http://rwilinski.me)", |
14 | "contributors": [ | 14 | "contributors": [ |
15 | { | 15 | { |
16 | "name": "Julien Breux", | 16 | "name": "Julien Breux", |
17 | "email": "julien.breux@gmail.com", | 17 | "email": "julien.breux@gmail.com", |
18 | "url": "https://github.com/JulienBreux/" | 18 | "url": "https://github.com/JulienBreux/" |
19 | } | 19 | }, |
20 | ], | 20 | { |
21 | "repository": { | 21 | "name": "Jabis Sevon", |
22 | "type": "git", | 22 | "email": "jabis.is@gmail.com", |
23 | "url": "https://github.com/RafalWilinski/express-status-monitor.git" | 23 | "url": "https://jscodex.com" |
24 | }, | 24 | } |
25 | "license": "MIT", | 25 | |
26 | "dependencies": { | 26 | ], |
27 | "on-headers": "^1.0.1", | 27 | "repository": { |
28 | "pidusage": "^1.0.4", | 28 | "type": "git", |
29 | "socket.io": "^1.4.8" | 29 | "url": "https://github.com/RafalWilinski/express-status-monitor.git" |
30 | } | 30 | }, |
31 | } | 31 | "license": "MIT", |
32 | "dependencies": { | ||
33 | "on-headers": "^1.0.1", | ||
34 | "pidusage": "^1.0.4", | ||
35 | "socket.io": "^0.9.17" | ||
36 | } | ||
37 | } | ... | ... |
socket.io.min.js
0 → 100644
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment