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  )