mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 00:56:37 +08:00
feat(obs): 去掉宿主机网卡趋势,磁盘读写改按速率展示
Agent 不再采集网卡累计字节,看板与节点网络图仅保留访问日志已提供/接收。 磁盘 IO 按小时换算为 B/s 曲线,摘要为近 24 小时平均速率,并同步 Swagger。
This commit is contained in:
@@ -44,6 +44,8 @@ sidebar: false
|
|||||||
- Agent 二进制替换升级时,若本地观测补传缓冲仍是旧格式或损坏,则直接删除该文件并在运行中重建,避免半迁移数据或阻塞心跳。
|
- Agent 二进制替换升级时,若本地观测补传缓冲仍是旧格式或损坏,则直接删除该文件并在运行中重建,避免半迁移数据或阻塞心跳。
|
||||||
- Zone 流量图:顶部独立访客为整窗去重;曲线标明为分桶 UV(桶内去重,不可跨桶相加)。
|
- Zone 流量图:顶部独立访客为整窗去重;曲线标明为分桶 UV(桶内去重,不可跨桶相加)。
|
||||||
- 明确 OpenResty 健康权威:当前 status/message 以节点表为准,ClickHouse 仅存 status 与连接时序(不含 message)。
|
- 明确 OpenResty 健康权威:当前 status/message 以节点表为准,ClickHouse 仅存 status 与连接时序(不含 message)。
|
||||||
|
- 看板与节点观测不再展示或采集宿主机网卡入/出站;网络趋势仅保留访问日志的已提供/接收数据。
|
||||||
|
- 宿主机磁盘读/写趋势改为按小时速率(B/s)展示,摘要为近 24 小时平均速率。
|
||||||
- Pages 部署包上传体积限制改为系统动态配置,默认仍为 100 MiB,可按环境调整。
|
- Pages 部署包上传体积限制改为系统动态配置,默认仍为 100 MiB,可按环境调整。
|
||||||
- 上传新部署后会按保留策略自动清理超出数量的历史部署包,减少磁盘占用。
|
- 上传新部署后会按保留策略自动清理超出数量的历史部署包,减少磁盘占用。
|
||||||
- Agent 在同步 Pages 部署时信任控制面已完成的包校验,不再重复限制文件数与展开体积,仅校验下载完整性并安全解压到本地。
|
- Agent 在同步 Pages 部署时信任控制面已完成的包校验,不再重复限制文件数与展开体积,仅校验下载完整性并安全解压到本地。
|
||||||
|
|||||||
@@ -14308,12 +14308,6 @@ const docTemplate = `{
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"storage_total_bytes": {
|
"storage_total_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -16390,12 +16384,6 @@ const docTemplate = `{
|
|||||||
"description": "sum(request_length)",
|
"description": "sum(request_length)",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"reported_nodes": {
|
"reported_nodes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
@@ -16439,12 +16427,6 @@ const docTemplate = `{
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"node_id": {
|
"node_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -17049,12 +17031,6 @@ const docTemplate = `{
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"storage_total_bytes": {
|
"storage_total_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14301,12 +14301,6 @@
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"storage_total_bytes": {
|
"storage_total_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -16383,12 +16377,6 @@
|
|||||||
"description": "sum(request_length)",
|
"description": "sum(request_length)",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"reported_nodes": {
|
"reported_nodes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
@@ -16432,12 +16420,6 @@
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"node_id": {
|
"node_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -17042,12 +17024,6 @@
|
|||||||
"memory_used_bytes": {
|
"memory_used_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"network_rx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"network_tx_bytes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"storage_total_bytes": {
|
"storage_total_bytes": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -714,10 +714,6 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
memory_used_bytes:
|
memory_used_bytes:
|
||||||
type: integer
|
type: integer
|
||||||
network_rx_bytes:
|
|
||||||
type: integer
|
|
||||||
network_tx_bytes:
|
|
||||||
type: integer
|
|
||||||
storage_total_bytes:
|
storage_total_bytes:
|
||||||
type: integer
|
type: integer
|
||||||
storage_used_bytes:
|
storage_used_bytes:
|
||||||
@@ -2096,10 +2092,6 @@ definitions:
|
|||||||
bytes_received:
|
bytes_received:
|
||||||
description: sum(request_length)
|
description: sum(request_length)
|
||||||
type: integer
|
type: integer
|
||||||
network_rx_bytes:
|
|
||||||
type: integer
|
|
||||||
network_tx_bytes:
|
|
||||||
type: integer
|
|
||||||
reported_nodes:
|
reported_nodes:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
@@ -2128,10 +2120,6 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
memory_used_bytes:
|
memory_used_bytes:
|
||||||
type: integer
|
type: integer
|
||||||
network_rx_bytes:
|
|
||||||
type: integer
|
|
||||||
network_tx_bytes:
|
|
||||||
type: integer
|
|
||||||
node_id:
|
node_id:
|
||||||
type: string
|
type: string
|
||||||
openresty_connections:
|
openresty_connections:
|
||||||
@@ -2527,10 +2515,6 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
memory_used_bytes:
|
memory_used_bytes:
|
||||||
type: integer
|
type: integer
|
||||||
network_rx_bytes:
|
|
||||||
type: integer
|
|
||||||
network_tx_bytes:
|
|
||||||
type: integer
|
|
||||||
storage_total_bytes:
|
storage_total_bytes:
|
||||||
type: integer
|
type: integer
|
||||||
storage_used_bytes:
|
storage_used_bytes:
|
||||||
|
|||||||
@@ -7,7 +7,22 @@ import type {
|
|||||||
NetworkTrendPoint,
|
NetworkTrendPoint,
|
||||||
} from '@/lib/services/openflare';
|
} 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({
|
export function NetworkDiskTrendChart({
|
||||||
networkPoints,
|
networkPoints,
|
||||||
@@ -50,54 +65,32 @@ export function NetworkDiskTrendChart({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TrendChart
|
|
||||||
labels={networkPoints.map((point) =>
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TrendChart
|
<TrendChart
|
||||||
labels={diskPoints.map((point) =>
|
labels={diskPoints.map((point) =>
|
||||||
formatTrendHour(point.bucket_started_at),
|
formatTrendHour(point.bucket_started_at),
|
||||||
)}
|
)}
|
||||||
height={180}
|
height={180}
|
||||||
summaryScope='total'
|
summaryScope='average'
|
||||||
summaryHint='近 24 小时 · 宿主机磁盘'
|
summaryHint='近 24 小时 · 宿主机磁盘 · 平均速率'
|
||||||
yAxisValueFormatter={formatBytes}
|
yAxisValueFormatter={formatDiskRate}
|
||||||
series={[
|
series={[
|
||||||
{
|
{
|
||||||
label: '磁盘读',
|
label: '磁盘读',
|
||||||
color: '#a78bfa',
|
color: '#a78bfa',
|
||||||
fillColor: 'rgba(167, 139, 250, 0.14)',
|
fillColor: 'rgba(167, 139, 250, 0.14)',
|
||||||
variant: 'area',
|
variant: 'area',
|
||||||
values: diskPoints.map((point) => point.disk_read_bytes),
|
values: diskPoints.map((point) =>
|
||||||
valueFormatter: formatBytes,
|
diskBytesToRate(point.disk_read_bytes),
|
||||||
|
),
|
||||||
|
valueFormatter: formatDiskRate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '磁盘写',
|
label: '磁盘写',
|
||||||
color: '#fb7185',
|
color: '#fb7185',
|
||||||
values: diskPoints.map((point) => point.disk_write_bytes),
|
values: diskPoints.map((point) =>
|
||||||
valueFormatter: formatBytes,
|
diskBytesToRate(point.disk_write_bytes),
|
||||||
|
),
|
||||||
|
valueFormatter: formatDiskRate,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,16 +9,25 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
import type { DiskIOTrendPoint } from '@/lib/services/openflare';
|
import type { DiskIOTrendPoint } from '@/lib/services/openflare';
|
||||||
|
import { formatBytesPerSecond } from '@/lib/utils/metrics';
|
||||||
|
|
||||||
import {
|
import { formatTrendHour } from '../../components/dashboard/dashboard-utils';
|
||||||
formatBytes,
|
|
||||||
formatTrendHour,
|
/** Backend disk points are per-hour totals; chart displays bytes/s within each hour. */
|
||||||
} from '../../components/dashboard/dashboard-utils';
|
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({
|
export function DiskIOTrendChart({
|
||||||
points,
|
points,
|
||||||
title = '24 小时磁盘 IO 趋势',
|
title = '24 小时磁盘 IO 趋势',
|
||||||
description = '观察磁盘读写变化,辅助判断日志放大、缓存抖动或磁盘压力。',
|
description = '按小时展示磁盘读写速率(B/s),辅助判断日志放大、缓存抖动或磁盘压力。',
|
||||||
}: {
|
}: {
|
||||||
points: DiskIOTrendPoint[];
|
points: DiskIOTrendPoint[];
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -35,23 +44,27 @@ export function DiskIOTrendChart({
|
|||||||
labels={points.map((point) =>
|
labels={points.map((point) =>
|
||||||
formatTrendHour(point.bucket_started_at),
|
formatTrendHour(point.bucket_started_at),
|
||||||
)}
|
)}
|
||||||
summaryScope='total'
|
summaryScope='average'
|
||||||
summaryHint='近 24 小时'
|
summaryHint='近 24 小时 · 平均速率'
|
||||||
yAxisValueFormatter={formatBytes}
|
yAxisValueFormatter={formatDiskRate}
|
||||||
series={[
|
series={[
|
||||||
{
|
{
|
||||||
label: '磁盘读',
|
label: '磁盘读',
|
||||||
color: '#a78bfa',
|
color: '#a78bfa',
|
||||||
fillColor: 'rgba(167, 139, 250, 0.14)',
|
fillColor: 'rgba(167, 139, 250, 0.14)',
|
||||||
variant: 'area',
|
variant: 'area',
|
||||||
values: points.map((point) => point.disk_read_bytes),
|
values: points.map((point) =>
|
||||||
valueFormatter: formatBytes,
|
diskBytesToRate(point.disk_read_bytes),
|
||||||
|
),
|
||||||
|
valueFormatter: formatDiskRate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '磁盘写',
|
label: '磁盘写',
|
||||||
color: '#fb7185',
|
color: '#fb7185',
|
||||||
values: points.map((point) => point.disk_write_bytes),
|
values: points.map((point) =>
|
||||||
valueFormatter: formatBytes,
|
diskBytesToRate(point.disk_write_bytes),
|
||||||
|
),
|
||||||
|
valueFormatter: formatDiskRate,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
export function NetworkTrendChart({
|
export function NetworkTrendChart({
|
||||||
points,
|
points,
|
||||||
title = '24 小时网络趋势',
|
title = '24 小时网络趋势',
|
||||||
description = '按小时展示已提供/接收数据(访问日志)与宿主机网卡;摘要为近 24 小时总量。',
|
description = '按小时展示已提供/接收数据(访问日志);摘要为近 24 小时总量。',
|
||||||
}: {
|
}: {
|
||||||
points: NetworkTrendPoint[];
|
points: NetworkTrendPoint[];
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -53,18 +53,6 @@ export function NetworkTrendChart({
|
|||||||
values: points.map((point) => point.bytes_provided),
|
values: points.map((point) => point.bytes_provided),
|
||||||
valueFormatter: formatBytes,
|
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,
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ export function NodeObservability({
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className='grid gap-3 sm:grid-cols-2'>
|
<div className='grid gap-3 sm:grid-cols-2'>
|
||||||
<div className='rounded-lg border px-3 py-3'>
|
<div className='rounded-lg border px-3 py-3 sm:col-span-2'>
|
||||||
<p className='text-xs text-muted-foreground uppercase tracking-wide'>
|
<p className='text-xs text-muted-foreground uppercase tracking-wide'>
|
||||||
OpenResty 连接
|
OpenResty 连接
|
||||||
</p>
|
</p>
|
||||||
@@ -731,15 +731,6 @@ export function NodeObservability({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='rounded-lg border px-3 py-3'>
|
|
||||||
<p className='text-xs text-muted-foreground uppercase tracking-wide'>
|
|
||||||
节点网络
|
|
||||||
</p>
|
|
||||||
<div className='mt-3 space-y-2 text-sm text-muted-foreground'>
|
|
||||||
<p>入站:{formatBytes(latestMetric.network_rx_bytes)}</p>
|
|
||||||
<p>出站:{formatBytes(latestMetric.network_tx_bytes)}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='grid gap-3 sm:grid-cols-2'>
|
<div className='grid gap-3 sm:grid-cols-2'>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type TrendChartSeries = {
|
|||||||
valueFormatter?: (value: number) => string;
|
valueFormatter?: (value: number) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TrendChartSummaryScope = 'last-point' | 'total';
|
type TrendChartSummaryScope = 'last-point' | 'total' | 'average';
|
||||||
|
|
||||||
type TrendChartProps = {
|
type TrendChartProps = {
|
||||||
labels: string[];
|
labels: string[];
|
||||||
@@ -43,6 +43,10 @@ function resolveSummaryValue(values: number[], scope: TrendChartSummaryScope) {
|
|||||||
if (scope === 'total') {
|
if (scope === 'total') {
|
||||||
return values.reduce((sum, value) => sum + value, 0);
|
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;
|
return values[values.length - 1] ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,11 +128,9 @@ function normalizeNetworkTrendPoints(
|
|||||||
isCompactNetworkTrendPoint(item)
|
isCompactNetworkTrendPoint(item)
|
||||||
? {
|
? {
|
||||||
bucket_started_at: String(item[0] ?? ''),
|
bucket_started_at: String(item[0] ?? ''),
|
||||||
network_rx_bytes: Number(item[1] ?? 0),
|
bytes_received: Number(item[1] ?? 0),
|
||||||
network_tx_bytes: Number(item[2] ?? 0),
|
bytes_provided: Number(item[2] ?? 0),
|
||||||
bytes_received: Number(item[3] ?? 0),
|
reported_nodes: Number(item[3] ?? 0),
|
||||||
bytes_provided: Number(item[4] ?? 0),
|
|
||||||
reported_nodes: Number(item[5] ?? 0),
|
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
...item,
|
...item,
|
||||||
|
|||||||
@@ -120,8 +120,6 @@ export interface NodeMetricSnapshot {
|
|||||||
storage_total_bytes: number;
|
storage_total_bytes: number;
|
||||||
disk_read_bytes: number;
|
disk_read_bytes: number;
|
||||||
disk_write_bytes: number;
|
disk_write_bytes: number;
|
||||||
network_rx_bytes: number;
|
|
||||||
network_tx_bytes: number;
|
|
||||||
openresty_connections: number;
|
openresty_connections: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,8 +922,6 @@ export interface CapacityTrendPoint {
|
|||||||
|
|
||||||
export interface NetworkTrendPoint {
|
export interface NetworkTrendPoint {
|
||||||
bucket_started_at: string;
|
bucket_started_at: string;
|
||||||
network_rx_bytes: number;
|
|
||||||
network_tx_bytes: number;
|
|
||||||
/** L1 接收数据 sum(request_length) */
|
/** L1 接收数据 sum(request_length) */
|
||||||
bytes_received: number;
|
bytes_received: number;
|
||||||
/** L1 已提供数据 sum(bytes_sent) */
|
/** L1 已提供数据 sum(bytes_sent) */
|
||||||
@@ -985,14 +981,8 @@ export interface DashboardOverview {
|
|||||||
|
|
||||||
export type CompactTrafficTrendPoint = [string, number, number, number];
|
export type CompactTrafficTrendPoint = [string, number, number, number];
|
||||||
export type CompactCapacityTrendPoint = [string, number, number, number];
|
export type CompactCapacityTrendPoint = [string, number, number, number];
|
||||||
export type CompactNetworkTrendPoint = [
|
/** [bucket, bytes_received, bytes_provided, reported_nodes] */
|
||||||
string,
|
export type CompactNetworkTrendPoint = [string, number, number, number];
|
||||||
number,
|
|
||||||
number,
|
|
||||||
number,
|
|
||||||
number,
|
|
||||||
number,
|
|
||||||
];
|
|
||||||
export type CompactDiskIOTrendPoint = [string, number, number, number];
|
export type CompactDiskIOTrendPoint = [string, number, number, number];
|
||||||
export type CompactDashboardNodeHealth = [
|
export type CompactDashboardNodeHealth = [
|
||||||
number,
|
number,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func BuildSnapshot(cfg *config.Config, stateStore *state.Store) *protocol.NodeMe
|
|||||||
metric.StorageTotalBytes = storageTotal
|
metric.StorageTotalBytes = storageTotal
|
||||||
metric.StorageUsedBytes = storageUsed
|
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()
|
metric.DiskReadBytes, metric.DiskWriteBytes = edgeobs.ReadLinuxDiskTotals()
|
||||||
|
|
||||||
if stateStore == nil {
|
if stateStore == nil {
|
||||||
|
|||||||
@@ -164,8 +164,7 @@ func persistNodeMetricSnapshot(ctx context.Context, nodeID string, snapshot *Nod
|
|||||||
StorageTotalBytes: snapshot.StorageTotalBytes,
|
StorageTotalBytes: snapshot.StorageTotalBytes,
|
||||||
DiskReadBytes: snapshot.DiskReadBytes,
|
DiskReadBytes: snapshot.DiskReadBytes,
|
||||||
DiskWriteBytes: snapshot.DiskWriteBytes,
|
DiskWriteBytes: snapshot.DiskWriteBytes,
|
||||||
NetworkRxBytes: snapshot.NetworkRxBytes,
|
// NetworkRx/Tx no longer collected from agents; CH columns remain 0.
|
||||||
NetworkTxBytes: snapshot.NetworkTxBytes,
|
|
||||||
}
|
}
|
||||||
return model.InsertOpenFlareMetricSnapshot(ctx, record)
|
return model.InsertOpenFlareMetricSnapshot(ctx, record)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,12 +354,9 @@ func compressCapacityTrendPoints(points []observability.CapacityTrendPoint) [][]
|
|||||||
func compressNetworkTrendPoints(points []observability.NetworkTrendPoint) [][]any {
|
func compressNetworkTrendPoints(points []observability.NetworkTrendPoint) [][]any {
|
||||||
rows := make([][]any, 0, len(points))
|
rows := make([][]any, 0, len(points))
|
||||||
for _, point := range points {
|
for _, point := range points {
|
||||||
// Compact layout (stable positions):
|
// Compact layout: [0] bucket, [1] bytes_received, [2] bytes_provided, [3] reported_nodes
|
||||||
// [0] bucket, [1] host_rx, [2] host_tx, [3] bytes_received, [4] bytes_provided, [5] reported_nodes
|
|
||||||
rows = append(rows, []any{
|
rows = append(rows, []any{
|
||||||
point.BucketStartedAt,
|
point.BucketStartedAt,
|
||||||
point.NetworkRxBytes,
|
|
||||||
point.NetworkTxBytes,
|
|
||||||
point.BytesReceived,
|
point.BytesReceived,
|
||||||
point.BytesProvided,
|
point.BytesProvided,
|
||||||
point.ReportedNodes,
|
point.ReportedNodes,
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ func TestGetOverviewStructure(t *testing.T) {
|
|||||||
require.Len(t, row, 4)
|
require.Len(t, row, 4)
|
||||||
}
|
}
|
||||||
for _, row := range overview.Trends.Network24h {
|
for _, row := range overview.Trends.Network24h {
|
||||||
require.Len(t, row, 6)
|
require.Len(t, row, 4)
|
||||||
}
|
}
|
||||||
for _, row := range overview.Trends.DiskIO24h {
|
for _, row := range overview.Trends.DiskIO24h {
|
||||||
require.Len(t, row, 4)
|
require.Len(t, row, 4)
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ type NodeMetricSnapshotView struct {
|
|||||||
StorageTotalBytes int64 `json:"storage_total_bytes"`
|
StorageTotalBytes int64 `json:"storage_total_bytes"`
|
||||||
DiskReadBytes int64 `json:"disk_read_bytes"`
|
DiskReadBytes int64 `json:"disk_read_bytes"`
|
||||||
DiskWriteBytes int64 `json:"disk_write_bytes"`
|
DiskWriteBytes int64 `json:"disk_write_bytes"`
|
||||||
NetworkRxBytes int64 `json:"network_rx_bytes"`
|
|
||||||
NetworkTxBytes int64 `json:"network_tx_bytes"`
|
|
||||||
OpenrestyConnections int64 `json:"openresty_connections"`
|
OpenrestyConnections int64 `json:"openresty_connections"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,13 +93,10 @@ type CapacityTrendPoint struct {
|
|||||||
ReportedNodes int `json:"reported_nodes"`
|
ReportedNodes int `json:"reported_nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkTrendPoint is a network trend bucket.
|
// NetworkTrendPoint is a business-byte trend bucket from access logs (L1).
|
||||||
// Host network_* is L3 (宿主机网卡).
|
// Host NIC trends are intentionally not exposed.
|
||||||
// bytes_received/provided are L1 business bytes from access logs.
|
|
||||||
type NetworkTrendPoint struct {
|
type NetworkTrendPoint struct {
|
||||||
BucketStartedAt time.Time `json:"bucket_started_at"`
|
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)
|
BytesReceived int64 `json:"bytes_received"` // sum(request_length)
|
||||||
BytesProvided int64 `json:"bytes_provided"` // sum(bytes_sent)
|
BytesProvided int64 `json:"bytes_provided"` // sum(bytes_sent)
|
||||||
ReportedNodes int `json:"reported_nodes"`
|
ReportedNodes int `json:"reported_nodes"`
|
||||||
@@ -135,11 +130,6 @@ type diskCounterState struct {
|
|||||||
seen bool
|
seen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type networkCounterState struct {
|
|
||||||
rx int64
|
|
||||||
tx int64
|
|
||||||
seen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTrafficWindowSummaryFromAccessLogs(
|
func buildTrafficWindowSummaryFromAccessLogs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@@ -194,8 +184,6 @@ func BuildMetricSnapshotViews(
|
|||||||
StorageTotalBytes: snapshot.StorageTotalBytes,
|
StorageTotalBytes: snapshot.StorageTotalBytes,
|
||||||
DiskReadBytes: snapshot.DiskReadBytes,
|
DiskReadBytes: snapshot.DiskReadBytes,
|
||||||
DiskWriteBytes: snapshot.DiskWriteBytes,
|
DiskWriteBytes: snapshot.DiskWriteBytes,
|
||||||
NetworkRxBytes: snapshot.NetworkRxBytes,
|
|
||||||
NetworkTxBytes: snapshot.NetworkTxBytes,
|
|
||||||
}
|
}
|
||||||
if matched := matchEdgeHealth(snapshot.CapturedAt, edgeHealth); matched != nil {
|
if matched := matchEdgeHealth(snapshot.CapturedAt, edgeHealth); matched != nil {
|
||||||
view.OpenrestyConnections = matched.Connections
|
view.OpenrestyConnections = matched.Connections
|
||||||
@@ -284,8 +272,8 @@ func buildHealthSummary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildNodeTrends builds 24h trend series.
|
// BuildNodeTrends builds 24h trend series.
|
||||||
// Business traffic (requests/errors/UV and provided/received bytes) comes from access logs.
|
// Business traffic (requests/errors and provided/received bytes) comes from access logs.
|
||||||
// Host capacity/disk/network come from metric snapshots (hourly when available).
|
// Host capacity/disk come from metric snapshots (hourly when available). Host NIC is not tracked.
|
||||||
func BuildNodeTrends(
|
func BuildNodeTrends(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
now time.Time,
|
now time.Time,
|
||||||
@@ -296,8 +284,7 @@ func BuildNodeTrends(
|
|||||||
|
|
||||||
trafficTrend := BuildTrafficTrendPointsFromAccessLogs(ctx, now, nodeID, trendSince)
|
trafficTrend := BuildTrafficTrendPointsFromAccessLogs(ctx, now, nodeID, trendSince)
|
||||||
capacityTrend := BuildCapacityTrendPoints(now, snapshots)
|
capacityTrend := BuildCapacityTrendPoints(now, snapshots)
|
||||||
networkTrend := BuildNetworkTrendPoints(now, snapshots)
|
networkTrend := emptyNetworkTrendPoints(now)
|
||||||
// Overlay L1 business bytes onto network points.
|
|
||||||
applyAccessLogBytesToNetworkTrend(ctx, now, nodeID, trendSince, networkTrend)
|
applyAccessLogBytesToNetworkTrend(ctx, now, nodeID, trendSince, networkTrend)
|
||||||
diskIOTrend := BuildDiskIOTrendPoints(now, snapshots)
|
diskIOTrend := BuildDiskIOTrendPoints(now, snapshots)
|
||||||
|
|
||||||
@@ -305,8 +292,6 @@ func BuildNodeTrends(
|
|||||||
if metricErr == nil && len(metricHourly) > 0 {
|
if metricErr == nil && len(metricHourly) > 0 {
|
||||||
capacityTrend = BuildCapacityTrendPointsFromHourly(now, metricHourly)
|
capacityTrend = BuildCapacityTrendPointsFromHourly(now, metricHourly)
|
||||||
diskIOTrend = BuildDiskIOTrendPointsFromHourly(now, metricHourly)
|
diskIOTrend = BuildDiskIOTrendPointsFromHourly(now, metricHourly)
|
||||||
networkTrend = BuildNetworkTrendPointsFromHourly(now, metricHourly)
|
|
||||||
applyAccessLogBytesToNetworkTrend(ctx, now, nodeID, trendSince, networkTrend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NodeTrends{
|
return NodeTrends{
|
||||||
@@ -522,85 +507,12 @@ func BuildCapacityTrendPointsFromHourly(now time.Time, hourly []*model.OpenFlare
|
|||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildNetworkTrendPoints builds 24h host-network trend buckets.
|
func emptyNetworkTrendPoints(now time.Time) []NetworkTrendPoint {
|
||||||
// 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 {
|
|
||||||
start := trendWindowStart(now)
|
start := trendWindowStart(now)
|
||||||
points := make([]NetworkTrendPoint, observabilityTrendBuckets)
|
points := make([]NetworkTrendPoint, observabilityTrendBuckets)
|
||||||
for index := range points {
|
for index := range points {
|
||||||
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
|
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
|
return points
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,27 +113,16 @@ func TestBuildCapacityTrendPointsFromHourlyFillsBuckets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildNetworkTrendPointsUsesCounterDeltas(t *testing.T) {
|
func TestEmptyNetworkTrendPointsHas24Buckets(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC)
|
now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC)
|
||||||
base := now.Truncate(time.Hour)
|
points := emptyNetworkTrendPoints(now)
|
||||||
snapshots := []*model.OpenFlareMetricSnapshot{
|
if len(points) != observabilityTrendBuckets {
|
||||||
{NodeID: "n1", CapturedAt: base.Add(10 * time.Minute), NetworkRxBytes: 1000, NetworkTxBytes: 2000},
|
t.Fatalf("len(points) = %d, want %d", len(points), observabilityTrendBuckets)
|
||||||
{NodeID: "n1", CapturedAt: base.Add(20 * time.Minute), NetworkRxBytes: 1500, NetworkTxBytes: 2600},
|
|
||||||
}
|
}
|
||||||
|
if !points[0].BucketStartedAt.Before(points[len(points)-1].BucketStartedAt) {
|
||||||
points := BuildNetworkTrendPoints(now, snapshots)
|
t.Fatalf("bucket order invalid: first=%v last=%v", points[0].BucketStartedAt, points[len(points)-1].BucketStartedAt)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ func TestHeartbeatPayloadBindingAndFrpsObservationInsert(t *testing.T) {
|
|||||||
Snapshot: &agent.NodeMetricSnapshot{
|
Snapshot: &agent.NodeMetricSnapshot{
|
||||||
CapturedAtUnix: now.Unix(),
|
CapturedAtUnix: now.Unix(),
|
||||||
CPUUsagePercent: 12.5,
|
CPUUsagePercent: 12.5,
|
||||||
NetworkRxBytes: 1024,
|
DiskReadBytes: 100,
|
||||||
NetworkTxBytes: 2048,
|
DiskWriteBytes: 200,
|
||||||
},
|
},
|
||||||
HealthEvents: []agent.NodeHealthEvent{},
|
HealthEvents: []agent.NodeHealthEvent{},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -48,9 +48,8 @@ func BuildSnapshot(cfg *config.Config, stateStore *state.Store) *service.AgentNo
|
|||||||
|
|
||||||
metric.MemoryTotalBytes, metric.MemoryUsedBytes = edgeobs.ReadMemInfo()
|
metric.MemoryTotalBytes, metric.MemoryUsedBytes = edgeobs.ReadMemInfo()
|
||||||
metric.StorageTotalBytes, metric.StorageUsedBytes = edgeobs.StatFilesystem(cfg.DataDir)
|
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()
|
metric.DiskReadBytes, metric.DiskWriteBytes = edgeobs.ReadLinuxDiskTotals()
|
||||||
|
|
||||||
if stateStore == nil {
|
if stateStore == nil {
|
||||||
return metric
|
return metric
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,10 +128,8 @@ type NodeMetricSnapshot struct {
|
|||||||
MemoryTotalBytes int64 `json:"memory_total_bytes"`
|
MemoryTotalBytes int64 `json:"memory_total_bytes"`
|
||||||
StorageUsedBytes int64 `json:"storage_used_bytes"`
|
StorageUsedBytes int64 `json:"storage_used_bytes"`
|
||||||
StorageTotalBytes int64 `json:"storage_total_bytes"`
|
StorageTotalBytes int64 `json:"storage_total_bytes"`
|
||||||
DiskReadBytes int64 `json:"disk_read_bytes"`
|
DiskReadBytes int64 `json:"disk_read_bytes"`
|
||||||
DiskWriteBytes int64 `json:"disk_write_bytes"`
|
DiskWriteBytes int64 `json:"disk_write_bytes"`
|
||||||
NetworkRxBytes int64 `json:"network_rx_bytes"`
|
|
||||||
NetworkTxBytes int64 `json:"network_tx_bytes"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeAccessLog is an access log entry from agent (L1 business fact).
|
// NodeAccessLog is an access log entry from agent (L1 business fact).
|
||||||
|
|||||||
Reference in New Issue
Block a user