Skip to content

Issue 222

clean edited this page Oct 14, 2026 · 2 revisions

Issue #222:本地日志记录(可配置目录、分等级)与终端风格实时日志窗口

背景与问题

Alune 之前没有统一的日志。服务端只有少量 console.log,桌面主进程的 console.error 只在开发控制台可见。打包后,Git 操作失败、SSH 异常、自动更新失败等问题没有可供排查的记录,也无法实时查看应用在做什么。

已确认的决策(2026-10-14)

待确认项 结论
日志窗口入口与快捷键 桌面菜单“帮助 → 查看日志”,快捷键 CmdOrCtrl+Shift+L;设置“日志”页提供“打开实时日志”按钮;状态栏暂不新增入口
Web 模式自定义目录 开放:填写本地服务所在机器上的绝对路径,由服务端检查是否可写;Web 模式没有原生目录选择器
默认保留策略 保留 14 天,总大小上限 200 MB;每天一个文件,单文件超过 20 MB 时分卷
DEBUG 写入频率 批量写入(200 ms 或 256 KiB 刷新一次,ERROR 立即刷新);默认最低等级为 INFO

目标与范围

  • 桌面主进程、本地服务(Nest)和前端未捕获错误统一写入本地 JSON Lines 文件,分 DEBUG、INFO、WARN、ERROR 四级。
  • 设置中可以修改目录、最低等级和保留策略,保存后立即生效,重启后保持。
  • 提供终端风格的实时日志窗口,支持筛选、搜索、暂停、清屏、复制和自动跟随。
  • 不包含:远程上传日志或崩溃报告、应用内导出诊断包。

方案与取舍

日志存储(apps/server/src/logging/)

  • 每个进程一个 LogStore 单例。桌面主进程和内嵌 Nest 服务运行在同一进程中,共享同一个存储,写入相同的文件和实时流。默认目录为 <数据目录>/logs:桌面端为 userData/logs,独立服务为 ALUNE_DATA_DIR 或 ~/.alune/logs。
  • 记录格式:{"time","level","source","message","context"},每行一条 JSON。文件名为 alune-YYYY-MM-DD.log,分卷为 alune-YYYY-MM-DD.N.log。
  • 清理:启动时、跨天、分卷、修改设置后以及每小时执行一次。先删除超出保留天数的文件,再从最早的文件开始删除,直到总大小低于上限;当前正在写入的文件不会被删除。
  • 设置保存在 <数据目录>/logging.json。保存时校验字段并测试目录是否可写,不可写时返回明确错误,原设置不变。启动时如果已保存的目录不可写,回退到默认目录,并在设置页显示原因。运行中目录被删除时先尝试重建,失败后回退到默认目录;日志写入失败不会影响应用。
  • 内存保留最近 1000 条作为历史。启动时从磁盘末尾的日志读取历史,供日志窗口打开时加载。

脱敏与隐私

  • 上下文中以 password、passphrase、secret、token、authorization、apiKey、privateKey、cookie、credential 等命名的键,值一律写为 [REDACTED]。
  • 文本中脱敏的内容:URL 中的用户名和密码、Authorization / PRIVATE-TOKEN / x-api-key 等请求头、Bearer/Basic/token 凭据、查询参数中的令牌、password=/token=/api_key: 等键值、私钥块,以及 GitHub、GitLab、OpenAI/Anthropic、Slack、Google 的已知令牌格式。桌面端每次启动生成的访问令牌在启动时注册,按原文脱敏。
  • 不记录文件内容、Diff、AI 请求及响应正文、HTTP 查询参数。DEBUG 级 Git 命令只记录子命令和选项名,不记录参数值,避免写入提交信息、路径和 URL。

记录范围

来源 内容 等级
git 所有写操作(暂存、提交、推送、拉取、合并、变基、储藏、标签、分支、Worktree 等)的开始、完成、耗时、仓库与本地/SSH 位置;失败原因 开始为 DEBUG,完成为 INFO;取消或请求校验失败为 WARN,Git 失败为 ERROR
git 每条 git 命令的子命令、选项名、退出码与耗时 DEBUG
ssh 连接中、已连接、已断开、失败(含服务器名称、主机和端口) DEBUG / INFO / ERROR
platform、ai 请求方法、主机和路径、状态码、耗时;失败与非 2xx 成功为 INFO,失败为 WARN
update 检查、可用、下载、安装和失败状态的变化 DEBUG / INFO / ERROR
app、server、process 启动(版本、平台、架构)、退出、服务关闭、启动失败、未捕获异常与未处理的 Promise 拒绝 INFO / ERROR
web 前端 error / unhandledrejection。前端 10 秒内去重,服务端每分钟最多记录 60 条 ERROR
nest:* 框架日志;路由映射等启动信息降为 DEBUG 对应等级

接口

  • GET/PUT /logs/settings、GET /logs/recent?limit=、POST /logs/client(桌面端统一加 /api 前缀,并沿用每次启动生成的令牌鉴权)。
  • Socket.IO:客户端发送 logs:subscribe 后加入 logs 房间;服务端每 100 ms 推送一批 log:entries,没有订阅者时不推送。

设置“日志”分类

依次为:“打开实时日志”和“打开日志目录”(Web 端为“复制日志目录路径”)、当前写入目录、自定义目录(桌面端可通过“选择目录…”选择)、“恢复默认目录”、最低记录等级、保留天数和总大小上限,最后是“保存设置”。

