diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 5aadaa8c..81127050 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -41,6 +41,7 @@ sidebar: false ### 修复 +- 修复 Agent 上报访问日志的 `bytes_sent` 在 Server 入库链路丢失,导致 Zone 概览“已提供的数据总计”长期为 0 的问题。 - 修复嵌入式静态前端访问 `/websites/:zoneId` 时回退到首页 HTML,导致 Zone 详情页显示总览并触发 React hydration error 的问题。 - Docker ClickHouse 性能配置改为单文件挂载,避免覆盖镜像内置的 Docker 网络监听配置,导致宿主机无法通过 8123/9000 访问服务。 diff --git a/internal/apps/openflare/agent/observability.go b/internal/apps/openflare/agent/observability.go index 8d7a81d9..0edfa849 100644 --- a/internal/apps/openflare/agent/observability.go +++ b/internal/apps/openflare/agent/observability.go @@ -19,13 +19,13 @@ import ( ) const ( - healthEventStatusActive = "active" - healthEventStatusResolved = "resolved" - healthSeverityInfo = "info" - healthSeverityWarning = "warning" - healthSeverityCritical = "critical" - accessLogPathMaxLength = 100 - healthEventMessageMaxLength = 4096 + healthEventStatusActive = "active" + healthEventStatusResolved = "resolved" + healthSeverityInfo = "info" + healthSeverityWarning = "warning" + healthSeverityCritical = "critical" + accessLogPathMaxLength = 100 + healthEventMessageMaxLength = 4096 ) // PersistHeartbeatObservability stores profile, snapshots, traffic, access logs, and health events. @@ -221,6 +221,7 @@ func buildNodeAccessLogRecords(nodeID string, direct []NodeAccessLog, buffered [ Host: strings.TrimSpace(item.Host), Path: truncateForDatabase(strings.TrimSpace(item.Path), accessLogPathMaxLength), StatusCode: item.StatusCode, + BytesSent: item.BytesSent, } if resolver != nil { record.Region = resolver.Resolve(record.RemoteAddr) diff --git a/internal/apps/openflare/agent/observability_test.go b/internal/apps/openflare/agent/observability_test.go new file mode 100644 index 00000000..7151ffb3 --- /dev/null +++ b/internal/apps/openflare/agent/observability_test.go @@ -0,0 +1,33 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package agent + +import ( + "testing" + "time" +) + +func TestBuildNodeAccessLogRecordsPreservesBytesSent(t *testing.T) { + reportedAt := time.Date(2026, 7, 12, 10, 0, 0, 0, time.UTC) + + records, err := buildNodeAccessLogRecords("node-a", []NodeAccessLog{ + { + LoggedAtUnix: reportedAt.Unix(), + RemoteAddr: "203.0.113.10", + Host: "api.example.com", + Path: "/v1/ping", + StatusCode: 200, + BytesSent: 4096, + }, + }, nil, reportedAt) + if err != nil { + t.Fatalf("buildNodeAccessLogRecords() error = %v", err) + } + if len(records) != 1 { + t.Fatalf("expected one access log record, got %d", len(records)) + } + if records[0].BytesSent != 4096 { + t.Fatalf("BytesSent = %d, want 4096", records[0].BytesSent) + } +} diff --git a/internal/repository/analytics/node_access_log_test.go b/internal/repository/analytics/node_access_log_test.go index 6434490f..79a8fce6 100644 --- a/internal/repository/analytics/node_access_log_test.go +++ b/internal/repository/analytics/node_access_log_test.go @@ -39,6 +39,7 @@ func TestBatchInsertNodeAccessLogs_UsesModelBatchSQL(t *testing.T) { Host: "example.com", Path: "/alpha", StatusCode: 200, + BytesSent: 2048, CreatedAt: loggedAt, }, }) @@ -48,4 +49,6 @@ func TestBatchInsertNodeAccessLogs_UsesModelBatchSQL(t *testing.T) { assert.True(t, mockBatch.sendCalled) require.Len(t, mockBatch.rows, 1) assert.Equal(t, "node-a", mockBatch.rows[0][1]) -} \ No newline at end of file + require.Len(t, mockBatch.rows[0], 10) + assert.Equal(t, uint64(2048), mockBatch.rows[0][8]) +} diff --git a/internal/repository/analytics/node_access_log_writer.go b/internal/repository/analytics/node_access_log_writer.go index 88b02336..0382931c 100644 --- a/internal/repository/analytics/node_access_log_writer.go +++ b/internal/repository/analytics/node_access_log_writer.go @@ -47,6 +47,7 @@ func BatchInsertNodeAccessLogs(ctx context.Context, logs []analyticsmodel.NodeAc logItem.Host, logItem.Path, logItem.StatusCode, + logItem.BytesSent, createdAt.UTC(), ); err != nil { return fmt.Errorf("append node access log to batch: %w", err) @@ -57,4 +58,4 @@ func BatchInsertNodeAccessLogs(ctx context.Context, logs []analyticsmodel.NodeAc return fmt.Errorf("send clickhouse batch: %w", err) } return nil -} \ No newline at end of file +}