feat(metrics): Add interval-based metrics tracking and reporting

- Implement time-interval statistics for requests, errors, and bytes
- Add per-second metrics calculation for requests and bandwidth
- Enhance GetStats method to return interval-specific performance data
- Track and reset interval status codes dynamically
- Improve metrics granularity with more detailed performance insights
This commit is contained in:
wood chen 2025-02-17 06:13:24 +08:00
parent a4437b9a39
commit 92910a608f
2 changed files with 78 additions and 18 deletions

View File

@ -244,7 +244,7 @@ func (h *ProxyHandler) OAuthCallbackHandler(w http.ResponseWriter, r *http.Reque
<script> <script>
localStorage.setItem('token', '%s'); localStorage.setItem('token', '%s');
localStorage.setItem('user', '%s'); localStorage.setItem('user', '%s');
window.location.href = '/admin'; window.location.href = '/admin/dashboard';
</script> </script>
</body> </body>
</html> </html>

View File

@ -38,6 +38,14 @@ type Collector struct {
config *config.Config config *config.Config
lastMinute time.Time // 用于计算每分钟带宽 lastMinute time.Time // 用于计算每分钟带宽
minuteBytes int64 // 当前分钟的字节数 minuteBytes int64 // 当前分钟的字节数
// 新增:时间段统计
lastStatsTime time.Time
intervalRequests int64
intervalErrors int64
intervalBytes int64
intervalLatencySum int64
intervalStatusCodes sync.Map
} }
var ( var (
@ -51,6 +59,7 @@ func InitCollector(cfg *config.Config) error {
instance = &Collector{ instance = &Collector{
startTime: time.Now(), startTime: time.Now(),
lastMinute: time.Now(), lastMinute: time.Now(),
lastStatsTime: time.Now(),
recentRequests: make([]models.RequestLog, 0, 1000), recentRequests: make([]models.RequestLog, 0, 1000),
config: cfg, config: cfg,
minLatency: math.MaxInt64, // 初始化为最大值 minLatency: math.MaxInt64, // 初始化为最大值
@ -83,11 +92,37 @@ func (c *Collector) EndRequest() {
// RecordRequest 记录请求 // RecordRequest 记录请求
func (c *Collector) RecordRequest(path string, status int, latency time.Duration, bytes int64, clientIP string, r *http.Request) { func (c *Collector) RecordRequest(path string, status int, latency time.Duration, bytes int64, clientIP string, r *http.Request) {
// 批量更新基础指标 // 更新总体统计
atomic.AddInt64(&c.totalRequests, 1) atomic.AddInt64(&c.totalRequests, 1)
atomic.AddInt64(&c.totalBytes, bytes) atomic.AddInt64(&c.totalBytes, bytes)
atomic.AddInt64(&c.latencySum, int64(latency)) atomic.AddInt64(&c.latencySum, int64(latency))
// 更新时间段统计
atomic.AddInt64(&c.intervalRequests, 1)
atomic.AddInt64(&c.intervalBytes, bytes)
atomic.AddInt64(&c.intervalLatencySum, int64(latency))
if status >= 400 {
atomic.AddInt64(&c.intervalErrors, 1)
}
// 更新带宽统计
atomic.AddInt64(&c.minuteBytes, bytes)
now := time.Now()
if now.Sub(c.lastMinute) >= time.Minute {
currentMinute := now.Format("15:04")
c.bandwidthStats.Store(currentMinute, atomic.SwapInt64(&c.minuteBytes, 0))
c.lastMinute = now
}
// 更新时间段状态码统计
statusKey := fmt.Sprintf("%d", status)
if counter, ok := c.intervalStatusCodes.Load(statusKey); ok {
atomic.AddInt64(counter.(*int64), 1)
} else {
var count int64 = 1
c.intervalStatusCodes.Store(statusKey, &count)
}
// 更新最小和最大响应时间 // 更新最小和最大响应时间
latencyNanos := int64(latency) latencyNanos := int64(latency)
for { for {
@ -149,7 +184,7 @@ func (c *Collector) RecordRequest(path string, status int, latency time.Duration
} }
// 更新状态码统计 // 更新状态码统计
statusKey := fmt.Sprintf("%d", status) statusKey = fmt.Sprintf("%d", status)
if counter, ok := c.statusCodeStats.Load(statusKey); ok { if counter, ok := c.statusCodeStats.Load(statusKey); ok {
atomic.AddInt64(counter.(*int64), 1) atomic.AddInt64(counter.(*int64), 1)
} else { } else {
@ -218,13 +253,29 @@ func (c *Collector) GetStats() map[string]interface{} {
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
// 计算平均延迟 now := time.Now()
interval := now.Sub(c.lastStatsTime)
c.lastStatsTime = now
// 获取并重置时间段统计
intervalRequests := atomic.SwapInt64(&c.intervalRequests, 0)
intervalBytes := atomic.SwapInt64(&c.intervalBytes, 0)
intervalLatencySum := atomic.SwapInt64(&c.intervalLatencySum, 0)
intervalErrors := atomic.SwapInt64(&c.intervalErrors, 0)
// 计算时间段平均延迟
avgLatency := float64(0) avgLatency := float64(0)
totalReqs := atomic.LoadInt64(&c.totalRequests) if intervalRequests > 0 {
if totalReqs > 0 { avgLatency = float64(intervalLatencySum) / float64(intervalRequests)
avgLatency = float64(atomic.LoadInt64(&c.latencySum)) / float64(totalReqs)
} }
// 收集并重置时间段状态码统计
intervalStatusStats := make(map[string]int64)
c.intervalStatusCodes.Range(func(key, value interface{}) bool {
intervalStatusStats[key.(string)] = atomic.SwapInt64(value.(*int64), 0)
return true
})
// 收集状态码统计 // 收集状态码统计
statusCodeStats := make(map[string]int64) statusCodeStats := make(map[string]int64)
c.statusCodeStats.Range(func(key, value interface{}) bool { c.statusCodeStats.Range(func(key, value interface{}) bool {
@ -292,18 +343,27 @@ func (c *Collector) GetStats() map[string]interface{} {
minLatency = 0 minLatency = 0
} }
// 获取最近请求记录(加锁)
c.recentRequestsMutex.RLock()
recentRequests := make([]models.RequestLog, len(c.recentRequests))
copy(recentRequests, c.recentRequests)
c.recentRequestsMutex.RUnlock()
return map[string]interface{}{ return map[string]interface{}{
"uptime": FormatUptime(time.Since(c.startTime)), "uptime": FormatUptime(time.Since(c.startTime)),
"active_requests": atomic.LoadInt64(&c.activeRequests), "active_requests": atomic.LoadInt64(&c.activeRequests),
"total_requests": atomic.LoadInt64(&c.totalRequests), "total_requests": atomic.LoadInt64(&c.totalRequests),
"total_errors": atomic.LoadInt64(&c.totalErrors), "total_errors": atomic.LoadInt64(&c.totalErrors),
"interval_errors": intervalErrors,
"total_bytes": atomic.LoadInt64(&c.totalBytes), "total_bytes": atomic.LoadInt64(&c.totalBytes),
"num_goroutine": runtime.NumGoroutine(), "num_goroutine": runtime.NumGoroutine(),
"memory_usage": utils.FormatBytes(int64(mem.Alloc)), "memory_usage": utils.FormatBytes(int64(mem.Alloc)),
"avg_response_time": fmt.Sprintf("%.2fms", avgLatency/float64(time.Millisecond)), "avg_response_time": fmt.Sprintf("%.2fms", avgLatency/float64(time.Millisecond)),
"status_code_stats": statusCodeStats, "requests_per_second": float64(intervalRequests) / interval.Seconds(),
"bytes_per_second": float64(intervalBytes) / interval.Seconds(),
"status_code_stats": intervalStatusStats,
"top_paths": pathMetrics, "top_paths": pathMetrics,
"recent_requests": c.recentRequests, "recent_requests": recentRequests,
"latency_stats": map[string]interface{}{ "latency_stats": map[string]interface{}{
"min": fmt.Sprintf("%.2fms", float64(minLatency)/float64(time.Millisecond)), "min": fmt.Sprintf("%.2fms", float64(minLatency)/float64(time.Millisecond)),
"max": fmt.Sprintf("%.2fms", float64(maxLatency)/float64(time.Millisecond)), "max": fmt.Sprintf("%.2fms", float64(maxLatency)/float64(time.Millisecond)),