From 7b6a1934344c0d498cfc1e6c0bb10ef91a1febf9 Mon Sep 17 00:00:00 2001 From: wood chen Date: Sat, 30 Nov 2024 21:42:30 +0800 Subject: [PATCH] feat(metrics): enhance metrics UI with detailed traffic statistics, status code breakdown, and recent requests overview --- internal/handler/metrics.go | 159 +++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 13 deletions(-) diff --git a/internal/handler/metrics.go b/internal/handler/metrics.go index e3506c3..d9f882c 100644 --- a/internal/handler/metrics.go +++ b/internal/handler/metrics.go @@ -293,6 +293,31 @@ var metricsTemplate = ` right: 140px; color: #666; } + /* 添加表格样式 */ + table { + width: 100%; + border-collapse: collapse; + margin: 10px 0; + } + th, td { + padding: 8px; + text-align: left; + border-bottom: 1px solid #eee; + } + th { + background: #f8f9fa; + color: #666; + } + .status-badge { + padding: 3px 8px; + border-radius: 12px; + font-size: 12px; + color: white; + } + .status-2xx { background: #28a745; } + .status-3xx { background: #17a2b8; } + .status-4xx { background: #ffc107; } + .status-5xx { background: #dc3545; } @@ -346,6 +371,56 @@ var metricsTemplate = ` +
+

流量统计

+
+ 总传输字节 + +
+
+ 每秒传输 + +
+
+ +
+

状态码统计

+
+
+ +
+

热门路径 (Top 10)

+ + + + + + + + + + + +
路径请求数错误数平均延迟传输大小
+
+ +
+

最近请求

+ + + + + + + + + + + + +
时间路径状态延迟大小客户端IP
+
+ @@ -356,6 +431,77 @@ var metricsTemplate = ` window.location.href = '/metrics/ui'; } + function formatBytes(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + } + + function formatDate(dateStr) { + const date = new Date(dateStr); + return date.toLocaleTimeString(); + } + + function updateMetrics(data) { + // 更新现有指标 + document.getElementById('uptime').textContent = data.uptime; + document.getElementById('activeRequests').textContent = data.active_requests; + document.getElementById('totalRequests').textContent = data.total_requests; + document.getElementById('totalErrors').textContent = data.total_errors; + document.getElementById('errorRate').textContent = (data.error_rate * 100).toFixed(2) + '%'; + document.getElementById('numGoroutine').textContent = data.num_goroutine; + document.getElementById('memoryUsage').textContent = data.memory_usage; + document.getElementById('avgResponseTime').textContent = data.avg_response_time; + document.getElementById('requestsPerSecond').textContent = data.requests_per_second.toFixed(2); + + // 更新流量统计 + document.getElementById('totalBytes').textContent = formatBytes(data.total_bytes); + document.getElementById('bytesPerSecond').textContent = formatBytes(data.bytes_per_second) + '/s'; + + // 更新状态码统计 + const statusCodesHtml = Object.entries(data.status_code_stats) + .map(([status, count]) => { + const statusClass = 'status-' + status.charAt(0) + 'xx'; + return '
' + + '' + + '' + status + '' + + '' + + '' + count + '' + + '
'; + }) + .join(''); + document.getElementById('statusCodes').innerHTML = statusCodesHtml; + + // 更新热门路径 + const topPathsHtml = data.top_paths.map(path => + '' + + '' + path.path + '' + + '' + path.request_count + '' + + '' + path.error_count + '' + + '' + path.avg_latency + '' + + '' + formatBytes(path.bytes_transferred) + '' + + '' + ).join(''); + document.querySelector('#topPaths tbody').innerHTML = topPathsHtml; + + // 更新最近请求 + const recentRequestsHtml = data.recent_requests.map(req => + '' + + '' + formatDate(req.Time) + '' + + '' + req.Path + '' + + '' + req.Status + '' + + '' + req.Latency + '' + + '' + formatBytes(req.BytesSent) + '' + + '' + req.ClientIP + '' + + '' + ).join(''); + document.querySelector('#recentRequests tbody').innerHTML = recentRequestsHtml; + + document.getElementById('lastUpdate').textContent = '最后更新: ' + new Date().toLocaleTimeString(); + } + function refreshMetrics() { fetch('/metrics', { headers: { @@ -377,19 +523,6 @@ var metricsTemplate = ` .catch(error => console.error('Error:', error)); } - function updateMetrics(data) { - document.getElementById('uptime').textContent = data.uptime; - document.getElementById('activeRequests').textContent = data.active_requests; - document.getElementById('totalRequests').textContent = data.total_requests; - document.getElementById('totalErrors').textContent = data.total_errors; - document.getElementById('errorRate').textContent = (data.error_rate * 100).toFixed(2) + '%'; - document.getElementById('numGoroutine').textContent = data.num_goroutine; - document.getElementById('memoryUsage').textContent = data.memory_usage; - document.getElementById('avgResponseTime').textContent = data.avg_response_time; - document.getElementById('requestsPerSecond').textContent = data.requests_per_second.toFixed(2); - document.getElementById('lastUpdate').textContent = '最后更新: ' + new Date().toLocaleTimeString(); - } - // 初始加载 refreshMetrics();