mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 07:36:37 +08:00
feat(access-logs): 新增 IP 明细 Tab 并完善日志详情字段
按时间窗聚合 IP 请求数/2xx 比例/入出站流量,支持排序与详情分析; 日志明细详情仅展示请求业务字段,IP 情报迁至独立详情弹窗。
This commit is contained in:
@@ -23,11 +23,12 @@ sidebar: false
|
||||
|
||||
### 变更
|
||||
|
||||
- 访问日志新增「IP 明细」标签页:可按 24 小时 / 7 天 / 15 天 / 30 天或自定义时间区间查看每个 IP 的请求数、2xx 比例、入站/出站流量与最后访问时间,并支持排序与详情分析;日志明细详情仅展示单条请求字段,IP 情报改在 IP 明细中查看。
|
||||
- 边缘缓存默认策略调整为「标准静态资源」:新建开启缓存时推荐仅缓存 css/js/图片/字体等扩展名(不含 HTML);原「按 URL」与空策略存量行为保留为「所有可缓存 GET」。重新发布节点配置后生效。
|
||||
- 优化访问日志概览及其他图表全局排行榜 (RankChart) 的样式布局:将每一项改为单行横向排布(左侧标签、中间进度条、右侧数值),数值支持自动格式化为 Compact 形式(如 39.57k、1.2M),同时调整默认高度为 320px 并隐藏滚动条。
|
||||
- 访问日志页面重构为「概览」与「日志明细」两个标签页:概览展示请求量、访问量、带宽趋势与 Top Paths/Hosts/IPs,明细列表保留检索能力;已移除时间折叠与 IP 汇总视图。
|
||||
- 访问日志页面重构为「概览」「IP 明细」与「日志明细」标签页:概览展示请求量、访问量、带宽趋势与 Top Paths/Hosts/IPs,明细列表保留检索能力;已移除时间折叠视图。
|
||||
- 边缘访问日志现支持上报并存储 User-Agent;概览新增设备类型饼图,以及浏览器、操作系统、User-Agent 排行。
|
||||
- 访问日志明细支持查看单条请求详情,可查看 User-Agent、IP 访问趋势与分析,并快捷将 IP 加入或移出 IP 组。
|
||||
- 访问日志明细支持查看单条请求详情(含 User-Agent、缓存状态等);IP 访问趋势、分析与加入 IP 组能力集中在 IP 明细详情中。
|
||||
- 访问日志页签状态改为 URL 参数记忆,筛选后不会再跳回概览。
|
||||
- 访问日志概览支持按 Zone/域名多选筛选,并可折叠展开层级选择。
|
||||
- 访问日志明细列表在 IP 旁展示对应国家/地区信息。
|
||||
|
||||
+45
-2
@@ -5217,7 +5217,7 @@ const docTemplate = `{
|
||||
"SessionCookie": []
|
||||
}
|
||||
],
|
||||
"description": "按 IP 聚合访问日志统计并分页返回,需要管理员权限",
|
||||
"description": "按 IP 聚合访问日志统计并分页返回;支持 hours 或 since/until 时间窗,需要管理员权限",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -5244,6 +5244,24 @@ const docTemplate = `{
|
||||
"name": "host",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "统计时间范围(小时,1-720,默认 168)",
|
||||
"name": "hours",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "开始时间 RFC3339(与 until 同时提供时优先于 hours)",
|
||||
"name": "since",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "结束时间 RFC3339",
|
||||
"name": "until",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
@@ -5258,7 +5276,7 @@ const docTemplate = `{
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "排序字段",
|
||||
"description": "排序字段 total_requests|request_length|bytes_sent|success_ratio|last_seen_at|remote_addr",
|
||||
"name": "sort_by",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -16312,6 +16330,9 @@ const docTemplate = `{
|
||||
"has_more": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hours": {
|
||||
"type": "integer"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -16324,6 +16345,9 @@ const docTemplate = `{
|
||||
"page_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"since": {
|
||||
"type": "string"
|
||||
},
|
||||
"sort_by": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -16332,21 +16356,40 @@ const docTemplate = `{
|
||||
},
|
||||
"total_ip": {
|
||||
"type": "integer"
|
||||
},
|
||||
"until": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"observability.AccessLogIPSummaryView": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bytes_received": {
|
||||
"type": "integer"
|
||||
},
|
||||
"bytes_sent": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_seen_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"recent_requests": {
|
||||
"description": "RecentRequests is deprecated and always 0.",
|
||||
"type": "integer"
|
||||
},
|
||||
"region": {
|
||||
"type": "string"
|
||||
},
|
||||
"remote_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"success_2xx_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"success_ratio": {
|
||||
"type": "number"
|
||||
},
|
||||
"total_requests": {
|
||||
"type": "integer"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# 访问日志 IP 明细 Tab — 实现计划
|
||||
|
||||
## 1. 目标与背景 (Goal & Context)
|
||||
|
||||
* **需求背景**:运维需要按 IP 维度快速查看时间窗内的访问量与流量,并下钻单 IP 情报;原先 IP 分析嵌在「单条访问日志详情」中,入口弱、列表能力缺失。
|
||||
* **开发范围 (Scope) V1**:
|
||||
* 访问日志页新增第三 Tab **「IP 明细」**(`?tab=ips`)。
|
||||
* IP 列表:时间筛选(快捷 24h/7d/15d/30d + 自定义 since/until)、分页、按请求数 / 入站 / 出站 / 最后访问 / 2xx 比例排序。
|
||||
* 列表列:IP、地区、请求数、2xx 比例(2xx 数 / 总请求)、入站流量、出站流量、最后访问。
|
||||
* 行详情:弹窗展示完整 **IP 情报**(分析 + 趋势 + Top 分布 + 加入 WAF IP 组)。
|
||||
* **日志明细详情弹窗仅展示单条请求字段**,不再内嵌 IP 情报;如需分析请到 IP 明细。
|
||||
* **Out of Scope(V1 不做)**:
|
||||
* 独立 `/access-logs/ip` 子路由全页。
|
||||
* IP 列表 UI 暴露节点 / host 筛选(后端可保留兼容参数,前端首版不放)。
|
||||
* 入/出站带宽时间序列(趋势图仍为请求数)。
|
||||
* 实时 GeoIP 二次查询(沿用入库 `region`)。
|
||||
|
||||
## 2. 设计与决策 (Design & Decisions)
|
||||
|
||||
### 2.1 页面与交互
|
||||
|
||||
| Tab | URL | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 概览 | `/access-logs` | 不变 |
|
||||
| IP 明细 | `/access-logs?tab=ips` | 新 |
|
||||
| 日志明细 | `/access-logs?tab=list` | 不变;详情弹窗瘦身 |
|
||||
|
||||
* **时间筛选(IP 明细)**:
|
||||
* 快捷:`hours` ∈ {24, 168, 360, 720},默认 168(7d)。
|
||||
* 自定义:`since` + `until`(RFC3339);**同时提供时覆盖 hours**。
|
||||
* **详情形态**:留在列表页的 Dialog(非独立子页)。
|
||||
* **日志详情瘦身**:`access-log-detail-dialog` 只渲染请求字段(时间、节点、IP、地区、host、path、UA、cache、status 等)及必要操作;删除 analysis/trend/WAF 组内嵌区块。WAF「按 IP 加入组」仅保留在 IP 详情弹窗。
|
||||
|
||||
### 2.2 API 设计(扩展现有端点,不新建)
|
||||
|
||||
**`GET /api/v1/d/access-logs/ip-summary`**
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `hours` | 1–720;默认 168;在无 since/until 时生效 |
|
||||
| `since` / `until` | 可选 RFC3339;同时有效时优先于 hours |
|
||||
| `sort_by` | `total_requests`(默认)\| `request_length`(入站)\| `bytes_sent`(出站)\| `last_seen_at` \| `success_ratio` |
|
||||
| `sort_order` | `asc` \| `desc` |
|
||||
| `p` / `page_size` | 分页,page_size 上限 200 |
|
||||
| `remote_addr` / `node_id` / `host` | 兼容保留;V1 UI 可不暴露 |
|
||||
|
||||
**响应行字段(扩展)**
|
||||
|
||||
```text
|
||||
remote_addr string
|
||||
region string // 窗内 argMax(region, logged_at) 或等价
|
||||
total_requests uint64
|
||||
success_2xx_count uint64 // status_code 200–299
|
||||
success_ratio float64 // success_2xx_count / total_requests;total=0 时为 0
|
||||
bytes_received uint64 // sum(request_length) 入站
|
||||
bytes_sent uint64 // sum(bytes_sent) 出站
|
||||
last_seen_at time
|
||||
```
|
||||
|
||||
* `recent_requests`:可停止计算或固定返回 0;**UI 不展示**。避免与可配置时间窗语义冲突。
|
||||
* 详情下钻仍用现有:
|
||||
* `GET .../ip-summary/analysis?remote_addr=&hours=`(或 since/until,若后续扩展;V1 将列表当前窗映射为 hours 或 since/until 与后端约定一致)
|
||||
* `GET .../ip-summary/trend?remote_addr=&hours=&bucket_minutes=`
|
||||
|
||||
**分析/趋势时间窗对齐**:打开 IP 详情时,将列表当前时间窗传入 analysis/trend(优先 since/until;仅有 hours 则传 hours)。
|
||||
|
||||
### 2.3 数据层(ClickHouse)
|
||||
|
||||
* 表:`of_node_access_logs`(已有 `bytes_sent`、`request_length`、`status_code`、`region`)。
|
||||
* 聚合:`GROUP BY remote_addr`,在 `NodeAccessLogFilter.Since/Until` 上过滤。
|
||||
* 2xx:`countIf(status_code >= 200 AND status_code < 300)`。
|
||||
* region:`argMax(region, logged_at)`。
|
||||
* 排序:服务端 ORDER BY 对应表达式;`success_ratio` 注意除零(`if(total=0,0,ratio)`)。
|
||||
|
||||
### 2.4 设计决策权衡
|
||||
|
||||
| 选项 | 结论 |
|
||||
| --- | --- |
|
||||
| 扩展 `/ip-summary` vs 新 `/ip-list` | **扩展现有**,前端 service 已有 `listIPSummaries` |
|
||||
| 详情弹窗 vs 子页 | **弹窗**,与现有明细交互一致 |
|
||||
| IP 情报放日志详情 vs 独立 IP 详情 | **仅 IP 明细详情**;日志详情只展示请求信息 |
|
||||
| 时间:仅快捷 vs 仅自定义 | **两者都要**,自定义优先 |
|
||||
|
||||
### 2.5 数据流(示意)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
UI_IP[IP 明细 Tab] --> API_List[GET /ip-summary]
|
||||
API_List --> CH[(of_node_access_logs)]
|
||||
UI_IP --> UI_Dlg[IP 详情 Dialog]
|
||||
UI_Dlg --> API_A[GET /ip-summary/analysis]
|
||||
UI_Dlg --> API_T[GET /ip-summary/trend]
|
||||
API_A --> CH
|
||||
API_T --> CH
|
||||
UI_List[日志明细 Tab] --> API_Logs[GET /access-logs]
|
||||
UI_List --> UI_LogDlg[日志详情 Dialog]
|
||||
UI_LogDlg -.->|不请求 IP 分析| X[仅请求字段]
|
||||
```
|
||||
|
||||
## 3. 具体修改文件清单 (Proposed Changes)
|
||||
|
||||
### 后端 Server
|
||||
|
||||
* #### [MODIFY] `internal/repository/analytics/node_access_log_stats.go`(及 filter 如有)
|
||||
* `IPSummariesNodeAccessLogs`:时间窗、sum 入/出、2xx count、ratio、region、扩展 sort。
|
||||
* #### [MODIFY] `internal/apps/openflare/observability/access_log_logics.go`
|
||||
* Query/View 类型扩展;解析 hours/since/until;去掉或忽略 recent 3h 硬编码。
|
||||
* #### [MODIFY] `internal/apps/openflare/observability/routers.go` / handler
|
||||
* 绑定新 query;Swagger 注释。
|
||||
* #### [MODIFY] 相关单元测试(logics / repository 若有)
|
||||
|
||||
### 前端 Web
|
||||
|
||||
* #### [MODIFY] `frontend/app/(main)/access-logs/page.tsx`
|
||||
* 第三 Tab `ips`;`resolveTab` / `handleTabChange`。
|
||||
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-tab.tsx`
|
||||
* 列表、时间筛选、排序、分页、打开详情。
|
||||
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-detail-dialog.tsx`
|
||||
* IP 入口详情壳。
|
||||
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-analysis-panel.tsx`
|
||||
* 从现有 `access-log-detail-dialog` **迁出** 分析/趋势/排行/WAF IP 组逻辑。
|
||||
* #### [MODIFY] `frontend/app/(main)/access-logs/components/access-log-detail-dialog.tsx`
|
||||
* **删除** IP 情报相关 UI 与 `getIPAnalysis` / `getIPTrend` 请求;仅请求日志字段展示。
|
||||
* #### [MODIFY] `frontend/app/(main)/access-logs/components/access-log-utils.ts`
|
||||
* tab 类型、IP 排序选项、时间筛选辅助。
|
||||
* #### [MODIFY] `frontend/lib/services/openflare/access-log.service.ts` + `types.ts`
|
||||
* `listIPSummaries` 参数与 `AccessLogIPSummaryItem` 字段同步。
|
||||
|
||||
### 文档
|
||||
|
||||
* #### [MODIFY] `docs/changelog/index.md` — `[Unreleased]` 用户可见说明
|
||||
* #### [MODIFY] `docs/design/observability-design.md` 或 data-model(如有访问日志 UI 约定)— 补充 IP 明细 Tab 与 API 字段(中文)
|
||||
* #### [MODIFY] `docs/plan/index.md` — 挂上本计划链接
|
||||
|
||||
## 4. 验证计划 (Verification Plan)
|
||||
|
||||
### 自动化
|
||||
|
||||
```bash
|
||||
go test ./internal/apps/openflare/observability/ ./internal/repository/analytics/
|
||||
# 前端:相关 tsc / 页面无类型错误
|
||||
make code-check # 完成后按项目门禁
|
||||
make prettier
|
||||
make swagger # API 注释变更后
|
||||
```
|
||||
|
||||
### 手动
|
||||
|
||||
1. `/access-logs?tab=ips` 默认 7d 列表有数据;切换 24h/自定义区间结果变化。
|
||||
2. 分别按请求数、入站、出站、2xx 比例、最后访问排序正确。
|
||||
3. 2xx 比例 = 2xx/总数;0 请求不出现 NaN/Infinity。
|
||||
4. 点 IP 打开详情:指标/趋势/Top/WAF 组可用;时间窗与列表一致。
|
||||
5. 日志明细 → 详情:仅请求信息,**无** IP 分析/趋势区块。
|
||||
6. 概览 Tab 行为无回归。
|
||||
|
||||
## 5. 状态
|
||||
|
||||
- [x] 需求澄清与方案确认
|
||||
- [x] 实现(后端 ip-summary 扩展 + 前端 IP 明细 Tab + 日志详情瘦身)
|
||||
- [x] 测试与 changelog(`go test` 相关包通过;changelog 已更新)
|
||||
- [ ] 提交合并
|
||||
@@ -18,6 +18,7 @@
|
||||
* [边缘可观测与业务流量统计重构](./20260717-observability-redesign.md):访问日志为业务唯一真相;Agent 只上报明细与主机读数;收敛「出站/已提供」双字段。
|
||||
* [访问日志 cache_status 明细可见](./20260718-access-log-cache-status.md):上报 `$upstream_cache_status`,明细展示命中/回源/未缓存三态。
|
||||
* [边缘缓存默认 static 策略](./20260718-edge-cache-static-default.md):开启缓存默认仅静态扩展名;存量 url→all。
|
||||
* [访问日志 IP 明细 Tab](./20260719-access-log-ip-tab.md):第三 Tab 按 IP 聚合列表(时间窗/流量/2xx 比例);IP 情报迁入独立详情;日志详情仅请求字段。
|
||||
|
||||
## 使用建议
|
||||
|
||||
|
||||
+45
-2
@@ -5210,7 +5210,7 @@
|
||||
"SessionCookie": []
|
||||
}
|
||||
],
|
||||
"description": "按 IP 聚合访问日志统计并分页返回,需要管理员权限",
|
||||
"description": "按 IP 聚合访问日志统计并分页返回;支持 hours 或 since/until 时间窗,需要管理员权限",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -5237,6 +5237,24 @@
|
||||
"name": "host",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "统计时间范围(小时,1-720,默认 168)",
|
||||
"name": "hours",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "开始时间 RFC3339(与 until 同时提供时优先于 hours)",
|
||||
"name": "since",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "结束时间 RFC3339",
|
||||
"name": "until",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
@@ -5251,7 +5269,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "排序字段",
|
||||
"description": "排序字段 total_requests|request_length|bytes_sent|success_ratio|last_seen_at|remote_addr",
|
||||
"name": "sort_by",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -16305,6 +16323,9 @@
|
||||
"has_more": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hours": {
|
||||
"type": "integer"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -16317,6 +16338,9 @@
|
||||
"page_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"since": {
|
||||
"type": "string"
|
||||
},
|
||||
"sort_by": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -16325,21 +16349,40 @@
|
||||
},
|
||||
"total_ip": {
|
||||
"type": "integer"
|
||||
},
|
||||
"until": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"observability.AccessLogIPSummaryView": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bytes_received": {
|
||||
"type": "integer"
|
||||
},
|
||||
"bytes_sent": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_seen_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"recent_requests": {
|
||||
"description": "RecentRequests is deprecated and always 0.",
|
||||
"type": "integer"
|
||||
},
|
||||
"region": {
|
||||
"type": "string"
|
||||
},
|
||||
"remote_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"success_2xx_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"success_ratio": {
|
||||
"type": "number"
|
||||
},
|
||||
"total_requests": {
|
||||
"type": "integer"
|
||||
}
|
||||
|
||||
+31
-2
@@ -1929,6 +1929,8 @@ definitions:
|
||||
properties:
|
||||
has_more:
|
||||
type: boolean
|
||||
hours:
|
||||
type: integer
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/observability.AccessLogIPSummaryView'
|
||||
@@ -1937,21 +1939,36 @@ definitions:
|
||||
type: integer
|
||||
page_size:
|
||||
type: integer
|
||||
since:
|
||||
type: string
|
||||
sort_by:
|
||||
type: string
|
||||
sort_order:
|
||||
type: string
|
||||
total_ip:
|
||||
type: integer
|
||||
until:
|
||||
type: string
|
||||
type: object
|
||||
observability.AccessLogIPSummaryView:
|
||||
properties:
|
||||
bytes_received:
|
||||
type: integer
|
||||
bytes_sent:
|
||||
type: integer
|
||||
last_seen_at:
|
||||
type: string
|
||||
recent_requests:
|
||||
description: RecentRequests is deprecated and always 0.
|
||||
type: integer
|
||||
region:
|
||||
type: string
|
||||
remote_addr:
|
||||
type: string
|
||||
success_2xx_count:
|
||||
type: integer
|
||||
success_ratio:
|
||||
type: number
|
||||
total_requests:
|
||||
type: integer
|
||||
type: object
|
||||
@@ -7173,7 +7190,7 @@ paths:
|
||||
- openflare-observability
|
||||
/api/v1/d/access-logs/ip-summary:
|
||||
get:
|
||||
description: 按 IP 聚合访问日志统计并分页返回,需要管理员权限
|
||||
description: 按 IP 聚合访问日志统计并分页返回;支持 hours 或 since/until 时间窗,需要管理员权限
|
||||
parameters:
|
||||
- description: 节点 ID
|
||||
in: query
|
||||
@@ -7187,6 +7204,18 @@ paths:
|
||||
in: query
|
||||
name: host
|
||||
type: string
|
||||
- description: 统计时间范围(小时,1-720,默认 168)
|
||||
in: query
|
||||
name: hours
|
||||
type: integer
|
||||
- description: 开始时间 RFC3339(与 until 同时提供时优先于 hours)
|
||||
in: query
|
||||
name: since
|
||||
type: string
|
||||
- description: 结束时间 RFC3339
|
||||
in: query
|
||||
name: until
|
||||
type: string
|
||||
- description: 页码
|
||||
in: query
|
||||
name: p
|
||||
@@ -7195,7 +7224,7 @@ paths:
|
||||
in: query
|
||||
name: page_size
|
||||
type: integer
|
||||
- description: 排序字段
|
||||
- description: 排序字段 total_requests|request_length|bytes_sent|success_ratio|last_seen_at|remote_addr
|
||||
in: query
|
||||
name: sort_by
|
||||
type: string
|
||||
|
||||
Reference in New Issue
Block a user