feat(dashboard): 首页请求趋势拆分状态码并合并容量到业务流量

- 24 小时请求趋势拆分展示请求总量与 200/400/500 状态码请求量,独占一行;
  时间桶聚合新增 status_200/400/500_count(CH countIf、PG FILTER),
  请求趋势改为基于原始桶聚合(小时 rollup 无状态码口径)
- 首页移除宿主机磁盘指标,容量趋势(CPU/内存)并入业务流量卡片展示
- 压缩协议 traffic_24h 扩展为 7 元组,前端归一化同步更新
This commit is contained in:
ryan
2026-08-13 10:57:21 +08:00
parent a9e4237bbf
commit a4dd5ca9e1
15 changed files with 196 additions and 160 deletions
@@ -330,11 +330,15 @@ func compressDistributionItems(items []observability.DistributionItem) [][]any {
func compressTrafficTrendPoints(points []observability.TrafficTrendPoint) [][]any {
rows := make([][]any, 0, len(points))
for _, point := range points {
// Compact layout: [0] bucket, [1] request, [2] error, [3] uv, [4] 2xx, [5] 4xx, [6] 5xx
rows = append(rows, []any{
point.BucketStartedAt,
point.RequestCount,
point.ErrorCount,
point.UniqueVisitorCount,
point.Status2xxCount,
point.Status4xxCount,
point.Status5xxCount,
})
}
return rows
@@ -143,7 +143,7 @@ func TestGetOverviewStructure(t *testing.T) {
require.Len(t, overview.Trends.Network24h, 24)
require.Len(t, overview.Trends.DiskIO24h, 24)
for _, row := range overview.Trends.Traffic24h {
require.Len(t, row, 4)
require.Len(t, row, 7)
}
for _, row := range overview.Trends.Capacity24h {
require.Len(t, row, 4)
@@ -85,6 +85,9 @@ type TrafficTrendPoint struct {
RequestCount int64 `json:"request_count"`
ErrorCount int64 `json:"error_count"`
UniqueVisitorCount int64 `json:"unique_visitor_count"`
Status2xxCount int64 `json:"status_2xx_count"`
Status4xxCount int64 `json:"status_4xx_count"`
Status5xxCount int64 `json:"status_5xx_count"`
}
// CapacityTrendPoint is a capacity trend bucket.
@@ -303,9 +306,10 @@ func BuildNodeTrends(
}
}
// BuildTrafficTrendPointsFromAccessLogs builds 24h request/error buckets from access logs.
// Prefers of_access_log_hourly when available; falls back to raw bucket aggregates.
// UniqueVisitorCount on hourly path is 0 (use TrafficSummary for exact UV).
// BuildTrafficTrendPointsFromAccessLogs builds 24h request/error/status buckets from access logs.
// Uses raw bucket aggregates: the hourly rollup (of_access_log_hourly) has no per-status counts,
// and the 24h window on the dashboard is cached, so the raw scan is acceptable.
// UniqueVisitorCount from buckets is exact (uniqExact on raw); TrafficSummary is used elsewhere for UV.
func BuildTrafficTrendPointsFromAccessLogs(ctx context.Context, now time.Time, nodeID string, since time.Time) []TrafficTrendPoint {
start := trendWindowStart(now)
points := make([]TrafficTrendPoint, observabilityTrendBuckets)
@@ -313,22 +317,6 @@ func BuildTrafficTrendPointsFromAccessLogs(ctx context.Context, now time.Time, n
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
}
if hourly, err := repository.ListOpenFlareTrafficHourlySince(ctx, nodeID, since); err == nil && len(hourly) > 0 {
for _, row := range hourly {
if row == nil {
continue
}
index, ok := trendBucketIndex(row.Hour, start)
if !ok {
continue
}
points[index].RequestCount += row.RequestCount
points[index].ErrorCount += row.ErrorCount
// UniqueVisitorCount intentionally not summed from hourly rollup (always 0 / overcounts).
}
return points
}
buckets, err := repository.ListOpenFlareAccessLogBuckets(ctx, model.OpenFlareAccessLogBucketQuery{
NodeID: nodeID,
Since: since,
@@ -353,6 +341,9 @@ func BuildTrafficTrendPointsFromAccessLogs(ctx context.Context, now time.Time, n
points[index].RequestCount = row.RequestCount
points[index].ErrorCount = row.ServerErrorCount
points[index].UniqueVisitorCount = row.UniqueIPCount
points[index].Status2xxCount = row.Status2xxCount
points[index].Status4xxCount = row.Status4xxCount
points[index].Status5xxCount = row.Status5xxCount
}
}
return points