diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 0f32ead8..50842ade 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -21,6 +21,7 @@ sidebar: false ### 新增 - 访问日志「日志明细」支持按 HTTP 状态码筛选:状态码下拉含常用 2xx/3xx/4xx/5xx 快捷选项,也可手动输入任意状态码,筛选条件贯穿 ClickHouse 与 PostgreSQL/SQLite 日志库。 - 访问日志「日志明细」支持自定义时间范围筛选:内置 shadcn 日期+时间选择器,可指定起止时间(RFC3339,需成对提供)。 +- 首页看板改版:24 小时请求趋势拆分展示请求总量与 2xx/4xx/5xx 状态码类请求量并独占一行;移除宿主机磁盘指标,24 小时容量趋势(CPU/内存)并入业务流量卡片展示。 ### 🛠 修复 - 修复首页「来源分布」卡片在 PostgreSQL/SQLite 日志库下无数据:聚合查询对空节点 ID 误拼 `node_id = ''` 恒空条件,现改为空节点 ID 表示全节点聚合(与 ClickHouse 语义一致),并同步过滤空白归属地。 diff --git a/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx b/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx similarity index 60% rename from frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx rename to frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx index 3e04c47f..25f0f394 100644 --- a/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx +++ b/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx @@ -3,39 +3,29 @@ import { TrendChart } from '@/components/data/trend-chart'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import type { - DiskIOTrendPoint, + CapacityTrendPoint, NetworkTrendPoint, } from '@/lib/services/openflare'; import { formatBytes, - formatBytesPerSecond, + formatPercent, formatTrendHour, } from './dashboard-utils'; -/** Backend disk points are per-hour totals; chart displays bytes/s within each hour. */ -const DISK_BUCKET_SECONDS = 3600; - -function diskBytesToRate(bytes: number) { - return bytes > 0 ? bytes / DISK_BUCKET_SECONDS : 0; -} - -function formatDiskRate(bytesPerSecond: number) { - return formatBytesPerSecond(bytesPerSecond, 1, { zeroText: '0 B' }); -} - -export function NetworkDiskTrendChart({ +/** 业务流量(来自访问日志)与容量趋势(节点 Agent 宿主机指标)合并展示。 */ +export function TrafficCapacityTrendChart({ networkPoints, - diskPoints, + capacityPoints, }: { networkPoints: NetworkTrendPoint[]; - diskPoints: DiskIOTrendPoint[]; + capacityPoints: CapacityTrendPoint[]; }) { return ( - 24 小时业务流量与宿主机磁盘 + 24 小时业务流量与容量趋势 @@ -66,31 +56,31 @@ export function NetworkDiskTrendChart({ /> + labels={capacityPoints.map((point) => formatTrendHour(point.bucket_started_at), )} height={180} summaryScope='average' - summaryHint='近 24 小时 · 宿主机磁盘 · 平均速率' - yAxisValueFormatter={formatDiskRate} + summaryHint='近 24 小时 · 宿主机容量 · 平均值' + yAxisValueFormatter={formatPercent} series={[ { - label: '磁盘读', - color: '#a78bfa', - fillColor: 'rgba(167, 139, 250, 0.14)', + label: '平均 CPU', + color: '#0f766e', + fillColor: 'rgba(15, 118, 110, 0.15)', variant: 'area', - values: diskPoints.map((point) => - diskBytesToRate(point.disk_read_bytes), + values: capacityPoints.map( + (point) => point.average_cpu_usage_percent, ), - valueFormatter: formatDiskRate, + valueFormatter: formatPercent, }, { - label: '磁盘写', - color: '#fb7185', - values: diskPoints.map((point) => - diskBytesToRate(point.disk_write_bytes), + label: '平均内存', + color: '#2563eb', + values: capacityPoints.map( + (point) => point.average_memory_usage_percent, ), - valueFormatter: formatDiskRate, + valueFormatter: formatPercent, }, ]} /> diff --git a/frontend/app/(main)/components/dashboard/traffic-trend-chart.tsx b/frontend/app/(main)/components/dashboard/traffic-trend-chart.tsx index 9349783d..069a0331 100644 --- a/frontend/app/(main)/components/dashboard/traffic-trend-chart.tsx +++ b/frontend/app/(main)/components/dashboard/traffic-trend-chart.tsx @@ -15,7 +15,7 @@ import { formatTrendHour } from './dashboard-utils'; export function TrafficTrendChart({ points, title = '24 小时请求趋势', - description = '观察整体请求量和错误量是否出现异常抬升。', + description = '按小时拆分请求总量与 2xx/4xx/5xx 状态码请求量,判断各状态是否异常抬升。', }: { points: TrafficTrendPoint[]; title?: string; @@ -43,9 +43,19 @@ export function TrafficTrendChart({ values: points.map((point) => point.request_count), }, { - label: '错误量', + label: '2xx 请求', + color: '#22c55e', + values: points.map((point) => point.status_2xx_count), + }, + { + label: '4xx 请求', + color: '#f97316', + values: points.map((point) => point.status_4xx_count), + }, + { + label: '5xx 请求', color: '#ef4444', - values: points.map((point) => point.error_count), + values: points.map((point) => point.status_5xx_count), }, ]} /> diff --git a/frontend/app/(main)/page.tsx b/frontend/app/(main)/page.tsx index f306f3c3..eab2bca0 100644 --- a/frontend/app/(main)/page.tsx +++ b/frontend/app/(main)/page.tsx @@ -1,114 +1,112 @@ 'use client'; -import { useQuery } from '@tanstack/react-query'; -import { LayoutDashboard, RefreshCw } from 'lucide-react'; +import {useQuery} from '@tanstack/react-query'; +import {LayoutDashboard, RefreshCw} from 'lucide-react'; -import { EmptyStateWithBorder } from '@/components/layout/empty'; -import { ErrorInline } from '@/components/layout/error'; -import { LoadingStateWithBorder } from '@/components/layout/loading'; -import { Button } from '@/components/ui/button'; -import { DashboardService } from '@/lib/services/openflare'; -import { formatDateTime } from '@/lib/utils'; +import {EmptyStateWithBorder} from '@/components/layout/empty'; +import {ErrorInline} from '@/components/layout/error'; +import {LoadingStateWithBorder} from '@/components/layout/loading'; +import {Button} from '@/components/ui/button'; +import {DashboardService} from '@/lib/services/openflare'; +import {formatDateTime} from '@/lib/utils'; -import { CapacityTrendChart } from './components/dashboard/capacity-trend-chart'; import { - SourceDistributionChart, - StatusCodeDistributionChart, - TopDomainChart, + SourceDistributionChart, + StatusCodeDistributionChart, + TopDomainChart, } from './components/dashboard/distribution-rank-charts'; -import { NetworkDiskTrendChart } from './components/dashboard/network-disk-trend-chart'; -import { NodeHealthTable } from './components/dashboard/node-health-table'; -import { TrafficTrendChart } from './components/dashboard/traffic-trend-chart'; -import { WorldStage } from './components/dashboard/world-stage'; -import { getErrorMessage } from './nodes/components/node-utils'; +import {NodeHealthTable} from './components/dashboard/node-health-table'; +import {TrafficCapacityTrendChart} from './components/dashboard/traffic-capacity-trend-chart'; +import {TrafficTrendChart} from './components/dashboard/traffic-trend-chart'; +import {WorldStage} from './components/dashboard/world-stage'; +import {getErrorMessage} from './nodes/components/node-utils'; const dashboardQueryKey = ['openflare', 'dashboard', 'overview']; export default function OpenFlareDashboardPage() { - const overviewQuery = useQuery({ - queryKey: dashboardQueryKey, - queryFn: () => DashboardService.getOverview(), - refetchInterval: 60_000, - }); + const overviewQuery = useQuery({ + queryKey: dashboardQueryKey, + queryFn: () => DashboardService.getOverview(), + refetchInterval: 60_000, + }); - const overview = overviewQuery.data; + const overview = overviewQuery.data; - return ( -
-
-
- -

