From 5eb4f55dfd56051a2343c4524abdee837f5dbaab Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Tue, 10 Aug 2021 07:55:06 +0100 Subject: [PATCH 01/18] Add the new gauges to the prometheus handler --- server/prometheus.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/prometheus.js b/server/prometheus.js index f60ec45a6..a6b4d7aac 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -8,6 +8,17 @@ const commonLabels = [ 'monitor_port', ] +const monitor_cert_days_remaining = new PrometheusClient.Gauge({ + name: 'monitor_cert_days_remaining', + help: 'The number of days remaining until the certificate expires', + labelNames: commonLabels +}); + +const monitor_cert_is_valid = new PrometheusClient.Gauge({ + name: 'monitor_cert_is_valid', + help: 'Is the certificate still valid? (1 = Yes, 0= No)', + labelNames: commonLabels +}); const monitor_response_time = new PrometheusClient.Gauge({ name: 'monitor_response_time', help: 'Monitor Response Time (ms)', From 692a11e51e8f93672659d6d066c9ccbdda97234d Mon Sep 17 00:00:00 2001 From: LouisLam Date: Tue, 10 Aug 2021 17:51:30 +0800 Subject: [PATCH 02/18] pass tls info to prometheus.update --- server/model/monitor.js | 12 +++++++++--- server/prometheus.js | 36 ++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 2520c282e..ce8a249c6 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -79,6 +79,10 @@ class Monitor extends BeanModel { const beat = async () => { + // Expose here for prometheus update + // undefined if not https + let tlsInfo = undefined; + if (! previousBeat) { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ this.id, @@ -132,7 +136,7 @@ class Monitor extends BeanModel { let certInfoStartTime = dayjs().valueOf(); if (this.getUrl()?.protocol === "https:") { try { - await this.updateTlsInfo(checkCertificate(res)); + tlsInfo = await this.updateTlsInfo(checkCertificate(res)); } catch (e) { if (e.message !== "No TLS certificate in response") { console.error(e.message) @@ -254,7 +258,7 @@ class Monitor extends BeanModel { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } - prometheus.update(bean) + prometheus.update(bean, tlsInfo) io.to(this.user_id).emit("heartbeat", bean.toJSON()); @@ -289,7 +293,7 @@ class Monitor extends BeanModel { /** * Store TLS info to database * @param checkCertificateResult - * @returns {Promise} + * @returns {Promise} */ async updateTlsInfo(checkCertificateResult) { let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ @@ -301,6 +305,8 @@ class Monitor extends BeanModel { } tls_info_bean.info_json = JSON.stringify(checkCertificateResult); await R.store(tls_info_bean); + + return checkCertificateResult; } static async sendStats(io, monitorID, userID) { diff --git a/server/prometheus.js b/server/prometheus.js index a6b4d7aac..21b83b342 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -1,33 +1,33 @@ -const PrometheusClient = require('prom-client'); +const PrometheusClient = require("prom-client"); const commonLabels = [ - 'monitor_name', - 'monitor_type', - 'monitor_url', - 'monitor_hostname', - 'monitor_port', + "monitor_name", + "monitor_type", + "monitor_url", + "monitor_hostname", + "monitor_port", ] const monitor_cert_days_remaining = new PrometheusClient.Gauge({ - name: 'monitor_cert_days_remaining', - help: 'The number of days remaining until the certificate expires', + name: "monitor_cert_days_remaining", + help: "The number of days remaining until the certificate expires", labelNames: commonLabels }); const monitor_cert_is_valid = new PrometheusClient.Gauge({ - name: 'monitor_cert_is_valid', - help: 'Is the certificate still valid? (1 = Yes, 0= No)', + name: "monitor_cert_is_valid", + help: "Is the certificate still valid? (1 = Yes, 0= No)", labelNames: commonLabels }); const monitor_response_time = new PrometheusClient.Gauge({ - name: 'monitor_response_time', - help: 'Monitor Response Time (ms)', + name: "monitor_response_time", + help: "Monitor Response Time (ms)", labelNames: commonLabels }); const monitor_status = new PrometheusClient.Gauge({ - name: 'monitor_status', - help: 'Monitor Status (1 = UP, 0= DOWN)', + name: "monitor_status", + help: "Monitor Status (1 = UP, 0= DOWN)", labelNames: commonLabels }); @@ -44,7 +44,11 @@ class Prometheus { } } - update(heartbeat) { + update(heartbeat, tlsInfo) { + + // TODO: TLS Info here + console.log(tlsInfo) + try { monitor_status.set(this.monitorLabelValues, heartbeat.status) } catch (e) { @@ -52,7 +56,7 @@ class Prometheus { } try { - if (typeof heartbeat.ping === 'number') { + if (typeof heartbeat.ping === "number") { monitor_response_time.set(this.monitorLabelValues, heartbeat.ping) } else { // Is it good? From 268dd33792a022f64cf92dff97664e5736950755 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Tue, 10 Aug 2021 12:14:13 +0100 Subject: [PATCH 03/18] Add TLS Info to Prometheus metric output --- server/prometheus.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/server/prometheus.js b/server/prometheus.js index 21b83b342..ef3b0352d 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -47,7 +47,26 @@ class Prometheus { update(heartbeat, tlsInfo) { // TODO: TLS Info here - console.log(tlsInfo) + + if (typeof tlsInfo !== "undefined"){ + try { + var is_valid = 0 + if (tlsInfo.valid == true){ + is_valid = 1 + } else { + is_valid = 0 + } + monitor_cert_is_valid.set(this.monitorLabelValues, is_valid) + } catch (e) { + console.error(e) + } + + try { + monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.daysRemaining) + } catch (e) { + console.error(e) + } + } try { monitor_status.set(this.monitorLabelValues, heartbeat.status) From debcac4924a88ebad51d559733d6ed006541a1fd Mon Sep 17 00:00:00 2001 From: LouisLam Date: Tue, 10 Aug 2021 20:00:59 +0800 Subject: [PATCH 04/18] run eslint --- server/prometheus.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/server/prometheus.js b/server/prometheus.js index ef3b0352d..3e4767b3d 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -45,13 +45,10 @@ class Prometheus { } update(heartbeat, tlsInfo) { - - // TODO: TLS Info here - - if (typeof tlsInfo !== "undefined"){ + if (typeof tlsInfo !== "undefined") { try { - var is_valid = 0 - if (tlsInfo.valid == true){ + let is_valid = 0 + if (tlsInfo.valid == true) { is_valid = 1 } else { is_valid = 0 From 48c6d8f19f27b71af25de65567cfce94df92a432 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Tue, 10 Aug 2021 19:34:47 +0800 Subject: [PATCH 05/18] Feat: Display recent ping chart --- package-lock.json | 84 +++++++++++++++++++++++++++++++++ package.json | 5 +- src/components/PingChart.vue | 91 ++++++++++++++++++++++++++++++++++++ src/pages/Details.vue | 17 ++++++- 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 src/components/PingChart.vue diff --git a/package-lock.json b/package-lock.json index c3fbf0633..4b9146424 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1779,6 +1779,16 @@ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", "dev": true }, + "chart.js": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.0.tgz", + "integrity": "sha512-J1a4EAb1Gi/KbhwDRmoovHTRuqT8qdF0kZ4XgwxpGethJHUdDrkqyPYwke0a+BuvSeUxPf8Cos6AX2AB8H8GLA==" + }, + "chartjs-adapter-dayjs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chartjs-adapter-dayjs/-/chartjs-adapter-dayjs-1.0.0.tgz", + "integrity": "sha512-EnbVqTJGFKLpg1TROLdCEufrzbmIa2oeLGx8O2Wdjw2EoMudoOo9+YFu+6CM0Z0hQ/v3yq/e/Y6efQMu22n8Jg==" + }, "chokidar": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", @@ -6814,11 +6824,85 @@ "@vue/shared": "3.2.1" } }, + "vue-chart-3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/vue-chart-3/-/vue-chart-3-0.5.7.tgz", + "integrity": "sha512-BccfPv2rodY6IOppYHvMluVmIJE1CHfp5uW2DXrHrm1kIzaafLwpQ5SwdrxuCevn/QhKoi7azzcxwRcoWbX9hg==", + "requires": { + "@vue/runtime-core": "^3.2.1", + "@vue/runtime-dom": "^3.2.1", + "csstype": "^3.0.8", + "lodash": "^4.17.21", + "nanoid": "^3.1.23", + "vue-demi": "^0.10.1" + }, + "dependencies": { + "@vue/reactivity": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.1.tgz", + "integrity": "sha512-4Lja2KmyiKvuraDed6dXK2A6+r/7x7xGDA7vVR2Aqc8hQVu0+FWeVX+IBfiVOSpbZXFlHLNmCBFkbuWLQSlgxg==", + "requires": { + "@vue/shared": "3.2.1" + } + }, + "@vue/runtime-core": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.1.tgz", + "integrity": "sha512-IsgelRM/5hYeRhz5+ECi66XvYDdjG2t4lARjHvCXw5s9Q4N6uIbjLMwtLzAWRxYf3/y258BrD+ehxAi943ScJg==", + "requires": { + "@vue/reactivity": "3.2.1", + "@vue/shared": "3.2.1" + } + }, + "@vue/runtime-dom": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.1.tgz", + "integrity": "sha512-bUAHUSe49A5wYdHQ8wsLU1CMPXaG2fRuv2661mx/6Q9+20QxglT3ss8ZeL6AVRu16JNJMcdvTTsNpbnMbVc/lQ==", + "requires": { + "@vue/runtime-core": "3.2.1", + "@vue/shared": "3.2.1", + "csstype": "^2.6.8" + }, + "dependencies": { + "csstype": { + "version": "2.6.17", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", + "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==" + } + } + }, + "@vue/shared": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.1.tgz", + "integrity": "sha512-INN92dVBNgd0TW9BqfQQKx/HWGCHhUUbAV5EZ5FgSCiEdwuZsJbGt1mdnaD9IxGhpiyOjP2ClxGG8SFp7ELcWg==" + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + } + } + }, "vue-confirm-dialog": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vue-confirm-dialog/-/vue-confirm-dialog-1.0.2.tgz", "integrity": "sha512-gTo1bMDWOLd/6ihmWv8VlPxhc9QaKoE5YqlsKydUOfrrQ3Q3taljF6yI+1TMtAtJLrvZ8DYrePhgBhY1VCJzbQ==" }, + "vue-demi": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.10.1.tgz", + "integrity": "sha512-L6Oi+BvmMv6YXvqv5rJNCFHEKSVu7llpWWJczqmAQYOdmPPw5PNYoz1KKS//Fxhi+4QP64dsPjtmvnYGo1jemA==" + }, "vue-eslint-parser": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.10.0.tgz", diff --git a/package.json b/package.json index 0f8929b52..8c7b0d0ce 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,14 @@ "@fortawesome/free-regular-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/vue-fontawesome": "^3.0.0-4", + "@louislam/sqlite3": "^5.0.3", "@popperjs/core": "^2.9.3", "args-parser": "^1.3.0", "axios": "^0.21.1", "bcrypt": "^5.0.1", "bootstrap": "^5.1.0", + "chart.js": "^3.5.0", + "chartjs-adapter-dayjs": "^1.0.0", "command-exists": "^1.2.9", "dayjs": "^1.10.6", "express": "^4.17.1", @@ -50,10 +53,10 @@ "redbean-node": "0.0.21", "socket.io": "^4.1.3", "socket.io-client": "^4.1.3", - "@louislam/sqlite3": "^5.0.3", "tcp-ping": "^0.1.1", "v-pagination-3": "^0.1.6", "vue": "^3.2.1", + "vue-chart-3": "^0.5.7", "vue-confirm-dialog": "^1.0.2", "vue-multiselect": "^3.0.0-alpha.2", "vue-router": "^4.0.10", diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue new file mode 100644 index 000000000..0d2dc76f4 --- /dev/null +++ b/src/components/PingChart.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 58ee68a22..bd6bf69dc 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -42,7 +42,11 @@

{{ pingTitle }}

(Current)

- + + + + +

Avg. {{ pingTitle }}

@@ -70,6 +74,14 @@
+
+
+
+ +
+
+
+
@@ -164,6 +176,7 @@ import Datetime from "../components/Datetime.vue"; import CountUp from "../components/CountUp.vue"; import Uptime from "../components/Uptime.vue"; import Pagination from "v-pagination-3"; +import PingChart from "../components/PingChart.vue"; export default { components: { @@ -174,6 +187,7 @@ export default { Confirm, Status, Pagination, + PingChart, }, data() { return { @@ -181,6 +195,7 @@ export default { perPage: 25, heartBeatList: [], toggleCertInfoBox: false, + showPingChartBox: false, } }, computed: { From 2bd735035c188373c024183a7074128ea6235ad2 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 11 Aug 2021 20:59:33 +0800 Subject: [PATCH 06/18] Misc: Show graph by default --- src/pages/Details.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Details.vue b/src/pages/Details.vue index bd6bf69dc..a0c6afb51 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -195,7 +195,7 @@ export default { perPage: 25, heartBeatList: [], toggleCertInfoBox: false, - showPingChartBox: false, + showPingChartBox: true, } }, computed: { From 71bec74081fcbc39fc5952becd6d04be0651debb Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 11 Aug 2021 21:00:33 +0800 Subject: [PATCH 07/18] Feat: Add down-ed bars, improve UI --- src/components/PingChart.vue | 65 ++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 0d2dc76f4..69dc6293a 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -24,7 +24,7 @@ export default { }, data() { return { - chartPeriodHrs: 24, + chartPeriodHrs: 6, }; }, computed: { @@ -39,11 +39,27 @@ export default { bottom: 10, }, }, + + elements: { + point: { + radius: 0, + }, + bar: { + barThickness: "flex", + } + }, scales: { x: { type: "time", time: { - stepSize: 30, + unit: "minute", + }, + ticks: { + maxRotation: 0, + autoSkipPadding: 10, + }, + grid: { + color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)", }, }, y: { @@ -51,10 +67,29 @@ export default { display: true, text: "Response Time (ms)", }, - } + offset: false, + grid: { + color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)", + }, + }, + y1: { + display: false, + position: "right", + grid: { + drawOnChartArea: false, + }, + min: 0, + max: 1, + offset: false, + }, }, bounds: "ticks", plugins: { + tooltip: { + filter: function (tooltipItem) { + return tooltipItem.datasetIndex === 0; + }, + }, legend: { display: false, }, @@ -62,9 +97,10 @@ export default { } }, chartData() { - let data = []; + let ping_data = []; + let down_data = []; if (this.monitorId in this.$root.heartbeatList) { - data = this.$root.heartbeatList[this.monitorId] + ping_data = this.$root.heartbeatList[this.monitorId] .filter( (beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours"))) .map((beat) => { @@ -73,15 +109,32 @@ export default { y: beat.ping, }; }); + down_data = this.$root.heartbeatList[this.monitorId] + .filter( + (beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours"))) + .map((beat) => { + return { + x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"), + y: beat.status === 0 ? 1 : 0, + }; + }); } return { datasets: [ { - data: data, + data: ping_data, fill: "origin", tension: 0.2, borderColor: "#5CDD8B", backgroundColor: "#5CDD8B38", + yAxisID: "y", + }, + { + type: "bar", + data: down_data, + borderColor: "#00000000", + backgroundColor: "#DC354568", + yAxisID: "y1", }, ], }; From 6ed1d8cb2f7f8a2dd466ae73614bb96adb02584f Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 12 Aug 2021 00:31:21 +0800 Subject: [PATCH 08/18] Feat: Use selective import, improve tooltip UI --- src/components/PingChart.vue | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 69dc6293a..0b41aa0d5 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -3,7 +3,7 @@