日志设置(自定义目录不可写时已回退到默认目录)

实时日志窗口

  • 桌面端:通过 logs:open-window IPC 打开独立 BrowserWindow,与主窗口共用会话分区,沿用鉴权头和 CSP。窗口只保留一个,重复打开时聚焦现有窗口;主窗口关闭时一并关闭,避免单独维持应用运行。IPC 只接受主窗口或日志窗口顶层页面的请求;日志窗口也可以使用复制权限。
  • Web 端:在名为 alune-logs 的标签页中打开 /logs。
  • /logs 单独渲染,不经过路由和应用外壳,也不读取或写入工作区设置,避免日志窗口覆盖主窗口状态。
  • 终端风格:等宽字体、深色背景(沿用终端的月光深色配色);DEBUG 灰、INFO 蓝、WARN 黄、ERROR 红,来源为青色。
  • 交互:多选等级(带计数)、按来源筛选、关键字搜索(消息、来源和上下文,⌘/Ctrl+F 聚焦搜索框)、暂停/继续(暂停期间累计待显示条数)、清屏(只清除显示)、复制(优先复制所选内容,否则复制当前筛选结果)、展开查看完整上下文。在底部时自动跟随;向上滚动后暂停跟随,并显示“回到底部 · N 条新日志”。
  • 打开时先加载最近 1000 条历史,再接入实时流,并去除两者重复的条目;最多显示 5000 条。

实时日志窗口:Git 操作、失败推送与已脱敏的前端错误

顺带修复

  • 开发环境下 apps/web/vite.config.ts 的 /socket.io 代理缺少 ws: true,所有 WebSocket(包括终端)都无法连接;已补上。生产和桌面端为同源访问,不受影响。

验收标准

  • 主进程、服务端和前端错误按等级写入本地 JSON Lines,格式统一。
  • 设置可以修改目录、等级和保留策略,保存后立即生效,重启后保持。
  • 目录不可写时,保存会报错;启动时回退到默认目录并显示原因。
  • 日志按天和大小分卷,并按保留天数和总大小清理。
  • 桌面端可以打开独立日志窗口,终端风格显示,新日志实时出现。
  • 支持按等级和来源筛选、关键字搜索、暂停、清屏、复制和自动跟随。
  • 日志中不出现令牌、密码或私钥(单元测试和桌面集成测试检查日志文件)。
  • Web 与桌面端、本地仓库:已验证。
  • 真实 SSH 主机与真实托管平台/AI 请求:代码路径已接入,未做现场验证。
  • Windows/Linux 原生桌面、打包版本长时间运行后的分卷清理与内存占用:未验证。

验证记录

日期:2026-10-14。环境:macOS arm64。浏览器验证使用隔离数据目录 /tmp/alune-222-data 和临时本地仓库,没有使用用户数据。

命令或场景 结果
npx jest(server) 33 个套件、367 项通过;其中 logging/log-store.spec.ts 10 项,覆盖脱敏、等级过滤、设置持久化与校验、跨天和 20 MB 分卷、保留天数和总大小清理、磁盘历史、不可写目录报错与启动回退、自定义目录
npm test -w web 294 项通过;设置下拉框清单测试由 11 个更新为 12 个
npm run test:unit -w desktop 45 项通过
npm run desktop:build + node apps/desktop/scripts/smoke.mjs 写入和恢复两个阶段均通过。新增 logs-smoke.cjs:检查 preload 桥接只暴露三个方法;通过 IPC 打开日志窗口,重复打开不新建窗口;日志窗口显示“实时”状态并加载启动历史;新日志实时出现;日志文件包含主进程启动记录,每行都是合法 JSON;每次启动生成的令牌不出现在页面和文件中
首次桌面集成测试 失败两次。第一次:本机暂存目录中的 @alune/shared 是旧副本(npm install --install-links 不刷新同版本的 file: 依赖),删除后重新暂存解决,CI 从干净环境开始,不受影响。第二次:dialogs-smoke 默认焦点断言失败,原因是重复打开日志窗口时,即使是测试模式也会显示并聚焦该窗口,抢占了主窗口焦点;改为遵守 show 选项后通过
浏览器(Vite + 隔离服务) 在临时仓库中暂存、提交、向带凭据的远程地址推送(失败):日志窗口实时显示开始、完成、耗时、失败原因和 DEBUG 命令;前端错误中的 token= 已脱敏;按 grep 检查文件,不含令牌片段;等级筛选、搜索、向上滚动后的“回到底部 · 4 条新日志”、暂停期间“4 条待显示”、继续、展开上下文均已验证
设置页 保存不可写目录时显示“没有写入权限”;已保存的目录改为不可写后重启服务,设置页提示已回退到默认目录

风险、未决事项与回退

  • 没有数据库迁移。新增文件 <数据目录>/logging.json 和 <数据目录>/logs/。回退代码后,这些文件可以保留或手动删除。
  • 脱敏基于规则,无法保证覆盖所有自定义凭据格式,因此默认不记录正文类内容。新增日志点时应只记录元数据。
  • DEBUG 会记录每条 git 命令,日志量较大,默认 INFO。
  • 前端错误上报依赖本地服务可用;本地服务不可用时,错误不会写入日志。
  • 本机重复暂存可能残留旧的 @alune/shared,可另行修复暂存脚本。

关联资料

Clone this wiki locally