可观测性
带脱敏的日志、诊断、可选的崩溃上报与分析。
本模板附带四层可观测性,全部按"默认安全"接线:带自动脱敏的日志、用于错误报告的 诊断数据、一个可选加入的崩溃上报插槽,以及一个可选加入的分析插槽。除非你完成 配置并且用户选择加入,否则没有任何东西离开设备。
日志
lib/main/logger.ts 封装了
electron-log。文件传输以 info
记录,控制台传输以 debug 记录,日志文件上限为 5 MB。startCatching 还会把
未捕获的主进程错误引流到可选的崩溃上报器。
用 writeLog 写日志,它会为每一行标注来源:
import { writeLog } from '@/lib/main/logger'
writeLog({ level: 'warn', message: 'Retry scheduled', context: { attempt: 2 }, source: 'main' })渲染进程的日志经由 app-log Conveyor 通道转发,落入同一个文件并标注 [renderer]
——而且它们的 context 在进入时会被脱敏(见下文)。日志文件位置遵循
electron-log 的默认值:
| 操作系统 | 路径 |
|---|---|
| macOS | ~/Library/Logs/<AppName>/main.log |
| Windows | %USERPROFILE%\AppData\Roaming\<AppName>\logs\main.log |
| Linux | ~/.config/<AppName>/logs/main.log |
应用内的日志页面(app/routes/LogsPage.tsx,logs Conveyor 领域)会显示
解析出的路径和一个"打开日志文件夹"按钮(logs-open-folder),这样提交报告的
用户无需了解操作系统惯例也能找到文件。
脱敏
lib/main/log-redact.ts 是一个卖点:它会把秘密从任何将被记录、放入诊断或发往
崩溃上报器的内容中擦除。redactSensitive 会遍历字符串、对象、数组与 Error
(防循环),并替换命中项:
| 模式 | 命中 |
|---|---|
| 敏感键 | password、secret、token、apikey / api_key、auth、cookie、session、credential、pin → 值变为 [redacted]。 |
| 敏感值 | 上述同类术语的 key: value / key=value 键值对。 |
| Bearer 令牌 | bearer <token> → bearer [redacted]。 |
| URL 中的凭据 | https://user:pass@host → https://[redacted]@host。 |
它被应用在秘密可能泄露的边界处:渲染进程的日志 context(app-log handler 中的
redactContext)、诊断数据,以及崩溃上报器的 beforeSend。这意味着日志行里的
一个漏网令牌、或诊断字段里的一个签名 URL,都会在被持久化或传输之前被剥除。
诊断
lib/main/diagnostics.ts 汇集一份构建与运行时元数据:应用名/版本/提交/构建时间、
Electron/Chromium/Node/V8 版本、操作系统平台/发行版/架构,以及日志路径。整份数据
在跨越 IPC 之前都会经过 redactSensitive,因此任何日后开始携带秘密的字段都会被
防御性地剥除。
关于页面通过 app-get-diagnostics 读取它,并提供一个"复制诊断信息"按钮——
用户可以把这段(已脱敏的)确切文本粘贴到错误报告里。
崩溃上报(可选加入插槽)
错误上报是一个可选加入插槽,在两件事同时成立前保持熄灭:配置了 DSN 且
用户已选择加入。lib/main/error-reporting.ts(ErrorReportingService)集成了
可选的 @sentry/electron:
- DSN 来自
MAIN_VITE_SENTRY_DSN(主进程构建变量)或SENTRY_DSN运行时 环境变量。没有 DSN → 服务处于惰性状态。 - 同意 是
settings.errorReportingOptIn(默认关闭,在高级设置标签页切换)。 若它为关闭,initialize()与report()都会直接返回。 - Sentry SDK 通过一个受保护的动态
require加载,因此若这个可选依赖未安装, 服务会记录一条警告并保持熄灭,而不是崩溃。 sendDefaultPii: false以及一个运行redactSensitive的beforeSend让被 捕获的事件保持已擦除。
主进程崩溃(经由日志器的 startCatching)与渲染进程错误(经由
app-report-renderer-error 通道转发到 reportRendererError)都会流入同一个
插槽。
产品分析(可选加入插槽)
app/services/analytics.ts 是第二个刻意做薄的可选加入插槽——与错误插槽相互独立。
默认不附带任何分析提供方;买家把它指向自己的采集器。除非以下两点同时成立,
否则它是彻底的空操作:
- 构建时设置了
VITE_ANALYTICS_ENDPOINT(analyticsConfig.configured),且 - 用户选择加入(
settings.analyticsOptIn,默认关闭)。
启用后,track() / page() 会以"发后不理"的方式向该端点发一个 JSON POST
(可携带一个可选的 VITE_ANALYTICS_WRITE_KEY bearer 令牌,keepalive: true)。
事件只携带调用方传入的内容外加一个本地生成的匿名 id——绝不包含 Supabase 会话、
JWT、邮箱或文件路径。网络失败会被吞掉,因此分析绝不会干扰应用。
两个可选加入插槽都默认关闭并以熄灭状态出厂。开启它们是一个刻意且有记录的动作 ——先设置环境变量/DSN,再尊重用户在高级标签页的同意。参见 环境变量。
渲染进程错误体验
两个 React 错误边界外加一个带主题的错误路由,让渲染进程的失败不至于变成一个 空白窗口:
RouterErrorBoundary(app/app.tsx)捕获路由树内的错误,并可通过导航恢复。ErrorBoundary(app/renderer.tsx)是包裹整个应用的最后兜底。ErrorPage(/error)是为路由错误显示的带主题界面。
渲染进程错误同样会被记录,并(在插槽开启时)被上报,因此一次崩溃既对用户可见, 也为你所捕获。
