package handler import ( "encoding/json" "fmt" "net/http" "runtime" "strings" "sync/atomic" "time" ) type Metrics struct { // 基础指标 Uptime string `json:"uptime"` ActiveRequests int64 `json:"active_requests"` TotalRequests int64 `json:"total_requests"` TotalErrors int64 `json:"total_errors"` ErrorRate float64 `json:"error_rate"` // 系统指标 NumGoroutine int `json:"num_goroutine"` MemoryUsage string `json:"memory_usage"` // 性能指标 AverageResponseTime string `json:"avg_response_time"` RequestsPerSecond float64 `json:"requests_per_second"` // 新增字段 TotalBytes int64 `json:"total_bytes"` BytesPerSecond float64 `json:"bytes_per_second"` StatusCodeStats map[string]int64 `json:"status_code_stats"` LatencyPercentiles map[string]float64 `json:"latency_percentiles"` TopPaths []PathMetrics `json:"top_paths"` RecentRequests []RequestLog `json:"recent_requests"` } type PathMetrics struct { Path string `json:"path"` RequestCount int64 `json:"request_count"` ErrorCount int64 `json:"error_count"` AvgLatency string `json:"avg_latency"` BytesTransferred int64 `json:"bytes_transferred"` } // 添加格式化字节的辅助函数 func formatBytes(bytes uint64) string { const ( MB = 1024 * 1024 KB = 1024 ) switch { case bytes >= MB: return fmt.Sprintf("%.2f MB", float64(bytes)/MB) case bytes >= KB: return fmt.Sprintf("%.2f KB", float64(bytes)/KB) default: return fmt.Sprintf("%d Bytes", bytes) } } func (h *ProxyHandler) MetricsHandler(w http.ResponseWriter, r *http.Request) { var m runtime.MemStats runtime.ReadMemStats(&m) // 获取状态码统计 statusStats := make(map[string]int64) for i, v := range h.metrics.statusStats { statusStats[fmt.Sprintf("%dxx", i+1)] = v.Load() } // 获取Top 10路径统计 var pathMetrics []PathMetrics h.metrics.pathStats.Range(func(key, value interface{}) bool { stats := value.(*PathStats) pathMetrics = append(pathMetrics, PathMetrics{ Path: key.(string), RequestCount: stats.requests.Load(), ErrorCount: stats.errors.Load(), AvgLatency: formatDuration(time.Duration(stats.latencySum.Load() / stats.requests.Load())), BytesTransferred: stats.bytes.Load(), }) return len(pathMetrics) < 10 }) // 获取最近的请求 var recentReqs []RequestLog h.recentRequests.RLock() cursor := h.recentRequests.cursor.Load() for i := 0; i < 10; i++ { idx := (cursor - int64(i) + 1000) % 1000 if h.recentRequests.items[idx] != nil { recentReqs = append(recentReqs, *h.recentRequests.items[idx]) } } h.recentRequests.RUnlock() metrics := Metrics{ Uptime: time.Since(h.startTime).String(), ActiveRequests: atomic.LoadInt64(&h.metrics.activeRequests), TotalRequests: atomic.LoadInt64(&h.metrics.totalRequests), TotalErrors: atomic.LoadInt64(&h.metrics.totalErrors), ErrorRate: float64(h.metrics.totalErrors) / float64(h.metrics.totalRequests), NumGoroutine: runtime.NumGoroutine(), MemoryUsage: formatBytes(m.Alloc), TotalBytes: h.metrics.totalBytes.Load(), BytesPerSecond: float64(h.metrics.totalBytes.Load()) / time.Since(h.startTime).Seconds(), StatusCodeStats: statusStats, TopPaths: pathMetrics, RecentRequests: recentReqs, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(metrics) } // 添加格式化时间的辅助函数 func formatDuration(d time.Duration) string { if d < time.Millisecond { return fmt.Sprintf("%.2f μs", float64(d.Microseconds())) } if d < time.Second { return fmt.Sprintf("%.2f ms", float64(d.Milliseconds())) } return fmt.Sprintf("%.2f s", d.Seconds()) } // 修改模板,添加登录页面 var loginTemplate = ` Proxy-Go Metrics Login

Metrics Login

密码错误
` // 修改原有的 metricsTemplate,添加 token 检查 var metricsTemplate = ` Proxy-Go Metrics

Proxy-Go Metrics

基础指标

运行时间
当前活跃请求
总请求数
错误数
错误率

系统指标

Goroutine数量
内存使用

性能指标

平均响应时间
每秒请求数
` // 添加认证中间件 func (h *ProxyHandler) AuthMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if auth == "" || !strings.HasPrefix(auth, "Bearer ") { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } token := strings.TrimPrefix(auth, "Bearer ") if !h.auth.validateToken(token) { http.Error(w, "Invalid token", http.StatusUnauthorized) return } next(w, r) } } // 修改处理器 func (h *ProxyHandler) MetricsPageHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(loginTemplate)) } func (h *ProxyHandler) MetricsDashboardHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(metricsTemplate)) } func (h *ProxyHandler) MetricsAuthHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } if req.Password != h.config.Metrics.Password { http.Error(w, "Invalid password", http.StatusUnauthorized) return } token := h.auth.generateToken() h.auth.addToken(token, time.Duration(h.config.Metrics.TokenExpiry)*time.Second) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "token": token, }) }