feat(metrics): 添加引用来源统计持久化支持

- 在 MetricsStorage 结构体中新增 refererStatsFile 字段
- 在 SaveMetrics 方法中添加引用来源统计保存逻辑
- 在 LoadMetrics 方法中实现引用来源统计的加载和恢复
- 前端页面调整路径显示,增加长路径的截断处理
- 优化引用来源统计的数据处理和类型转换
This commit is contained in:
wood chen 2025-03-09 12:02:32 +08:00
parent d2e5020d22
commit 2a41458bb8
2 changed files with 67 additions and 19 deletions

View File

@ -24,6 +24,7 @@ type MetricsStorage struct {
metricsFile string metricsFile string
pathStatsFile string pathStatsFile string
statusCodeFile string statusCodeFile string
refererStatsFile string
} }
// NewMetricsStorage 创建新的指标存储 // NewMetricsStorage 创建新的指标存储
@ -40,6 +41,7 @@ func NewMetricsStorage(collector *Collector, dataDir string, saveInterval time.D
metricsFile: filepath.Join(dataDir, "metrics.json"), metricsFile: filepath.Join(dataDir, "metrics.json"),
pathStatsFile: filepath.Join(dataDir, "path_stats.json"), pathStatsFile: filepath.Join(dataDir, "path_stats.json"),
statusCodeFile: filepath.Join(dataDir, "status_codes.json"), statusCodeFile: filepath.Join(dataDir, "status_codes.json"),
refererStatsFile: filepath.Join(dataDir, "referer_stats.json"),
} }
} }
@ -129,6 +131,11 @@ func (ms *MetricsStorage) SaveMetrics() error {
return fmt.Errorf("保存状态码统计失败: %v", err) return fmt.Errorf("保存状态码统计失败: %v", err)
} }
// 保存引用来源统计
if err := saveJSONToFile(ms.refererStatsFile, stats["top_referers"]); err != nil {
return fmt.Errorf("保存引用来源统计失败: %v", err)
}
ms.mutex.Lock() ms.mutex.Lock()
ms.lastSaveTime = time.Now() ms.lastSaveTime = time.Now()
ms.mutex.Unlock() ms.mutex.Unlock()
@ -165,6 +172,17 @@ func (ms *MetricsStorage) LoadMetrics() error {
return fmt.Errorf("加载状态码统计失败: %v", err) return fmt.Errorf("加载状态码统计失败: %v", err)
} }
// 加载引用来源统计(如果文件存在)
var refererStats []map[string]interface{}
if fileExists(ms.refererStatsFile) {
if err := loadJSONFromFile(ms.refererStatsFile, &refererStats); err != nil {
log.Printf("[MetricsStorage] 加载引用来源统计失败: %v", err)
// 不中断加载过程
} else {
log.Printf("[MetricsStorage] 成功加载引用来源统计: %d 条记录", len(refererStats))
}
}
// 将加载的数据应用到收集器 // 将加载的数据应用到收集器
// 1. 应用总字节数 // 1. 应用总字节数
if totalBytes, ok := basicMetrics["total_bytes"].(float64); ok { if totalBytes, ok := basicMetrics["total_bytes"].(float64); ok {
@ -214,6 +232,35 @@ func (ms *MetricsStorage) LoadMetrics() error {
} }
} }
// 4. 应用引用来源统计
if len(refererStats) > 0 {
for _, refererStat := range refererStats {
referer, ok := refererStat["path"].(string)
if !ok {
continue
}
requestCount, _ := refererStat["request_count"].(float64)
errorCount, _ := refererStat["error_count"].(float64)
bytesTransferred, _ := refererStat["bytes_transferred"].(float64)
// 创建或更新引用来源统计
var refererMetrics *models.PathMetrics
if existingMetrics, ok := ms.collector.refererStats.Load(referer); ok {
refererMetrics = existingMetrics.(*models.PathMetrics)
} else {
refererMetrics = &models.PathMetrics{Path: referer}
ms.collector.refererStats.Store(referer, refererMetrics)
}
// 设置统计值
refererMetrics.RequestCount.Store(int64(requestCount))
refererMetrics.ErrorCount.Store(int64(errorCount))
refererMetrics.BytesTransferred.Store(int64(bytesTransferred))
}
log.Printf("[MetricsStorage] 应用了 %d 条引用来源统计记录", len(refererStats))
}
// 4. 应用延迟分布桶(如果有) // 4. 应用延迟分布桶(如果有)
if latencyStats, ok := basicMetrics["latency_stats"].(map[string]interface{}); ok { if latencyStats, ok := basicMetrics["latency_stats"].(map[string]interface{}); ok {
if distribution, ok := latencyStats["distribution"].(map[string]interface{}); ok { if distribution, ok := latencyStats["distribution"].(map[string]interface{}); ok {

View File

@ -274,6 +274,7 @@ export default function DashboardPage() {
let displayRange = range; let displayRange = range;
if (range === "lt10ms") displayRange = "<10ms"; if (range === "lt10ms") displayRange = "<10ms";
if (range === "gt1s") displayRange = ">1s"; if (range === "gt1s") displayRange = ">1s";
if (range === "200-1000ms") displayRange = "0.2-1s";
return ( return (
<div key={range} className="p-3 rounded-lg border bg-card text-card-foreground shadow-sm"> <div key={range} className="p-3 rounded-lg border bg-card text-card-foreground shadow-sm">
@ -404,7 +405,7 @@ export default function DashboardPage() {
<tbody> <tbody>
{metrics.top_referers.map((referer, index) => ( {metrics.top_referers.map((referer, index) => (
<tr key={index} className="border-b"> <tr key={index} className="border-b">
<td className="p-2"> <td className="p-2 max-w-xs truncate">
<span className="text-blue-600"> <span className="text-blue-600">
{referer.path} {referer.path}
</span> </span>
@ -441,7 +442,7 @@ export default function DashboardPage() {
<tbody> <tbody>
{(metrics.top_paths || []).map((path, index) => ( {(metrics.top_paths || []).map((path, index) => (
<tr key={index} className="border-b"> <tr key={index} className="border-b">
<td className="p-2"> <td className="p-2 max-w-xs truncate">
<a <a
href={path.path} href={path.path}
target="_blank" target="_blank"