总览

+ return ( +
+
+
+ +

总览

+
+
+ {overview?.generated_at ? ( + 数据生成于 {formatDateTime(overview.generated_at)} + ) : null} + +
+
+ + {overviewQuery.isLoading ? ( + + ) : overviewQuery.isError ? ( + overviewQuery.refetch()} + /> + ) : !overview ? ( + + ) : ( + <> + + +
+ +
+ +
+ + + +
+ +
+ +
+ + + + )}
-
- {overview?.generated_at ? ( - 数据生成于 {formatDateTime(overview.generated_at)} - ) : null} - -
-
- - {overviewQuery.isLoading ? ( - - ) : overviewQuery.isError ? ( - overviewQuery.refetch()} - /> - ) : !overview ? ( - - ) : ( - <> - - -
- - -
- -
- -
- -
- - - -
- - - - )} -
- ); + ); } diff --git a/frontend/lib/services/openflare/dashboard.service.ts b/frontend/lib/services/openflare/dashboard.service.ts index b65d99c9..391c5a10 100644 --- a/frontend/lib/services/openflare/dashboard.service.ts +++ b/frontend/lib/services/openflare/dashboard.service.ts @@ -89,6 +89,9 @@ function normalizeTrafficTrendPoints( request_count: Number(item[1] ?? 0), error_count: Number(item[2] ?? 0), unique_visitor_count: Number(item[3] ?? 0), + status_2xx_count: Number(item[4] ?? 0), + status_4xx_count: Number(item[5] ?? 0), + status_5xx_count: Number(item[6] ?? 0), } : item, ); diff --git a/frontend/lib/services/openflare/types.ts b/frontend/lib/services/openflare/types.ts index 960b0c48..2395a343 100644 --- a/frontend/lib/services/openflare/types.ts +++ b/frontend/lib/services/openflare/types.ts @@ -1159,6 +1159,9 @@ export interface TrafficTrendPoint { request_count: number; error_count: number; unique_visitor_count: number; + status_2xx_count: number; + status_4xx_count: number; + status_5xx_count: number; } export interface CapacityTrendPoint { @@ -1227,7 +1230,15 @@ export interface DashboardOverview { nodes: DashboardNodeHealth[]; } -export type CompactTrafficTrendPoint = [string, number, number, number]; +export type CompactTrafficTrendPoint = [ + string, + number, + number, + number, + number, + number, + number, +]; export type CompactCapacityTrendPoint = [string, number, number, number]; /** [bucket, bytes_received, bytes_provided, reported_nodes] */ export type CompactNetworkTrendPoint = [string, number, number, number]; diff --git a/internal/apps/openflare/dashboard/logics.go b/internal/apps/openflare/dashboard/logics.go index cadcba89..2afae163 100644 --- a/internal/apps/openflare/dashboard/logics.go +++ b/internal/apps/openflare/dashboard/logics.go @@ -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 diff --git a/internal/apps/openflare/dashboard/logics_test.go b/internal/apps/openflare/dashboard/logics_test.go index d54e2e41..1f43bf01 100644 --- a/internal/apps/openflare/dashboard/logics_test.go +++ b/internal/apps/openflare/dashboard/logics_test.go @@ -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) diff --git a/internal/apps/openflare/observability/analytics.go b/internal/apps/openflare/observability/analytics.go index c0c590ce..6299b772 100644 --- a/internal/apps/openflare/observability/analytics.go +++ b/internal/apps/openflare/observability/analytics.go @@ -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 diff --git a/internal/model/analytics/node_access_log_stats.go b/internal/model/analytics/node_access_log_stats.go index ad98ac29..6e3acc1d 100644 --- a/internal/model/analytics/node_access_log_stats.go +++ b/internal/model/analytics/node_access_log_stats.go @@ -10,6 +10,9 @@ type NodeAccessLogBucketAggregate struct { SuccessCount int64 `gorm:"column:success_count"` ClientErrorCount int64 `gorm:"column:client_error_count"` ServerErrorCount int64 `gorm:"column:server_error_count"` + Status2xxCount int64 `gorm:"column:status_2xx_count"` + Status4xxCount int64 `gorm:"column:status_4xx_count"` + Status5xxCount int64 `gorm:"column:status_5xx_count"` UniqueIPCount int64 `gorm:"column:unique_ip_count"` UniqueHostCount int64 `gorm:"column:unique_host_count"` BytesSent int64 `gorm:"column:bytes_sent"` diff --git a/internal/model/openflare_observability.go b/internal/model/openflare_observability.go index 82050813..59d325a8 100644 --- a/internal/model/openflare_observability.go +++ b/internal/model/openflare_observability.go @@ -198,6 +198,9 @@ type OpenFlareAccessLogBucketRow struct { SuccessCount int64 `json:"success_count"` ClientErrorCount int64 `json:"client_error_count"` ServerErrorCount int64 `json:"server_error_count"` + Status2xxCount int64 `json:"status_2xx_count"` + Status4xxCount int64 `json:"status_4xx_count"` + Status5xxCount int64 `json:"status_5xx_count"` BytesSent int64 `json:"bytes_sent"` RequestLength int64 `json:"request_length"` } diff --git a/internal/repository/analytics/node_access_log_stats.go b/internal/repository/analytics/node_access_log_stats.go index 82ba8c7d..8f6cceb7 100644 --- a/internal/repository/analytics/node_access_log_stats.go +++ b/internal/repository/analytics/node_access_log_stats.go @@ -46,6 +46,9 @@ SELECT countIf(status_code < 400) AS success_count, countIf(status_code >= 400 AND status_code < 500) AS client_error_count, countIf(status_code >= 500) AS server_error_count, + countIf(status_code >= 200 AND status_code < 300) AS status_2xx_count, + countIf(status_code >= 400 AND status_code < 500) AS status_4xx_count, + countIf(status_code >= 500) AS status_5xx_count, uniqExactIf(remote_addr, remote_addr != '') AS unique_ip_count, uniqExactIf(host, host != '') AS unique_host_count, sum(bytes_sent) AS bytes_sent, @@ -70,10 +73,10 @@ ORDER BY %s`, bucketExpr, tableName, clause, nodeAccessLogBucketOrderClause(filt var result []NodeAccessLogBucketAggregate for rows.Next() { var ( - bucketEpoch int64 - requestCount, successCount, clientErrorCount, serverErrorCount, uniqueIPCount, uniqueHostCount, bytesSent, requestLength uint64 + bucketEpoch int64 + requestCount, successCount, clientErrorCount, serverErrorCount, status2xxCount, status4xxCount, status5xxCount, uniqueIPCount, uniqueHostCount, bytesSent, requestLength uint64 ) - if err := rows.Scan(&bucketEpoch, &requestCount, &successCount, &clientErrorCount, &serverErrorCount, &uniqueIPCount, &uniqueHostCount, &bytesSent, &requestLength); err != nil { + if err := rows.Scan(&bucketEpoch, &requestCount, &successCount, &clientErrorCount, &serverErrorCount, &status2xxCount, &status4xxCount, &status5xxCount, &uniqueIPCount, &uniqueHostCount, &bytesSent, &requestLength); err != nil { return nil, fmt.Errorf("scan bucket aggregate row: %w", err) } result = append(result, NodeAccessLogBucketAggregate{ @@ -82,6 +85,9 @@ ORDER BY %s`, bucketExpr, tableName, clause, nodeAccessLogBucketOrderClause(filt SuccessCount: safeInt64Count(successCount), ClientErrorCount: safeInt64Count(clientErrorCount), ServerErrorCount: safeInt64Count(serverErrorCount), + Status2xxCount: safeInt64Count(status2xxCount), + Status4xxCount: safeInt64Count(status4xxCount), + Status5xxCount: safeInt64Count(status5xxCount), UniqueIPCount: safeInt64Count(uniqueIPCount), UniqueHostCount: safeInt64Count(uniqueHostCount), BytesSent: safeInt64Count(bytesSent), diff --git a/internal/repository/logstore/postgres_store.go b/internal/repository/logstore/postgres_store.go index 94c4d8ae..0ae1bdcf 100644 --- a/internal/repository/logstore/postgres_store.go +++ b/internal/repository/logstore/postgres_store.go @@ -307,6 +307,9 @@ func (s *gormLogStore) BucketAggregates(ctx context.Context, query model.OpenFla SuccessCount int64 ClientErrorCount int64 ServerErrorCount int64 + Status2xxCount int64 `gorm:"column:status_2xx_count"` + Status4xxCount int64 `gorm:"column:status_4xx_count"` + Status5xxCount int64 `gorm:"column:status_5xx_count"` UniqueIPCount int64 UniqueHostCount int64 BytesSent int64 @@ -322,6 +325,9 @@ func (s *gormLogStore) BucketAggregates(ctx context.Context, query model.OpenFla "COUNT(*) FILTER (WHERE status_code < 400) AS success_count, " + "COUNT(*) FILTER (WHERE status_code >= 400 AND status_code < 500) AS client_error_count, " + "COUNT(*) FILTER (WHERE status_code >= 500) AS server_error_count, " + + "COUNT(*) FILTER (WHERE status_code >= 200 AND status_code < 300) AS status_2xx_count, " + + "COUNT(*) FILTER (WHERE status_code >= 400 AND status_code < 500) AS status_4xx_count, " + + "COUNT(*) FILTER (WHERE status_code >= 500) AS status_5xx_count, " + distinctNonEmptyCountSQL(s.db, "remote_addr") + " AS unique_ip_count, " + distinctNonEmptyCountSQL(s.db, "host") + " AS unique_host_count, " + "COALESCE(SUM(bytes_sent),0) AS bytes_sent, " + @@ -338,6 +344,9 @@ func (s *gormLogStore) BucketAggregates(ctx context.Context, query model.OpenFla SuccessCount: r.SuccessCount, ClientErrorCount: r.ClientErrorCount, ServerErrorCount: r.ServerErrorCount, + Status2xxCount: r.Status2xxCount, + Status4xxCount: r.Status4xxCount, + Status5xxCount: r.Status5xxCount, UniqueIPCount: r.UniqueIPCount, UniqueHostCount: r.UniqueHostCount, BytesSent: r.BytesSent, diff --git a/internal/repository/logstore/postgres_store_test.go b/internal/repository/logstore/postgres_store_test.go index a1150f59..5f07e405 100644 --- a/internal/repository/logstore/postgres_store_test.go +++ b/internal/repository/logstore/postgres_store_test.go @@ -709,9 +709,9 @@ func TestGormBucketAggregatesFullFieldSet(t *testing.T) { rows := []analyticsmodel.NodeAccessLog{ {ID: 1, NodeID: "n1", LoggedAt: base, RemoteAddr: "1.1.1.1", Host: "a.example.com", StatusCode: 200, BytesSent: 100, RequestLength: 10}, {ID: 2, NodeID: "n1", LoggedAt: base.Add(time.Minute), RemoteAddr: "1.1.1.1", Host: "a.example.com", StatusCode: 301, BytesSent: 200, RequestLength: 20}, - {ID: 3, NodeID: "n1", LoggedAt: base.Add(2 * time.Minute), RemoteAddr: "2.2.2.2", Host: "b.example.com", StatusCode: 404, BytesSent: 300, RequestLength: 30}, + {ID: 3, NodeID: "n1", LoggedAt: base.Add(2 * time.Minute), RemoteAddr: "2.2.2.2", Host: "b.example.com", StatusCode: 400, BytesSent: 300, RequestLength: 30}, {ID: 4, NodeID: "n1", LoggedAt: base.Add(3 * time.Minute), RemoteAddr: "", Host: "b.example.com", StatusCode: 500, BytesSent: 400, RequestLength: 40}, - {ID: 5, NodeID: "n1", LoggedAt: base.Add(4 * time.Minute), RemoteAddr: "3.3.3.3", Host: "c.example.com", StatusCode: 502, BytesSent: 500, RequestLength: 50}, + {ID: 5, NodeID: "n1", LoggedAt: base.Add(4 * time.Minute), RemoteAddr: "3.3.3.3", Host: "c.example.com", StatusCode: 500, BytesSent: 500, RequestLength: 50}, {ID: 6, NodeID: "n2", LoggedAt: base.Add(time.Hour), RemoteAddr: "9.9.9.9", Host: "d.example.com", StatusCode: 200, BytesSent: 999, RequestLength: 99}, } if err := s.BatchInsertNodeAccessLogs(ctx, rows); err != nil { @@ -741,6 +741,10 @@ func TestGormBucketAggregatesFullFieldSet(t *testing.T) { if b.ServerErrorCount != 2 { t.Errorf("server_error_count = %d, want 2", b.ServerErrorCount) } + if b.Status2xxCount != 1 || b.Status4xxCount != 1 || b.Status5xxCount != 2 { + t.Errorf("status class counts = 2xx:%d 4xx:%d 5xx:%d, want 1/1/2 (200/301/400/500/500)", + b.Status2xxCount, b.Status4xxCount, b.Status5xxCount) + } if b.UniqueIPCount != 3 { t.Errorf("unique_ip_count = %d, want 3 (empty remote_addr excluded)", b.UniqueIPCount) } diff --git a/internal/repository/openflare_access_log.go b/internal/repository/openflare_access_log.go index 06111ac0..b5ac1ca1 100644 --- a/internal/repository/openflare_access_log.go +++ b/internal/repository/openflare_access_log.go @@ -258,6 +258,9 @@ func buildOpenFlareAccessLogBucketRows(ctx context.Context, query model.OpenFlar SuccessCount: partial.SuccessCount, ClientErrorCount: partial.ClientErrorCount, ServerErrorCount: partial.ServerErrorCount, + Status2xxCount: partial.Status2xxCount, + Status4xxCount: partial.Status4xxCount, + Status5xxCount: partial.Status5xxCount, BytesSent: partial.BytesSent, RequestLength: partial.RequestLength, })