# 日志策略逻辑梳理与问题清单(当前基线:`E:\AI_PRODUCT\waf-platform`) ## 1. 结论摘要 - 当前链路是 **`type` + `writeTargets` 双字段共同决定行为**。 - 运行时真正用于读写判断的是 `writeTargets`(`ParseWriteTargetsFromPolicy` 解析结果)。 - HTTP 与 DNS 都已接入“公用策略”下发,DNS 也已支持 ClickHouse 读取。 - 目前存在多处逻辑不一致,核心风险是:**页面显示、数据库值、实际读写行为可能不同步**。 ## 2. 关键入口文件 - 类型与组合映射:`EdgeCommon/pkg/serverconfigs/access_log_storages.go` - 写入目标定义/解析:`EdgeCommon/pkg/serverconfigs/access_log_write_targets.go` - 策略创建/更新(Admin):`EdgeAdmin/internal/web/actions/default/servers/accesslogs/createPopup.go`、`EdgeAdmin/internal/web/actions/default/servers/accesslogs/update.go` - 策略保存(API):`EdgeAPI/internal/rpc/services/service_http_access_log_policy_plus.go` - 策略落库(DAO):`EdgeAPI/internal/db/models/http_access_log_policy_dao.go` - 公用策略运行时缓存:`EdgeAPI/internal/accesslogs/storage_manager.go` - HTTP 节点队列:`EdgeNode/internal/nodes/http_access_log_queue.go` - DNS 节点队列:`EdgeDNS/internal/nodes/ns_access_log_queue.go` - 节点配置下发:`EdgeAPI/internal/db/models/node_dao.go`、`EdgeAPI/internal/db/models/ns_node_dao_plus.go` - HTTP 查询服务:`EdgeAPI/internal/rpc/services/service_http_access_log.go` - DNS 查询服务:`EdgeAPI/internal/rpc/services/nameservers/service_ns_access_log.go` - CH 查询实现:`EdgeAPI/internal/clickhouse/logs_ingest_store.go`、`EdgeAPI/internal/clickhouse/ns_logs_ingest_store.go` ## 3. 数据模型与语义 `edgeHTTPAccessLogPolicies` 关键字段: - `type`:`file` / `file_mysql` / `file_clickhouse` / `file_mysql_clickhouse` / `es` / `tcp` / `syslog` / `command` - `writeTargets`:JSON(`file/mysql/clickhouse` 三个布尔值) - `disableDefaultDB`:停用默认数据库存储(兼容旧语义) 当前实际规则: 1. Admin 侧根据下拉 `type` 生成 `writeTargetsJSON`。 2. API 原样落库(仅做少量历史 type 别名兼容)。 3. 运行时使用 `ParseWriteTargetsFromPolicy(writeTargets, type, disableDefaultDB)` 得到最终写入目标。 ## 4. 端到端链路(当前行为) ### 4.1 策略创建/更新 - 创建与更新都会调用 `ParseStorageTypeAndWriteTargets`,并同时提交 `type` 与 `writeTargetsJSON`。 - `file_clickhouse` / `file_mysql_clickhouse` 在 UI 上隐藏了手填路径输入,依赖旧值或默认目录回退。 - DAO 更新时,只有 `writeTargetsJSON` 非空才会覆盖 `writeTargets` 字段。 ### 4.2 HTTP 写入链路 - Node 侧: - `needWriteFile = writeTargets == nil || writeTargets.NeedWriteFile()` - `needReportAPI = writeTargets == nil || writeTargets.NeedReportToAPI()` - API 侧: - `CreateHTTPAccessLogs` 里是否写 MySQL 由 `canWriteAccessLogsToDB() -> WriteMySQL()` 决定。 - 同时调用 `writeAccessLogsToPolicy()`,把日志再交给公用策略存储引擎处理(如 file/es/tcp/syslog/command)。 - 查询侧: - `shouldReadAccessLogsFromClickHouse()` 为真且 CH 配置可用时优先读 CH。 - CH 失败后,按 `shouldReadAccessLogsFromMySQL()` 回退 MySQL。 ### 4.3 DNS 写入链路 - DNS 节点: - `needWriteFile = targets == nil || targets.File || targets.ClickHouse` - `needReportAPI = targets == nil || targets.MySQL` - 即 CH-only 下 DNS 只写本地文件,不上报 API。 - DNS API 查询: - 与 HTTP 一样优先 CH,再按策略回退 MySQL。 ### 4.4 节点路径更新机制 - API 下发公用策略的 `AccessLogFilePath` 与 `AccessLogWriteTargets` 到 HTTP/DNS 节点配置。 - Node/DNS 收到新配置后会 `SetDirByPolicyPath(...)` 并 `EnsureInit/Reopen/Close`,可自动切换目录。 - 空路径时会回退到: - HTTP:`EDGE_LOG_DIR` 或默认 `/var/log/edge/edge-node` - DNS:`EDGE_DNS_LOG_DIR` 或默认 `/var/log/edge/edge-dns` ## 5. 行为矩阵(按当前代码) - `file` - 写文件:是 - 写 MySQL:否(仅当 `writeTargets.mysql=true` 才会写) - 读:优先 CH(若开启),否则按 MySQL 开关 - `file_mysql` - 写文件:是 - 写 MySQL:是 - 读:MySQL 可读;若 CH 也开则优先 CH - `file_clickhouse` - 写文件:是 - 写 MySQL:否(理论上) - 读:优先 CH;若 CH 不可用且 mysql=false,则返回空 - `file_mysql_clickhouse` - 写文件:是 - 写 MySQL:是 - 读:优先 CH,失败回退 MySQL - `es/tcp/syslog/command` - 仍会由 `writeTargets` 决定是否 MySQL(当前解析默认给 MySQL=true) - 另外会通过策略引擎输出到对应目标 ## 6. 逻辑问题清单(按优先级) ### P0:`type` 与 `writeTargets` 双真源,容易漂移 - 页面展示与回显会参考 `type`,实际写读判断优先看 `writeTargets`。 - 一旦两者不一致,会出现“UI 看起来是 ClickHouse,实际还在写/读 MySQL”。 ### P0:`disableDefaultDB` 在新链路中容易失效 - `WriteMySQL()` 优先看 `writeTargets.MySQL`,只有 `writeTargets` 为空才回退 `disableDefaultDB`。 - 由于 Admin 基本总会提交 `writeTargetsJSON`,`disableDefaultDB` 常常不会真正生效。 ### P1:HTTP 与 DNS 在 CH-only 场景上报 API 语义不一致 - HTTP:`NeedReportToAPI()` = `MySQL || ClickHouse`,CH-only 仍会上报 API。 - DNS:CH-only 不上报 API,仅写文件给 Fluent Bit。 - 高并发下会带来不必要的 API 压力与行为差异。 ### P1:`file_clickhouse` 可能出现空路径,策略引擎会启动失败 - `FileStorage.Start()` 要求 `path` 非空。 - 但 UI 在 clickhouse 组合类型隐藏路径输入,若 `options.path` 为空,策略引擎会报错(虽然节点本地写文件仍可回退目录工作)。 ### P1:HTTP 可能出现“节点写文件 + API 再写文件”的重复路径 - `CreateHTTPAccessLogs` 无论是否写 MySQL,都会 `writeAccessLogsToPolicy()`。 - 公用策略若为 file*,API 侧 `StorageManager.createStorage()` 会创建 `FileStorage` 并再次落文件。 - 若目标是“仅节点写文件供 Fluent Bit 采集”,这会引入额外重复写入。 ### P2:DNS `requestId` 生成算法有重复风险 - `ns_access_log_queue.go` 里 `timestamp/requestId` 为 `loop()` 局部变量,每轮 tick 重置。 - 同秒跨批次可能冲突,影响游标分页与去重。 ### P2:UI 文案分支存在不可达条件 - `createPopup.html` / `update.html` 在 `file|file_mysql` 区块内嵌了 clickhouse 条件文案分支,实际不会触发。 - 不影响功能,但会增加理解成本。 ## 7. 建议修复顺序 1. 先统一单一真源(建议 API 层统一按 `type` 规范化并覆盖 `writeTargets`)。 2. 明确 `disableDefaultDB` 与 `writeTargets` 的优先级,避免“配置项在 UI 可选但不生效”。 3. 统一 HTTP/DNS 在 CH-only 的上报语义(建议都走“节点文件 + Fluent Bit”,API 不再接收该流量)。 4. 修复 file_clickhouse 空路径策略启动失败(要求路径 or 统一默认路径回填到 options)。 5. 修复 DNS requestId 生成(全局原子递增或更高精度时间戳方案)。 ## 8. 当前可用性判断 - 系统“可运行”,但配置行为存在歧义,且在高并发下会放大成本和排障难度。 - 若目标是稳定的高吞吐日志链路,建议优先处理 P0/P1 问题后再继续线上放量。