docs(obs): 约定访问日志 cache_status 与明细三态展示

仅上报 $upstream_cache_status,不上报回源地址;UI 由原始值推导
命中缓存 / 回源 / 未使用缓存。
This commit is contained in:
ryan
2026-07-18 22:46:46 +08:00
parent 9aec984bee
commit ee9d651c8a
2 changed files with 70 additions and 11 deletions
+61 -9
View File
@@ -202,7 +202,9 @@ Agent:tail access.log → 解析 JSON 行 → 原样字段上报(可截断 p
"status_code": 200,
"bytes_sent": 1024,
"request_length": 128,
"request_time_ms": 15
"request_time_ms": 15,
"user_agent": "Mozilla/5.0 ...",
"cache_status": "HIT"
}
```
@@ -216,6 +218,8 @@ Agent:tail access.log → 解析 JSON 行 → 原样字段上报(可截断 p
| `bytes_sent` | int64 | ✅ | **`$body_bytes_sent`** | **已提供数据**(响应体) |
| `request_length` | int64 | 建议 | `$request_length` | **接收数据** |
| `request_time_ms` | int64 | 可选 | `$request_time * 1000` | 耗时;缺省 0 |
| `user_agent` | string | 建议 | `$http_user_agent` | UA;可截断入库 |
| `cache_status` | string | 建议 | **`$upstream_cache_status`** | 边缘缓存结果(见 §3.5.1) |
**明确不由 Agent 上报(由 Server 写入):**
@@ -223,6 +227,45 @@ Agent:tail access.log → 解析 JSON 行 → 原样字段上报(可截断 p
* `id` / `created_at`:Server 生成
* `node_id`:取自 payload / 鉴权上下文
**明确不上报:**
* `upstream_addr` / 回源地址 / `origin_fetched`:不做回源端点追踪;「是否回源」仅由 `cache_status` 在控制面推导(§3.5.1)
### 3.5.1 `cache_status` — 缓存命中与回源(明细优先)
**目标(第一期):** 访问日志明细/详情能展示「是否命中缓存 / 是否回源 / 未使用缓存」。
**口径:** 只存 OpenResty `$upstream_cache_status` 原始值;**不上报** upstream 地址。
#### 原始值(入库)
| 值 | 含义(OpenResty) |
| --- | --- |
| `HIT` | 命中缓存 |
| `MISS` | 未命中,向 upstream 取内容 |
| `BYPASS` | 跳过缓存(如 method/cookie/策略导致 `$openflare_skip_cache`) |
| `EXPIRED` | 缓存过期后回源 |
| `STALE` | 提供陈旧缓存(stale) |
| `UPDATING` | 后台更新中,可能返回旧缓存 |
| `REVALIDATED` | 协商验证后仍用缓存 |
| `-` 或空 | 未经过 `proxy_cache`(如 Pages 本地静态、非代理 location) |
#### UI 三态推导(不落库)
控制面展示用派生枚举 `cache_outcome`,**不写 CH**:
| 三态 | 条件(`cache_status`) | 列表标签建议 |
| --- | --- | --- |
| **命中缓存** | `HIT` / `STALE` / `REVALIDATED` / `UPDATING` | 命中 |
| **回源** | `MISS` / `EXPIRED` | 回源 |
| **未使用缓存** | `BYPASS` / `-` / `""` | 未缓存 |
详情可同时显示三态 + 原始 `cache_status`。
#### 边界
* Pages 静态 / 无 `proxy_cache` 的 location:多为空或 `-` → **未使用缓存**,不得标成「命中」。
* 第一期只做明细可见;命中率看板、hourly 维度可后续用同一列聚合。
**单次心跳条数建议:**
* 软上限例如 2000 条/拍;超出进入 `buffered` 下一批,**禁止** 在 Agent 压成 TrafficReport。
@@ -309,6 +352,8 @@ type NodeAccessLog struct {
RemoteAddr string `json:"remote_addr"`
Host string `json:"host"`
Path string `json:"path"`
UserAgent string `json:"user_agent,omitempty"`
CacheStatus string `json:"cache_status,omitempty"` // $upstream_cache_status
StatusCode int `json:"status_code"`
BytesSent int64 `json:"bytes_sent"` // body_bytes_sent,已提供数据
RequestLength int64 `json:"request_length"` // 接收数据
@@ -419,10 +464,12 @@ CREATE TABLE IF NOT EXISTS of_node_access_logs
region String, -- Server GeoIP 写入,Agent 不传
host String,
path String,
user_agent String DEFAULT '', -- $http_user_agent
cache_status String DEFAULT '', -- $upstream_cache_status
status_code Int32,
bytes_sent UInt64, -- 已提供数据(body)
request_length UInt64 DEFAULT 0, -- 接收数据;新增
request_time_ms UInt32 DEFAULT 0, -- 可选;新增
request_length UInt64 DEFAULT 0, -- 接收数据
request_time_ms UInt32 DEFAULT 0, -- 可选
created_at DateTime64(3, 'UTC')
)
ENGINE = MergeTree()
@@ -441,18 +488,19 @@ SETTINGS index_granularity = 8192;
| `region` | String | Server GeoIP |
| `host` | String | 上报 |
| `path` | String | 上报 |
| `user_agent` | String | 上报(可空) |
| `cache_status` | String | 上报(可空)→ **缓存状态** |
| `status_code` | Int32 | 上报 |
| `bytes_sent` | UInt64 | 上报 → **已提供数据** |
| `request_length` | UInt64 | 上报 → **接收数据** |
| `request_time_ms` | UInt32 | 上报可选 |
| `created_at` | DateTime64(3) | Server now |
**迁移:** 现表已有 `bytes_sent`;新增:
**迁移:** 现表已有 `bytes_sent` / `request_length` / `request_time_ms` / `user_agent`;缓存状态新增:
```sql
ALTER TABLE of_node_access_logs
ADD COLUMN IF NOT EXISTS request_length UInt64 DEFAULT 0,
ADD COLUMN IF NOT EXISTS request_time_ms UInt32 DEFAULT 0;
ADD COLUMN IF NOT EXISTS cache_status String DEFAULT '';
```
### 5.2 L1 小时汇总(Server 侧 MV)
@@ -625,14 +673,16 @@ Relay 专用 `of_node_obs_frps` / `of_node_obs_frpc` **保留**(非本 Agent
## 7. OpenResty 日志格式(与明细对齐)
目标 `log_format`(与现网一致,保证 `bytes_sent` 键 = body):
目标 `log_format`(保证 `bytes_sent` 键 = body;含 UA 与缓存状态):
```nginx
log_format openflare_json escape=json
'{"ts":"$time_iso8601","host":"$host","path":"$request_uri",'
'"remote_addr":"$remote_addr","status":$status,'
'"request_time":$request_time,'
'"bytes_sent":$body_bytes_sent,"request_length":$request_length}';
'"bytes_sent":$body_bytes_sent,"request_length":$request_length,'
'"user_agent":"$http_user_agent",'
'"cache_status":"$upstream_cache_status"}';
```
Agent 解析:
@@ -640,7 +690,9 @@ Agent 解析:
* `ts` → `logged_at_unix`
* `bytes_sent` → 协议 `bytes_sent`(已提供)
* `request_length` → 协议 `request_length`
* `request_time` → 可选 `request_time_ms = round(sec * 1000)`
* `request_time` → 可选 `request_time_ms = round(sec * 1000)`
* `user_agent` → 协议 `user_agent`
* `cache_status` → 协议 `cache_status`(原样透传,不做三态压缩)
---
+9 -2
View File
@@ -154,6 +154,8 @@ status ← $status
request_time ← $request_time
bytes_sent ← $body_bytes_sent 【已提供数据 = 响应体字节】
request_length← $request_length 【接收数据】
user_agent ← $http_user_agent
cache_status ← $upstream_cache_status 【缓存状态;UI 可推导命中/回源/未缓存】
```
观测端口请求 **不写** 业务 access.log(独立 server `access_log off`)。
@@ -170,7 +172,9 @@ request_length← $request_length 【接收数据】
"status_code": 200,
"bytes_sent": 1024,
"request_length": 128,
"request_time_ms": 15
"request_time_ms": 15,
"user_agent": "curl/8.0",
"cache_status": "MISS"
},
{
"logged_at_unix": 1721289602,
@@ -180,7 +184,9 @@ request_length← $request_length 【接收数据】
"status_code": 200,
"bytes_sent": 8192,
"request_length": 300,
"request_time_ms": 8
"request_time_ms": 8,
"user_agent": "Mozilla/5.0",
"cache_status": "HIT"
}
]
```
@@ -191,6 +197,7 @@ request_length← $request_length 【接收数据】
| `request_length` | **接收数据**(单请求) |
| `logged_at_unix` | 请求完成时间(业务时间轴) |
| `host` | 用于 Zone 域名过滤 |
| `cache_status` | `$upstream_cache_status` 原样;详情/列表可推导三态(命中/回源/未缓存);**不上报** upstream 地址 |
| 无 `region` | **Server 入库时** GeoIP 写入 |
### 4.3 Server 如何用(产品指标)