Files

5.6 KiB
Raw Permalink Blame 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 + 前端集中呈现”的统一调试窗口。