github.com/gofiber/fiber/v2@v2.47.0/middleware/monitor/index.go (about) 1 package monitor 2 3 import ( 4 "strconv" 5 "strings" 6 "time" 7 ) 8 9 type viewBag struct { 10 title string 11 refresh time.Duration 12 fontURL string 13 chartJSURL string 14 customHead string 15 } 16 17 // returns index with new title/refresh 18 func newIndex(dat viewBag) string { 19 timeout := dat.refresh.Milliseconds() - timeoutDiff 20 if timeout < timeoutDiff { 21 timeout = timeoutDiff 22 } 23 ts := strconv.FormatInt(timeout, 10) 24 replacer := strings.NewReplacer("$TITLE", dat.title, "$TIMEOUT", ts, 25 "$FONT_URL", dat.fontURL, "$CHART_JS_URL", dat.chartJSURL, "$CUSTOM_HEAD", dat.customHead, 26 ) 27 return replacer.Replace(indexHTML) 28 } 29 30 const ( 31 defaultTitle = "Fiber Monitor" 32 33 defaultRefresh = 3 * time.Second 34 timeoutDiff = 200 // timeout will be Refresh (in milliseconds) - timeoutDiff 35 minRefresh = timeoutDiff * time.Millisecond 36 defaultFontURL = `https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap` 37 defaultChartJSURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` 38 defaultCustomHead = `` 39 40 // parametrized by $TITLE and $TIMEOUT 41 indexHTML = `<!DOCTYPE html> 42 <html lang="en"> 43 <head> 44 <meta charset="UTF-8"> 45 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 46 <link href="$FONT_URL" rel="stylesheet"> 47 <script src="$CHART_JS_URL"></script> 48 49 <title>$TITLE</title> 50 <style> 51 body { 52 margin: 0; 53 font: 16px / 1.6 'Roboto', sans-serif; 54 } 55 .wrapper { 56 max-width: 900px; 57 margin: 0 auto; 58 padding: 30px 0; 59 } 60 .title { 61 text-align: center; 62 margin-bottom: 2em; 63 } 64 .title h1 { 65 font-size: 1.8em; 66 padding: 0; 67 margin: 0; 68 } 69 .row { 70 display: flex; 71 margin-bottom: 20px; 72 align-items: center; 73 } 74 .row .column:first-child { width: 35%; } 75 .row .column:last-child { width: 65%; } 76 .metric { 77 color: #777; 78 font-weight: 900; 79 } 80 h2 { 81 padding: 0; 82 margin: 0; 83 font-size: 2.2em; 84 } 85 h2 span { 86 font-size: 12px; 87 color: #777; 88 } 89 h2 span.ram_os { color: rgba(255, 150, 0, .8); } 90 h2 span.ram_total { color: rgba(0, 200, 0, .8); } 91 canvas { 92 width: 200px; 93 height: 180px; 94 } 95 $CUSTOM_HEAD 96 </style> 97 </head> 98 <body> 99 <section class="wrapper"> 100 <div class="title"><h1>$TITLE</h1></div> 101 <section class="charts"> 102 <div class="row"> 103 <div class="column"> 104 <div class="metric">CPU Usage</div> 105 <h2 id="cpuMetric">0.00%</h2> 106 </div> 107 <div class="column"> 108 <canvas id="cpuChart"></canvas> 109 </div> 110 </div> 111 <div class="row"> 112 <div class="column"> 113 <div class="metric">Memory Usage</div> 114 <h2 id="ramMetric" title="PID used / OS used / OS total">0.00 MB</h2> 115 </div> 116 <div class="column"> 117 <canvas id="ramChart"></canvas> 118 </div> 119 </div> 120 <div class="row"> 121 <div class="column"> 122 <div class="metric">Response Time</div> 123 <h2 id="rtimeMetric">0ms</h2> 124 </div> 125 <div class="column"> 126 <canvas id="rtimeChart"></canvas> 127 </div> 128 </div> 129 <div class="row"> 130 <div class="column"> 131 <div class="metric">Open Connections</div> 132 <h2 id="connsMetric">0</h2> 133 </div> 134 <div class="column"> 135 <canvas id="connsChart"></canvas> 136 </div> 137 </div> 138 </section> 139 </section> 140 <script> 141 function formatBytes(bytes, decimals = 1) { 142 if (bytes === 0) return '0 Bytes'; 143 144 const k = 1024; 145 const dm = decimals < 0 ? 0 : decimals; 146 const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 147 148 const i = Math.floor(Math.log(bytes) / Math.log(k)); 149 150 return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; 151 } 152 Chart.defaults.global.legend.display = false; 153 Chart.defaults.global.defaultFontSize = 8; 154 Chart.defaults.global.animation.duration = 1000; 155 Chart.defaults.global.animation.easing = 'easeOutQuart'; 156 Chart.defaults.global.elements.line.backgroundColor = 'rgba(0, 172, 215, 0.25)'; 157 Chart.defaults.global.elements.line.borderColor = 'rgba(0, 172, 215, 1)'; 158 Chart.defaults.global.elements.line.borderWidth = 2; 159 160 const options = { 161 scales: { 162 yAxes: [{ ticks: { beginAtZero: true }}], 163 xAxes: [{ 164 type: 'time', 165 time: { 166 unitStepSize: 30, 167 unit: 'second' 168 }, 169 gridlines: { display: false } 170 }] 171 }, 172 tooltips: { enabled: false }, 173 responsive: true, 174 maintainAspectRatio: false, 175 animation: false 176 }; 177 const cpuMetric = document.querySelector('#cpuMetric'); 178 const ramMetric = document.querySelector('#ramMetric'); 179 const rtimeMetric = document.querySelector('#rtimeMetric'); 180 const connsMetric = document.querySelector('#connsMetric'); 181 182 const cpuChartCtx = document.querySelector('#cpuChart').getContext('2d'); 183 const ramChartCtx = document.querySelector('#ramChart').getContext('2d'); 184 const rtimeChartCtx = document.querySelector('#rtimeChart').getContext('2d'); 185 const connsChartCtx = document.querySelector('#connsChart').getContext('2d'); 186 187 const cpuChart = createChart(cpuChartCtx); 188 const ramChart = createChart(ramChartCtx); 189 const rtimeChart = createChart(rtimeChartCtx); 190 const connsChart = createChart(connsChartCtx); 191 192 const charts = [cpuChart, ramChart, rtimeChart, connsChart]; 193 194 function createChart(ctx) { 195 return new Chart(ctx, { 196 type: 'line', 197 data: { 198 labels: [], 199 datasets: [{ 200 label: '', 201 data: [], 202 lineTension: 0.2, 203 pointRadius: 0, 204 }] 205 }, 206 options 207 }); 208 } 209 ramChart.data.datasets.push({ 210 data: [], 211 lineTension: 0.2, 212 pointRadius: 0, 213 backgroundColor: 'rgba(255, 200, 0, .6)', 214 borderColor: 'rgba(255, 150, 0, .8)', 215 }) 216 ramChart.data.datasets.push({ 217 data: [], 218 lineTension: 0.2, 219 pointRadius: 0, 220 backgroundColor: 'rgba(0, 255, 0, .4)', 221 borderColor: 'rgba(0, 200, 0, .8)', 222 }) 223 function update(json, rtime) { 224 cpu = json.pid.cpu.toFixed(1); 225 cpuOS = json.os.cpu.toFixed(1); 226 227 cpuMetric.innerHTML = cpu + '% <span>' + cpuOS + '%</span>'; 228 ramMetric.innerHTML = formatBytes(json.pid.ram) + '<span> / </span><span class="ram_os">' + formatBytes(json.os.ram) + 229 '<span><span> / </span><span class="ram_total">' + formatBytes(json.os.total_ram) + '</span>'; 230 rtimeMetric.innerHTML = rtime + 'ms <span>client</span>'; 231 connsMetric.innerHTML = json.pid.conns + ' <span>' + json.os.conns + '</span>'; 232 233 cpuChart.data.datasets[0].data.push(cpu); 234 ramChart.data.datasets[2].data.push((json.os.total_ram / 1e6).toFixed(2)); 235 ramChart.data.datasets[1].data.push((json.os.ram / 1e6).toFixed(2)); 236 ramChart.data.datasets[0].data.push((json.pid.ram / 1e6).toFixed(2)); 237 rtimeChart.data.datasets[0].data.push(rtime); 238 connsChart.data.datasets[0].data.push(json.pid.conns); 239 240 const timestamp = new Date().getTime(); 241 242 charts.forEach(chart => { 243 if (chart.data.labels.length > 50) { 244 chart.data.datasets.forEach(function (dataset) { dataset.data.shift(); }); 245 chart.data.labels.shift(); 246 } 247 chart.data.labels.push(timestamp); 248 chart.update(); 249 }); 250 setTimeout(fetchJSON, $TIMEOUT) 251 } 252 function fetchJSON() { 253 var t1 = '' 254 var t0 = performance.now() 255 fetch(window.location.href, { 256 headers: { 'Accept': 'application/json' }, 257 credentials: 'same-origin' 258 }) 259 .then(res => { 260 t1 = performance.now() 261 return res.json() 262 }) 263 .then(res => { update(res, Math.round(t1 - t0)) }) 264 .catch(console.error); 265 } 266 fetchJSON() 267 </script> 268 </body> 269 </html> 270 ` 271 )