From 26057514a10fce41d9904af476e57fd66bc3f86a Mon Sep 17 00:00:00 2001 From: ryan Date: Sat, 18 Jul 2026 13:17:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(obs):=20=E5=8E=BB=E6=8E=89=E5=AE=BF?= =?UTF-8?q?=E4=B8=BB=E6=9C=BA=E7=BD=91=E5=8D=A1=E8=B6=8B=E5=8A=BF=EF=BC=8C?= =?UTF-8?q?=E7=A3=81=E7=9B=98=E8=AF=BB=E5=86=99=E6=94=B9=E6=8C=89=E9=80=9F?= =?UTF-8?q?=E7=8E=87=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent 不再采集网卡累计字节,看板与节点网络图仅保留访问日志已提供/接收。 磁盘 IO 按小时换算为 B/s 曲线,摘要为近 24 小时平均速率,并同步 Swagger。 --- docs/changelog/index.md | 2 + docs/docs.go | 24 ----- docs/swagger.json | 24 ----- docs/swagger.yaml | 16 --- .../dashboard/network-disk-trend-chart.tsx | 61 +++++------ .../nodes/components/disk-io-trend-chart.tsx | 37 ++++--- .../nodes/components/network-trend-chart.tsx | 14 +-- .../nodes/components/node-observability.tsx | 11 +- frontend/components/data/trend-chart.tsx | 6 +- .../services/openflare/dashboard.service.ts | 8 +- frontend/lib/services/openflare/types.ts | 14 +-- .../apps/agent/observability/collector.go | 2 +- .../apps/openflare/agent/observability.go | 3 +- internal/apps/openflare/dashboard/logics.go | 5 +- .../apps/openflare/dashboard/logics_test.go | 2 +- .../apps/openflare/observability/analytics.go | 100 ++---------------- .../openflare/observability/analytics_test.go | 23 ++-- internal/apps/openflare/relay/logics_test.go | 4 +- .../apps/relay/observability/collector.go | 3 +- pkg/protocol/agent.go | 6 +- 20 files changed, 87 insertions(+), 278 deletions(-) diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 7864a72b..3bd2ab2a 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -44,6 +44,8 @@ sidebar: false - Agent 二进制替换升级时,若本地观测补传缓冲仍是旧格式或损坏,则直接删除该文件并在运行中重建,避免半迁移数据或阻塞心跳。 - Zone 流量图:顶部独立访客为整窗去重;曲线标明为分桶 UV(桶内去重,不可跨桶相加)。 - 明确 OpenResty 健康权威:当前 status/message 以节点表为准,ClickHouse 仅存 status 与连接时序(不含 message)。 +- 看板与节点观测不再展示或采集宿主机网卡入/出站;网络趋势仅保留访问日志的已提供/接收数据。 +- 宿主机磁盘读/写趋势改为按小时速率(B/s)展示,摘要为近 24 小时平均速率。 - Pages 部署包上传体积限制改为系统动态配置,默认仍为 100 MiB,可按环境调整。 - 上传新部署后会按保留策略自动清理超出数量的历史部署包,减少磁盘占用。 - Agent 在同步 Pages 部署时信任控制面已完成的包校验,不再重复限制文件数与展开体积,仅校验下载完整性并安全解压到本地。 diff --git a/docs/docs.go b/docs/docs.go index 64cbd786..e8a11a8b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -14308,12 +14308,6 @@ const docTemplate = `{ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "storage_total_bytes": { "type": "integer" }, @@ -16390,12 +16384,6 @@ const docTemplate = `{ "description": "sum(request_length)", "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "reported_nodes": { "type": "integer" } @@ -16439,12 +16427,6 @@ const docTemplate = `{ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "node_id": { "type": "string" }, @@ -17049,12 +17031,6 @@ const docTemplate = `{ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "storage_total_bytes": { "type": "integer" }, diff --git a/docs/swagger.json b/docs/swagger.json index 8f928898..c370a52f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -14301,12 +14301,6 @@ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "storage_total_bytes": { "type": "integer" }, @@ -16383,12 +16377,6 @@ "description": "sum(request_length)", "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "reported_nodes": { "type": "integer" } @@ -16432,12 +16420,6 @@ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "node_id": { "type": "string" }, @@ -17042,12 +17024,6 @@ "memory_used_bytes": { "type": "integer" }, - "network_rx_bytes": { - "type": "integer" - }, - "network_tx_bytes": { - "type": "integer" - }, "storage_total_bytes": { "type": "integer" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8c14e1bf..5020e0dd 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -714,10 +714,6 @@ definitions: type: integer memory_used_bytes: type: integer - network_rx_bytes: - type: integer - network_tx_bytes: - type: integer storage_total_bytes: type: integer storage_used_bytes: @@ -2096,10 +2092,6 @@ definitions: bytes_received: description: sum(request_length) type: integer - network_rx_bytes: - type: integer - network_tx_bytes: - type: integer reported_nodes: type: integer type: object @@ -2128,10 +2120,6 @@ definitions: type: integer memory_used_bytes: type: integer - network_rx_bytes: - type: integer - network_tx_bytes: - type: integer node_id: type: string openresty_connections: @@ -2527,10 +2515,6 @@ definitions: type: integer memory_used_bytes: type: integer - network_rx_bytes: - type: integer - network_tx_bytes: - type: integer storage_total_bytes: type: integer storage_used_bytes: diff --git a/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx b/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx index 912e3bb9..3e04c47f 100644 --- a/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx +++ b/frontend/app/(main)/components/dashboard/network-disk-trend-chart.tsx @@ -7,7 +7,22 @@ import type { NetworkTrendPoint, } from '@/lib/services/openflare'; -import { formatBytes, formatTrendHour } from './dashboard-utils'; +import { + formatBytes, + formatBytesPerSecond, + 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({ networkPoints, @@ -50,54 +65,32 @@ export function NetworkDiskTrendChart({ ]} /> - - formatTrendHour(point.bucket_started_at), - )} - height={180} - summaryScope='total' - summaryHint='近 24 小时 · 宿主机网卡' - yAxisValueFormatter={formatBytes} - series={[ - { - label: '宿主机网卡入站', - color: '#a3e635', - fillColor: 'rgba(163, 230, 53, 0.12)', - variant: 'area', - values: networkPoints.map((point) => point.network_rx_bytes), - valueFormatter: formatBytes, - }, - { - label: '宿主机网卡出站', - color: '#f97316', - values: networkPoints.map((point) => point.network_tx_bytes), - valueFormatter: formatBytes, - }, - ]} - /> - formatTrendHour(point.bucket_started_at), )} height={180} - summaryScope='total' - summaryHint='近 24 小时 · 宿主机磁盘' - yAxisValueFormatter={formatBytes} + summaryScope='average' + summaryHint='近 24 小时 · 宿主机磁盘 · 平均速率' + yAxisValueFormatter={formatDiskRate} series={[ { label: '磁盘读', color: '#a78bfa', fillColor: 'rgba(167, 139, 250, 0.14)', variant: 'area', - values: diskPoints.map((point) => point.disk_read_bytes), - valueFormatter: formatBytes, + values: diskPoints.map((point) => + diskBytesToRate(point.disk_read_bytes), + ), + valueFormatter: formatDiskRate, }, { label: '磁盘写', color: '#fb7185', - values: diskPoints.map((point) => point.disk_write_bytes), - valueFormatter: formatBytes, + values: diskPoints.map((point) => + diskBytesToRate(point.disk_write_bytes), + ), + valueFormatter: formatDiskRate, }, ]} /> diff --git a/frontend/app/(main)/nodes/components/disk-io-trend-chart.tsx b/frontend/app/(main)/nodes/components/disk-io-trend-chart.tsx index 78af3098..caf6b398 100644 --- a/frontend/app/(main)/nodes/components/disk-io-trend-chart.tsx +++ b/frontend/app/(main)/nodes/components/disk-io-trend-chart.tsx @@ -9,16 +9,25 @@ import { CardTitle, } from '@/components/ui/card'; import type { DiskIOTrendPoint } from '@/lib/services/openflare'; +import { formatBytesPerSecond } from '@/lib/utils/metrics'; -import { - formatBytes, - formatTrendHour, -} from '../../components/dashboard/dashboard-utils'; +import { formatTrendHour } from '../../components/dashboard/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 DiskIOTrendChart({ points, title = '24 小时磁盘 IO 趋势', - description = '观察磁盘读写变化,辅助判断日志放大、缓存抖动或磁盘压力。', + description = '按小时展示磁盘读写速率(B/s),辅助判断日志放大、缓存抖动或磁盘压力。', }: { points: DiskIOTrendPoint[]; title?: string; @@ -35,23 +44,27 @@ export function DiskIOTrendChart({ labels={points.map((point) => formatTrendHour(point.bucket_started_at), )} - summaryScope='total' - summaryHint='近 24 小时' - yAxisValueFormatter={formatBytes} + summaryScope='average' + summaryHint='近 24 小时 · 平均速率' + yAxisValueFormatter={formatDiskRate} series={[ { label: '磁盘读', color: '#a78bfa', fillColor: 'rgba(167, 139, 250, 0.14)', variant: 'area', - values: points.map((point) => point.disk_read_bytes), - valueFormatter: formatBytes, + values: points.map((point) => + diskBytesToRate(point.disk_read_bytes), + ), + valueFormatter: formatDiskRate, }, { label: '磁盘写', color: '#fb7185', - values: points.map((point) => point.disk_write_bytes), - valueFormatter: formatBytes, + values: points.map((point) => + diskBytesToRate(point.disk_write_bytes), + ), + valueFormatter: formatDiskRate, }, ]} /> diff --git a/frontend/app/(main)/nodes/components/network-trend-chart.tsx b/frontend/app/(main)/nodes/components/network-trend-chart.tsx index 0e361783..34e34387 100644 --- a/frontend/app/(main)/nodes/components/network-trend-chart.tsx +++ b/frontend/app/(main)/nodes/components/network-trend-chart.tsx @@ -18,7 +18,7 @@ import { export function NetworkTrendChart({ points, title = '24 小时网络趋势', - description = '按小时展示已提供/接收数据(访问日志)与宿主机网卡;摘要为近 24 小时总量。', + description = '按小时展示已提供/接收数据(访问日志);摘要为近 24 小时总量。', }: { points: NetworkTrendPoint[]; title?: string; @@ -53,18 +53,6 @@ export function NetworkTrendChart({ values: points.map((point) => point.bytes_provided), valueFormatter: formatBytes, }, - { - label: '宿主机网卡入站', - color: '#a3e635', - values: points.map((point) => point.network_rx_bytes), - valueFormatter: formatBytes, - }, - { - label: '宿主机网卡出站', - color: '#f97316', - values: points.map((point) => point.network_tx_bytes), - valueFormatter: formatBytes, - }, ]} /> diff --git a/frontend/app/(main)/nodes/components/node-observability.tsx b/frontend/app/(main)/nodes/components/node-observability.tsx index 45ecf4fb..11c7e616 100644 --- a/frontend/app/(main)/nodes/components/node-observability.tsx +++ b/frontend/app/(main)/nodes/components/node-observability.tsx @@ -717,7 +717,7 @@ export function NodeObservability({ ) : null}
-
+

OpenResty 连接

@@ -731,15 +731,6 @@ export function NodeObservability({

-
-

- 节点网络 -

-
-

入站:{formatBytes(latestMetric.network_rx_bytes)}

-

出站:{formatBytes(latestMetric.network_tx_bytes)}

-
-
diff --git a/frontend/components/data/trend-chart.tsx b/frontend/components/data/trend-chart.tsx index 6c755c49..12b9c636 100644 --- a/frontend/components/data/trend-chart.tsx +++ b/frontend/components/data/trend-chart.tsx @@ -15,7 +15,7 @@ type TrendChartSeries = { valueFormatter?: (value: number) => string; }; -type TrendChartSummaryScope = 'last-point' | 'total'; +type TrendChartSummaryScope = 'last-point' | 'total' | 'average'; type TrendChartProps = { labels: string[]; @@ -43,6 +43,10 @@ function resolveSummaryValue(values: number[], scope: TrendChartSummaryScope) { if (scope === 'total') { return values.reduce((sum, value) => sum + value, 0); } + if (scope === 'average') { + const sum = values.reduce((acc, value) => acc + value, 0); + return sum / values.length; + } return values[values.length - 1] ?? 0; } diff --git a/frontend/lib/services/openflare/dashboard.service.ts b/frontend/lib/services/openflare/dashboard.service.ts index 2dba8819..50c9d619 100644 --- a/frontend/lib/services/openflare/dashboard.service.ts +++ b/frontend/lib/services/openflare/dashboard.service.ts @@ -128,11 +128,9 @@ function normalizeNetworkTrendPoints( isCompactNetworkTrendPoint(item) ? { bucket_started_at: String(item[0] ?? ''), - network_rx_bytes: Number(item[1] ?? 0), - network_tx_bytes: Number(item[2] ?? 0), - bytes_received: Number(item[3] ?? 0), - bytes_provided: Number(item[4] ?? 0), - reported_nodes: Number(item[5] ?? 0), + bytes_received: Number(item[1] ?? 0), + bytes_provided: Number(item[2] ?? 0), + reported_nodes: Number(item[3] ?? 0), } : { ...item, diff --git a/frontend/lib/services/openflare/types.ts b/frontend/lib/services/openflare/types.ts index 589e302a..a76bddde 100644 --- a/frontend/lib/services/openflare/types.ts +++ b/frontend/lib/services/openflare/types.ts @@ -120,8 +120,6 @@ export interface NodeMetricSnapshot { storage_total_bytes: number; disk_read_bytes: number; disk_write_bytes: number; - network_rx_bytes: number; - network_tx_bytes: number; openresty_connections: number; } @@ -924,8 +922,6 @@ export interface CapacityTrendPoint { export interface NetworkTrendPoint { bucket_started_at: string; - network_rx_bytes: number; - network_tx_bytes: number; /** L1 接收数据 sum(request_length) */ bytes_received: number; /** L1 已提供数据 sum(bytes_sent) */ @@ -985,14 +981,8 @@ export interface DashboardOverview { export type CompactTrafficTrendPoint = [string, number, number, number]; export type CompactCapacityTrendPoint = [string, number, number, number]; -export type CompactNetworkTrendPoint = [ - string, - number, - number, - number, - number, - number, -]; +/** [bucket, bytes_received, bytes_provided, reported_nodes] */ +export type CompactNetworkTrendPoint = [string, number, number, number]; export type CompactDiskIOTrendPoint = [string, number, number, number]; export type CompactDashboardNodeHealth = [ number, diff --git a/internal/apps/agent/observability/collector.go b/internal/apps/agent/observability/collector.go index 60d1bbaf..1bf1e4fd 100644 --- a/internal/apps/agent/observability/collector.go +++ b/internal/apps/agent/observability/collector.go @@ -57,7 +57,7 @@ func BuildSnapshot(cfg *config.Config, stateStore *state.Store) *protocol.NodeMe metric.StorageTotalBytes = storageTotal metric.StorageUsedBytes = storageUsed - metric.NetworkRxBytes, metric.NetworkTxBytes = edgeobs.ReadLinuxNetworkTotals() + // Host NIC totals are not collected (product no longer surfaces host NIC trends). metric.DiskReadBytes, metric.DiskWriteBytes = edgeobs.ReadLinuxDiskTotals() if stateStore == nil { diff --git a/internal/apps/openflare/agent/observability.go b/internal/apps/openflare/agent/observability.go index 9a78490a..65f8341b 100644 --- a/internal/apps/openflare/agent/observability.go +++ b/internal/apps/openflare/agent/observability.go @@ -164,8 +164,7 @@ func persistNodeMetricSnapshot(ctx context.Context, nodeID string, snapshot *Nod StorageTotalBytes: snapshot.StorageTotalBytes, DiskReadBytes: snapshot.DiskReadBytes, DiskWriteBytes: snapshot.DiskWriteBytes, - NetworkRxBytes: snapshot.NetworkRxBytes, - NetworkTxBytes: snapshot.NetworkTxBytes, + // NetworkRx/Tx no longer collected from agents; CH columns remain 0. } return model.InsertOpenFlareMetricSnapshot(ctx, record) } diff --git a/internal/apps/openflare/dashboard/logics.go b/internal/apps/openflare/dashboard/logics.go index eac9987e..bd2934ad 100644 --- a/internal/apps/openflare/dashboard/logics.go +++ b/internal/apps/openflare/dashboard/logics.go @@ -354,12 +354,9 @@ func compressCapacityTrendPoints(points []observability.CapacityTrendPoint) [][] func compressNetworkTrendPoints(points []observability.NetworkTrendPoint) [][]any { rows := make([][]any, 0, len(points)) for _, point := range points { - // Compact layout (stable positions): - // [0] bucket, [1] host_rx, [2] host_tx, [3] bytes_received, [4] bytes_provided, [5] reported_nodes + // Compact layout: [0] bucket, [1] bytes_received, [2] bytes_provided, [3] reported_nodes rows = append(rows, []any{ point.BucketStartedAt, - point.NetworkRxBytes, - point.NetworkTxBytes, point.BytesReceived, point.BytesProvided, point.ReportedNodes, diff --git a/internal/apps/openflare/dashboard/logics_test.go b/internal/apps/openflare/dashboard/logics_test.go index 3d1975e8..2bf744c2 100644 --- a/internal/apps/openflare/dashboard/logics_test.go +++ b/internal/apps/openflare/dashboard/logics_test.go @@ -149,7 +149,7 @@ func TestGetOverviewStructure(t *testing.T) { require.Len(t, row, 4) } for _, row := range overview.Trends.Network24h { - require.Len(t, row, 6) + require.Len(t, row, 4) } for _, row := range overview.Trends.DiskIO24h { require.Len(t, row, 4) diff --git a/internal/apps/openflare/observability/analytics.go b/internal/apps/openflare/observability/analytics.go index b9c526fe..e35355de 100644 --- a/internal/apps/openflare/observability/analytics.go +++ b/internal/apps/openflare/observability/analytics.go @@ -51,8 +51,6 @@ type NodeMetricSnapshotView struct { StorageTotalBytes int64 `json:"storage_total_bytes"` DiskReadBytes int64 `json:"disk_read_bytes"` DiskWriteBytes int64 `json:"disk_write_bytes"` - NetworkRxBytes int64 `json:"network_rx_bytes"` - NetworkTxBytes int64 `json:"network_tx_bytes"` OpenrestyConnections int64 `json:"openresty_connections"` } @@ -95,13 +93,10 @@ type CapacityTrendPoint struct { ReportedNodes int `json:"reported_nodes"` } -// NetworkTrendPoint is a network trend bucket. -// Host network_* is L3 (宿主机网卡). -// bytes_received/provided are L1 business bytes from access logs. +// NetworkTrendPoint is a business-byte trend bucket from access logs (L1). +// Host NIC trends are intentionally not exposed. type NetworkTrendPoint struct { BucketStartedAt time.Time `json:"bucket_started_at"` - NetworkRxBytes int64 `json:"network_rx_bytes"` - NetworkTxBytes int64 `json:"network_tx_bytes"` BytesReceived int64 `json:"bytes_received"` // sum(request_length) BytesProvided int64 `json:"bytes_provided"` // sum(bytes_sent) ReportedNodes int `json:"reported_nodes"` @@ -135,11 +130,6 @@ type diskCounterState struct { seen bool } -type networkCounterState struct { - rx int64 - tx int64 - seen bool -} func buildTrafficWindowSummaryFromAccessLogs( ctx context.Context, @@ -194,8 +184,6 @@ func BuildMetricSnapshotViews( StorageTotalBytes: snapshot.StorageTotalBytes, DiskReadBytes: snapshot.DiskReadBytes, DiskWriteBytes: snapshot.DiskWriteBytes, - NetworkRxBytes: snapshot.NetworkRxBytes, - NetworkTxBytes: snapshot.NetworkTxBytes, } if matched := matchEdgeHealth(snapshot.CapturedAt, edgeHealth); matched != nil { view.OpenrestyConnections = matched.Connections @@ -284,8 +272,8 @@ func buildHealthSummary( } // BuildNodeTrends builds 24h trend series. -// Business traffic (requests/errors/UV and provided/received bytes) comes from access logs. -// Host capacity/disk/network come from metric snapshots (hourly when available). +// Business traffic (requests/errors and provided/received bytes) comes from access logs. +// Host capacity/disk come from metric snapshots (hourly when available). Host NIC is not tracked. func BuildNodeTrends( ctx context.Context, now time.Time, @@ -296,8 +284,7 @@ func BuildNodeTrends( trafficTrend := BuildTrafficTrendPointsFromAccessLogs(ctx, now, nodeID, trendSince) capacityTrend := BuildCapacityTrendPoints(now, snapshots) - networkTrend := BuildNetworkTrendPoints(now, snapshots) - // Overlay L1 business bytes onto network points. + networkTrend := emptyNetworkTrendPoints(now) applyAccessLogBytesToNetworkTrend(ctx, now, nodeID, trendSince, networkTrend) diskIOTrend := BuildDiskIOTrendPoints(now, snapshots) @@ -305,8 +292,6 @@ func BuildNodeTrends( if metricErr == nil && len(metricHourly) > 0 { capacityTrend = BuildCapacityTrendPointsFromHourly(now, metricHourly) diskIOTrend = BuildDiskIOTrendPointsFromHourly(now, metricHourly) - networkTrend = BuildNetworkTrendPointsFromHourly(now, metricHourly) - applyAccessLogBytesToNetworkTrend(ctx, now, nodeID, trendSince, networkTrend) } return NodeTrends{ @@ -522,85 +507,12 @@ func BuildCapacityTrendPointsFromHourly(now time.Time, hourly []*model.OpenFlare return points } -// BuildNetworkTrendPoints builds 24h host-network trend buckets. -// Host network counters must be process-lifetime cumulative values; this function -// converts consecutive samples into deltas. -func BuildNetworkTrendPoints( - now time.Time, - snapshots []*model.OpenFlareMetricSnapshot, -) []NetworkTrendPoint { - start := trendWindowStart(now) - points := make([]NetworkTrendPoint, observabilityTrendBuckets) - accumulators := make([]snapshotTrendAccumulator, observabilityTrendBuckets) - for index := range points { - points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour) - accumulators[index].nodes = make(map[string]struct{}) - } - sort.Slice(snapshots, func(i int, j int) bool { - if snapshots[i].CapturedAt.Equal(snapshots[j].CapturedAt) { - return snapshots[i].NodeID < snapshots[j].NodeID - } - return snapshots[i].CapturedAt.Before(snapshots[j].CapturedAt) - }) - previousHostByNode := make(map[string]networkCounterState, len(snapshots)) - for _, snapshot := range snapshots { - if snapshot == nil { - continue - } - nodeKey := snapshot.NodeID - if nodeKey == "" { - nodeKey = unknownTrendNodeKey - } - previous := previousHostByNode[nodeKey] - previousHostByNode[nodeKey] = networkCounterState{ - rx: snapshot.NetworkRxBytes, - tx: snapshot.NetworkTxBytes, - seen: true, - } - if !previous.seen { - continue - } - index, ok := trendBucketIndex(snapshot.CapturedAt, start) - if !ok { - continue - } - points[index].NetworkRxBytes += nonNegativeDelta(snapshot.NetworkRxBytes, previous.rx) - points[index].NetworkTxBytes += nonNegativeDelta(snapshot.NetworkTxBytes, previous.tx) - if snapshot.NodeID != "" { - accumulators[index].nodes[snapshot.NodeID] = struct{}{} - } - } - for index := range points { - points[index].ReportedNodes = len(accumulators[index].nodes) - } - return points -} - -// BuildNetworkTrendPointsFromHourly builds 24h host-network trend buckets from metric hourly aggregates. -// Business bytes (已提供/接收) are applied separately via applyAccessLogBytesToNetworkTrend. -func BuildNetworkTrendPointsFromHourly( - now time.Time, - metricHourly []*model.OpenFlareMetricHourly, -) []NetworkTrendPoint { +func emptyNetworkTrendPoints(now time.Time) []NetworkTrendPoint { start := trendWindowStart(now) points := make([]NetworkTrendPoint, observabilityTrendBuckets) for index := range points { points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour) } - for _, row := range metricHourly { - if row == nil { - continue - } - index, ok := trendBucketIndex(row.Hour, start) - if !ok { - continue - } - points[index].NetworkRxBytes += row.NetworkRxBytes - points[index].NetworkTxBytes += row.NetworkTxBytes - if row.ReportedNodes > points[index].ReportedNodes { - points[index].ReportedNodes = row.ReportedNodes - } - } return points } diff --git a/internal/apps/openflare/observability/analytics_test.go b/internal/apps/openflare/observability/analytics_test.go index 6feb96f1..4b0e5d84 100644 --- a/internal/apps/openflare/observability/analytics_test.go +++ b/internal/apps/openflare/observability/analytics_test.go @@ -113,27 +113,16 @@ func TestBuildCapacityTrendPointsFromHourlyFillsBuckets(t *testing.T) { } } -func TestBuildNetworkTrendPointsUsesCounterDeltas(t *testing.T) { +func TestEmptyNetworkTrendPointsHas24Buckets(t *testing.T) { t.Parallel() now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC) - base := now.Truncate(time.Hour) - snapshots := []*model.OpenFlareMetricSnapshot{ - {NodeID: "n1", CapturedAt: base.Add(10 * time.Minute), NetworkRxBytes: 1000, NetworkTxBytes: 2000}, - {NodeID: "n1", CapturedAt: base.Add(20 * time.Minute), NetworkRxBytes: 1500, NetworkTxBytes: 2600}, + points := emptyNetworkTrendPoints(now) + if len(points) != observabilityTrendBuckets { + t.Fatalf("len(points) = %d, want %d", len(points), observabilityTrendBuckets) } - - points := BuildNetworkTrendPoints(now, snapshots) - current := points[len(points)-1] - if current.NetworkRxBytes != 500 { - t.Fatalf("network_rx_bytes = %d, want 500", current.NetworkRxBytes) - } - if current.NetworkTxBytes != 600 { - t.Fatalf("network_tx_bytes = %d, want 600", current.NetworkTxBytes) - } - if current.BytesReceived != 0 || current.BytesProvided != 0 { - t.Fatalf("business bytes should be 0 without access logs overlay, got received=%d provided=%d", - current.BytesReceived, current.BytesProvided) + if !points[0].BucketStartedAt.Before(points[len(points)-1].BucketStartedAt) { + t.Fatalf("bucket order invalid: first=%v last=%v", points[0].BucketStartedAt, points[len(points)-1].BucketStartedAt) } } diff --git a/internal/apps/openflare/relay/logics_test.go b/internal/apps/openflare/relay/logics_test.go index 6eea5fe9..08060e68 100644 --- a/internal/apps/openflare/relay/logics_test.go +++ b/internal/apps/openflare/relay/logics_test.go @@ -92,8 +92,8 @@ func TestHeartbeatPayloadBindingAndFrpsObservationInsert(t *testing.T) { Snapshot: &agent.NodeMetricSnapshot{ CapturedAtUnix: now.Unix(), CPUUsagePercent: 12.5, - NetworkRxBytes: 1024, - NetworkTxBytes: 2048, + DiskReadBytes: 100, + DiskWriteBytes: 200, }, HealthEvents: []agent.NodeHealthEvent{}, }) diff --git a/internal/apps/relay/observability/collector.go b/internal/apps/relay/observability/collector.go index 1a6a45d0..c314b401 100644 --- a/internal/apps/relay/observability/collector.go +++ b/internal/apps/relay/observability/collector.go @@ -48,9 +48,8 @@ func BuildSnapshot(cfg *config.Config, stateStore *state.Store) *service.AgentNo metric.MemoryTotalBytes, metric.MemoryUsedBytes = edgeobs.ReadMemInfo() metric.StorageTotalBytes, metric.StorageUsedBytes = edgeobs.StatFilesystem(cfg.DataDir) - metric.NetworkRxBytes, metric.NetworkTxBytes = edgeobs.ReadLinuxNetworkTotals() + // Host NIC totals are not collected. metric.DiskReadBytes, metric.DiskWriteBytes = edgeobs.ReadLinuxDiskTotals() - if stateStore == nil { return metric } diff --git a/pkg/protocol/agent.go b/pkg/protocol/agent.go index 330b9f56..01c38f99 100644 --- a/pkg/protocol/agent.go +++ b/pkg/protocol/agent.go @@ -128,10 +128,8 @@ type NodeMetricSnapshot struct { MemoryTotalBytes int64 `json:"memory_total_bytes"` StorageUsedBytes int64 `json:"storage_used_bytes"` StorageTotalBytes int64 `json:"storage_total_bytes"` - DiskReadBytes int64 `json:"disk_read_bytes"` - DiskWriteBytes int64 `json:"disk_write_bytes"` - NetworkRxBytes int64 `json:"network_rx_bytes"` - NetworkTxBytes int64 `json:"network_tx_bytes"` + DiskReadBytes int64 `json:"disk_read_bytes"` + DiskWriteBytes int64 `json:"disk_write_bytes"` } // NodeAccessLog is an access log entry from agent (L1 business fact).