Files
DCU_DesktopController/.qoder/repowiki/knowledge/zh/ESP32 DCU 调试控制台仓库根/logging_system.md
T

59 lines
5.6 KiB
Markdown
Raw Normal View History

## 1. 使用的方案
项目在前端(ESP32/DCU Web 控制台)实现了自研轻量级日志子系统,位于 `js/common/log.js`。其职责是:**接收带标签的日志条目 → 去重/过滤 → 维护 DOM 面板 + 环形缓冲 → 按级别样式化渲染**。
日志数据并非由前端直接构造所有业务事件,而是通过 `js/common/polling.js` 以轮询方式从后端 ESP32 设备拉取 `/system/status` 与 `/logs` 两条流,把后端的结构化输出注入到统一的 UI 中;同时各模块通过 `addLog(level, tag, message)` 主动写入调试类日志。因此该“logging system”是 **后端实时日志拉取 + 前端集中式渲染** 的组合。
## 2. 核心文件
- `js/common/log.js` — 日志缓存、去重、DOM 渲染、过滤、自动滚动、条数上限裁剪的核心库(`addLog`、`clearLogs`、`setLogFilter`、`toggleAutoScroll` 四导出)
- `js/common/polling.js` — 定时递归 setTimeout 调度 `/system/status` 与 `/logs` 轮询,并将远端行 `^[DIEW] ([^|]+)\|(.*)$` 解析为 `addLog(m[1], m[2], m[3])`
- `css/motor.css`、`components.css`、`can.css`、`layout.css`、`responsive.css`、`base.css` — `.log-line`、`.lt/.lv/.lg/.lm` 等日志元素的 CSS 样式
- `pages/index.html`、`pages/can_monitor.html`、`pages/motor.html`、`tabs.html` — 包含 `#logScroll`、`#logCount`、`#autoScrollBtn`、`.log-filter` 按钮的页面骨架(由 `log.js` 通过 ID 直接操作)
- 消费端(导入 `addLog` 的模块举例):`js/app.js`、`js/common/connection.js`、`js/ble/state.js`、`js/ota/file-handle.js`、`js/ota/session.js`、`js/ota/upload-esp32.js`、`js/ota/upload-gd32.js`、`js/common/polling.js`、`js/ota/progress.js`
## 3. 架构与设计约定
### 日志条目结构
每个日志是一个三元组 `(level, tag, message)`,经 `formatTime` 加上本地时间戳,再被 HTML 转义(调用 `utils.escapeHtml` 防 XSS)后渲染为一行:
```
[时间] [级别] [TAG] 消息
```
其中级别 `lv` 使用 class `D|I|W|E`,tag 用 `lg`,消息用 `lm`。
### 去重策略
- 同一帧内相邻重复项:通过 `makeLogHash(level, tag, message)` 计算字符串哈希并与 `lastLogHash` 比较,完全相同的连续条目只写一次。
- 用于避免高频率状态上报产生刷屏。
### 缓冲与裁剪
- 内存采用普通数组 `logEntries`,`MAX_LOGS = 1000` 作为硬上限;超出时 `trimIfNeeded()` 移出最旧的 DOM 节点并 shift 掉对应 entry。
- 没有持久化历史到 localStorage。
### 过滤与 UI
- 全局 `logFilter` ∈ `{ all, D, I, W, E }`,由页面 `#autoScrollBtn` 与 `.log-filter` 控制。
- `setLogFilter` 会清空 `#logScroll` 并按当前 filter 重建 Fragment 显示,支持运行时切换。
- `toggleAutoScroll` 切换自动滚动开关,文字在 “🔽 自动滚动” / “⏸️ 暂停滚动” 间切换。
- 自动滚动使用 `requestAnimationFrame` 批量合并,防止每条日志都触发强制布局。
### 来源分流
- **后端实时流**:`polling.js` 周期性 GET `/logs`,将每行正则 `^(.) (.+)\|(.+)$` 解析为 level/tag/message,直接 `addLog(...)` 投递。
- **前端主动日志**:连接、OTA、BLE、文件处理等各模块遇到关键流程点(断线、重启、握手、校验失败、SHA 不匹配等)调用 `addLog`,按 `connection`/`系统`/`诊断`/`上传`/`文件`/`OTA`/`内存`/`BLE` 等 tag 区分领域。
### 后端协议契约(由 poller 侧 enforce)
- `/system/status` 返回 JSON,含 `sys_state_code`、`relay_state`、堆低水位等字段。
- `/logs` 返回文本行,格式约定为 `LEVEL TAG|MESSAGE`(首字符为 `D/I/W/E`,然后空格、tag、竖线、正文),这是前后端共享的契约,而非任何框架约定。
## 4. 约定与约束
- **唯一入口**:全仓库前端日志通过 `common/log.js` 暴露的四函数进出;外部模块仅 import/addLog,不允许自行创建日志 DOM。
- **必须带 tag**:所有调用均传入二级分类 tag(如 `'连接'`、`'诊断'`、`'上传'`、`'文件'`、`'内存'`、`'系统'`、`'BLE'`、`'OTA'`),以便后期按 tag 过滤或筛选。
- **级别限定**:level 严格为 `D`(debug)、`I`(info)、`W`(warn)、`E`(error) 其一;UI 据此设置 CSS 颜色与过滤器类别。
- **XSS 防护**:level/tag/message 插入 innerHTML 前全部经 `escapeHtml` 处理;新增内容也需遵循此规则。
- **性能约束**:单条日志路径禁用多次 DOM query;高频重复内容依赖去重;批量追加走 `DocumentFragment`(filter 重建时);自动滚动批量化到下一 rAF。
- **轮询间隔**:`status` 与 `logs` 轮询默认 1000ms,上传期间暂停(`pausePollingForUpload`),GD32 中继期临时降至 400ms(`setStatusPollIntervalMs(400)`)以减少日志滞后,结束后恢复。
- **页面前置条件**:页面需存在 `#logScroll`、`#logCount`、`#autoScrollBtn`、`.log-filter` 四个选择器目标,否则 `clearLogs`/`setLogFilter`/`toggleAutoScroll` 无作用但不会抛错。
- **未覆盖场景**:Node.js / Python 后端脚本目录(`scripts/*.py`、`scripts/*.mjs`)普遍使用原生 `console.*`(见 grep 结果),不使用前端日志系统——说明本日志系统限定于浏览器端控制台。
## 5. 总结
这是一个围绕前端 DOM 的微型日志子系统:通过统一的 `addLog(level,tag,message)` 接口汇聚多模块的调试信息,并以环形缓冲 + 去重 + 自动滚动的形式挂载到页面上;同时通过 `polling.js` 把后端 ESP32 的 `/logs` 实时串流入这个面板,形成“设备端打 log + 前端集中呈现”的统一调试窗口。