本文介绍如何初始化用户体验监控 Electron SDK。Electron SDK 采用 main + renderer 两步初始化:main 侧持有项目配置与上报通道,renderer 侧持有窗口内采集能力。
操作步骤
1. 在主进程入口尽早初始化 main SDK。
id(项目标识)与 url(上报基地址)为必填,二者缺失会初始化失败并返回禁用态 client;version 会自动取 app.getVersion(),无需手工传入。设备身份 aid 与用户标识 userId 不在 main 配置,请在 renderer 侧传入。import { init } from 'tdem-electron-sdk/main';const tdem = init({id: 'project-id',url: 'https://dem.rumt-zh.com',});
注意:
initElectronMain 是含义更明确的长期正式名称,与 init 完全等价,可任选其一。必须在创建任何
BrowserWindow 或自定义 Session 之前调用。init() 为同步调用,返回 main client;初始化失败不抛异常,返回安全禁用态 client(见下文)。2. 创建
BrowserWindow。无需配置 TDEM preload,SDK 已在 Session 层注入;已有业务 preload 原样保留。const window = new BrowserWindow({webPreferences: {contextIsolation: true,nodeIntegration: false,},});
3. 在渲染进程入口初始化 renderer SDK。renderer 不传
id / url / version。import { init } from 'tdem-electron-sdk/renderer';const tdem = await init({sessionReplay: {sessionSampleRate: 0.1,},});tdem.captureMessage('renderer started');tdem.track('settings_saved');
无 renderer 特定配置时可直接调用:
const tdem = await init();
注意:
renderer
init() 是 Promise,必须 await。初始化期间通过自动 preload 获取 main-owned bootstrap。若 main 未初始化或 preload 未注入,会直接拒绝,不会降级为浏览器直传。
同一个窗口只会创建一个 renderer 实例;重复调用会输出诊断警告并返回首次创建的 client。
上下文隔离与桥接方式
SDK 自动根据
process.contextIsolated 选择桥接方式:配置 | 方式 |
contextIsolation: true | contextBridge.exposeInMainWorld() |
contextIsolation: false | 冻结后安装到 shared world,并输出 reduced-security 警告 |
推荐开启上下文隔离。关闭时功能可用,但 SDK 无法隔离页面脚本与 preload 环境。
放行远程页面(开发服务器 / 可信远程站)
自动 preload 只为主 frame 开放 bridge,默认允许
file: 与业务自定义协议,拒绝远程 HTTP(S) renderer。加载可信远程页面或 Vite/Webpack 开发服务器时,必须由 main 显式授权 origin:init({id: 'project-id',url: 'https://dem.rumt-zh.com',renderer: {allowedOrigins: ['https://app.example.com', 'http://localhost:5173'],},});
初始化失败与禁用态
初始化失败不会向业务代码抛异常。SDK 输出一条诊断错误,并返回安全的禁用态 client:
const tdem = init({ id: 'project-id', url: 'https://dem.rumt-zh.com' });if (!tdem.enabled) {// 可选:接入业务日志或告警。不要因此阻断应用启动。console.error('TDEM 初始化失败', tdem.initializationError);}
禁用态 client 的
captureException() / captureMessage() / track() / measure() / dumpMemory() / flush() / close() / dispose() 都是安全空操作,不会影响主进程继续启动;它不会启用 recording transport 或浏览器直传 fallback,也不会被记为已初始化,因此修正配置或运行环境后可以再次调用 init() 重试。初始化失败时,
initializationError 是一个 TdemElectronInitializationError 实例(继承自 Error),可通过 code 字段区分具体原因:INVALID_PROJECT_ID:id 为空或全为空白字符。INVALID_COLLECTOR_URL:url 不是合法 URL,或协议不是 http: / https:。ELECTRON_RUNTIME_UNAVAILABLE:require('electron') 失败,即不在 Electron 主进程内调用。INVALID_ELECTRON_RUNTIME:缺少 ipcMain、BrowserWindow.fromWebContents,或运行时不提供 net.fetch() / net.request()。UNSUPPORTED_ELECTRON_VERSION:Electron 主版本低于 23。INITIALIZATION_IN_PROGRESS:上一次初始化尚未结束时重复调用 init()。该错误类与
init 同入口导出,可按 code 分流处理(例如版本过低提示升级、配置错误提示修正):import { init, TdemElectronInitializationError } from 'tdem-electron-sdk/main';const tdem = init({ id: 'project-id', url: 'https://dem.rumt-zh.com' });if (!tdem.enabled) {const error = tdem.initializationError;if (error instanceof TdemElectronInitializationError) {switch (error.code) {case 'UNSUPPORTED_ELECTRON_VERSION':// 运行环境不满足,提示升级 Electronbreak;case 'INVALID_PROJECT_ID':case 'INVALID_COLLECTOR_URL':// 配置错误,提示检查 id / urlbreak;default:break;}}}
注意:
这里的错误码是 main 初始化 专属的一套;网络代理层另有
INVALID_HEADER / REQUEST_TOO_LARGE 等错误码,二者互不通用。上报域名
请根据项目所在地域选择对应的上报域名。不同站点的数据相互隔离,跨站填写会导致数据无法入库。
站点 | 上报域名 |
国内站 | https://dem.rumt-zh.com |
新加坡站 | https://dem.rumt-sg.com |
美国站 | https://dem.rumt-us.com |
接入时填写站点根地址即可,无需拼接路径,SDK 会自行拼接具体协议端点。
完整配置项说明
Electron 的配置分属两个进程,请勿混写:main 配置只在 main 的
init() 里生效,renderer 配置只在 renderer 的 init() 里生效。main
基础配置
id 与 url 为必填,由 SDK 强制校验(缺失会初始化失败并返回禁用态 client);version、sessionId 均有自动兜底,无需手工传入。设备身份(aid)与用户维度(userId)均由 renderer 侧承担,main 配置不接受 aid 与 userId。配置项 | 说明 |
id | 必填。项目标识,对应请求体中的 project_key。 |
url | 必填。采集服务基地址,SDK 会自行拼接具体协议端点( /api/v1/config、/api/v1/collect/events、/api/v1/collect/replay)。国内站填 https://dem.rumt-zh.com,详见 上报域名。 |
version | 应用版本号。默认自动取 app.getVersion(),不可用则回落 1.0.0,因此可不配置;若业务版本号与 package.json 不一致,建议显式配置(显式配置优先)。renderer 侧统一沿用 main 的版本,不可单独覆盖。 |
设备身份
aid 不在 main 侧配置。它由 renderer 侧 init() 的 aid 选项承担,SDK 通过 localStorage 的 TDEM_ID 维护跨启动稳定的设备身份,详见 配置说明。配置项 | 说明 |
sessionId | 预置会话 ID,用于让 main 与外部会话体系对齐。不传由 SDK 生成。 |
defaultTags | 全局默认标签( Record<string, string>)。 |
captureUncaughtException | 是否捕获主进程未捕获异常,默认开启(仅显式传 false 才关闭)。 |
captureUnhandledRejection | 是否捕获主进程未处理的 Promise 拒绝,默认开启。 |
beforeReport / beforeRequest / afterRequest | 上报与请求钩子。 |
网络与进程监控
配置项 | 说明 |
api | slowThreshold / isSlowApi:慢请求阈值(ms);isSlowApi 传自定义判定函数,命中即按慢请求上报。判定优先级为 isSlowApi → slowThreshold → 内置默认1000ms。apiDetail / reportRequest:明细与全量成功请求上报。sseStallThresholdMs:SSE 卡流阈值(ms),省略或非法值按60000。ignoreHackReg / injectTraceHeader / injectTraceUrls / injectTraceIgnoreUrls / traceFlag:忽略劫持的 URL 正则、全链路追踪头及其注入白名单 / 跳过列表与标记开关。retCodeHandler / ret / reqParamHandler / resBodyHandler / resHeaders / reqHeaders:返回码解析与修正、报文处理。ret 指定响应体中承载返回码的键名数组(小写比对),默认 ["ret", "retcode", "code", "errcode"]。覆盖范围: electron.net.request / net.fetch、Node http / https,以及 undici / 全局 fetch。识别 text/event-stream 后只出 sse_call。该开关独立于 renderer 的 api,且不会通过 bootstrap 下发到窗口。 |
processSignal | 壳层进程健康信号,默认 开启。开启后 main 上报 event_type=process_signal,event_name 为 render_process_gone / child_process_gone / unresponsive / responsive。其中 render_process_gone、unresponsive、responsive 会带窗口身份 event_properties.window_id / web_contents_id;child_process_gone 不带,改带 process_role / service_name / exit_reason / exit_code / repeat_count。设 false 只关闭这类信号,不影响 dump 补报或 appMetrics。 |
nativeCrash | native crash 采集,兼容 boolean 与对象两种形式。 true:启用,不附加自定义日志;false:禁用。对象形式省略 enabled 时默认启用;logPath 可指向普通文件或目录。启用后 SDK 内部使用 crashReporter.start({ uploadToServer: false }),业务不得重复调用 crashReporter.start()。dump 补报走 event_type=crash / crash_type=native_crash,event_properties.process_type 按 sidecar 或 dump ASCII ptype 归因(browser → main,无法识别为 unknown)。 |
应用指标、文件 IO 与内存 Dump
配置项 | 说明 |
appMetrics | 应用内存 / CPU 周期上报,默认关闭。 true 或 { enabled: true, intervalMs } 开启。上报 event_type=app_metrics,detection_source=app_metrics,measurements 含 app_memory / memory_size / free_memory 以及内存 / CPU 分桶与 app_cpu(字节 / 百分比合计,允许 > 100)。不是网页 JS 堆,不是 custom,也不是旧的 memory_usage。intervalMs 非法(非有限值或 ≤0)时回落默认60000。历史配置名 memoryReport 仍作为同义别名被接受;两者同时出现时以 appMetrics 为准。 |
filesystemIo | 文件系统 IO 采集,默认关闭。开启后 main 对每次本地文件读 / 写 / 打开上报一条 event_type=file_io。writeFile / appendFile 内部再走 open 时只记外层一条(对齐 sentry-electron / OpenTelemetry fs instrumentation)。SDK 自己的 preload、 tdem-memory-dumps 与 native crash 产物目录不会上报。开关经 bootstrap 下发给全部 renderer;窗口拿不到 Node fs 时静默,不失败。file_io 与 dump 的 file_path / path 可能包含用户目录,必须走现有脱敏 / 字段替换管线,不要新增采集入口。 |
手动内存 dump 只在 main 可用,不依赖上述两个开关,文件只留本地、不上报附件:
const result = await tdem.dumpMemory();if (result.ok) {// result.filePath 与 custom / memory_dump 的 event_properties.file_path 相同}
禁用态 client 的
dumpMemory() 返回 { ok: false },不写文件、不抛异常。Node 子进程注入
配置项 | 说明 |
nodeInjection | 把 sidecar / CLI 纳入同一 TDEM 会话,默认关闭。省略或 false 时行为与未接入本能力前一致,不会改写任何 Node 子进程。必须在创建窗口和缓存 spawn 之前于 main 显式打开。接入方只改 Electron 配置,不要在 sidecar 或 CLI 里再 init tdem-node-sdk。Electron main 不会调用 Node SDK 根入口 init();它通过 tdem-node-sdk/client 的 createNodeClient 组合 httpIntegration / filesystemIntegration,并在开启时装配 childInjectionIntegration。Node 事件经 EventSink 进入既有 Electron Core pipeline,不另建 Node HTTP 上报通道。 被注入子孙进程用 Node http / https 直报 {url}/api/v1/collect/events,不走 Electron IPC。打包须把 Node SDK 留在 asar 外,详见 SDK 集成-打包注意事项。 |
renderer.allowedOrigins |
renderer
配置说明
renderer 配置镜像 Web SDK 的公开面,但项目 ID、Collector URL 与版本由 main 持有,renderer 不接受
id / url / version。其中 userId 需传入,aid 由 SDK 自动生成,其余为可选配置。配置项 | 说明 |
userId | 必填。业务用户标识,对应上报体的 context.user_id;Electron 的用户维度聚合由 renderer 侧承担(main 配置不接受 userId),不传则控制台无法按用户维度聚合。 |
aid | 设备标识, boolean | string。传非空字符串时直接采用该值;传 true 或留空时,读取 localStorage 的 TDEM_ID,不存在则生成 UUID 并写回,因此同一应用下重启后保持同一设备身份。 |
env | 运行环境标识。非 production 时控制台默认筛选可能看不到数据。 |
debug | SDK 诊断日志,默认 false。 |
defaultTags | 全局默认标签。 |
pageUrl | 自定义页面 URL。 |
spa | SPA 路由模式。 |
delay / repeat / reportImmediately | 上报调度相关。 |
beforeReport / beforeRequest / afterRequest | 上报与请求钩子。 |
onError | 是否采集错误, boolean。 |
api | 接口与 SSE 监控, boolean | ElectronApiMonitorConfig。SSE 跟随本开关(无独立 SSE 开关),默认卡流 60000 ms,可通过 api.sseStallThresholdMs 覆盖;覆盖 EventSource 与 fetch / XHR 的 text/event-stream。同一个 EventSource 实例的浏览器自动重连合入同一条 sse_call。子字段与 main 侧 api 大体相同,但 resourceTypeHandler(自定义资源类型判定)与 usePerformanceTiming(改用 Resource Timing 计算耗时)仅 renderer 侧生效,main 不消费。 |
assets | 静态资源测速, boolean。传 true 开启;采集 img / css / script / link / audio / video 六类资源的加载耗时。 |
pagePerformance | 页面性能与 Web Vitals, boolean | { firstScreenInfo, urlHandler, slowThreshold, isSlowPage }。 |
webVitals | Web Vitals 上报, { manualReport }。 |
click | 点击行为, { ignoreIds, ignoreClasses }。 |
console | 控制台日志采集, { level }。 |
bridge | JSBridge 监控, { target, methods }。 |
websocket | WebSocket 监控, { enabled }。 |
lagMonitor | 卡顿监控, { enabled, threshold, noResponseThreshold, sampleRate }。 |
blankScreen | |
memoryMonitor | Electron renderer 会强制关闭该开关,不再报页面堆。纯 Web 接入不受影响。页面级内存请改用 main 的 appMetrics。 |
reportRetry | 上报重试, { maxRetryCount, retryInterval, whenRetryEndStillFail }。 |
sessionReplay | |
struggleMonitor | |
resourceFilterPolicy | 资源过滤策略, { ignoreInternalSchemes, ignoreAsarResources, ignoreDevtoolsResources, customMatchers }。 |
白屏检测(blankScreen)
配置同 Web SDK,默认开启。对象形式可覆盖以下字段:
配置项 | 说明 |
enabled | 是否开启,默认 true。 |
containers | 检测容器选择器,默认 ['body', 'html', '#app', '#root']。 |
ignoreContainers | 不参与检测的容器选择器,默认 []。 |
containerMatchers | 容器匹配器,按标签 / id / class 指定,默认 { tagName: ['body', 'html'], id: ['app', 'root'], className: [] }。 |
ignoreMatchers | 忽略匹配器,结构同 containerMatchers,默认全为空数组。 |
detectStartPosition | 检测起始坐标,默认 { x: 0, y: 0 }。 |
emptyElementsPercent | 空白元素占比阈值(%),默认 70。 |
sameElementsPercent | 相同元素占比阈值(%),默认 70。 |
debounceDuration | 防抖时长(ms),默认 2000。 |
everySideSampleNumber | 每边采样点数,默认 9。 |
disableSameElementsCheck | 是否关闭相同元素检查,默认 true。 |
ignoreElesWhenDomChange | DOM 变更时忽略的元素选择器,默认 []。 |
reDetectInterval | 复检间隔(ms),默认 2000。 |
samePointDepth | 相同点判定深度,默认 5。 |
customBlankScreenDector | 自定义白屏检测函数,返回布尔值;设置后由业务自行判定白屏,默认 null。 |
会话回放(sessionReplay)
配置项 | 说明 |
enabled | 总开关。 |
sessionSampleRate / errorSampleRate | 全量与错误采样率,取值 0 - 1。 |
stickySession | 是否粘性会话。 |
privacy | |
maskAllInputs | 顶层简写,遮罩所有输入框,默认 true。 |
slowClickTimeout / slowClickThreshold / rageClickCount / rageClickWindow | 慢点击与连点配置。 |
enableMutationMerge / mutationMergeWindowMs / mutationBreadcrumbLimit / mutationLimit | DOM 变更合并与上限。 |
useCompression / eventsCompression | 压缩开关。 |
uploadUrl | 自定义上传地址(一般无需配置,默认走 main 代理)。 |
onError / beforeSend | 错误回调与发送前改写(返回 null 可丢弃)。 |
debug | 回放调试日志。 |
子配置:
privacy配置项 | 说明 |
maskAllText | 制品未读取该键(类型声明存在但运行时无消费点)。遮罩文本请改用 mask 数组。说明: 当前版本不生效。 |
maskAllInputs | 遮罩所有输入框,默认 true。 |
mask | 遮罩文本的 CSS 选择器数组,默认 [".tdem-mask", "[data-tdem-mask]", "[data-sensitive]", ".sensitive", ".private"]。传入值追加合并。 |
block | 阻止录制的 CSS 选择器数组,默认 [".tdem-block", "[data-tdem-block]", ".secret", "[data-secret]", "iframe[srcdoc]:not([src])", ".replayer-wrapper"]。追加合并。 |
ignore | 忽略录制的 CSS 选择器数组,默认 [".tdem-ignore", "[data-tdem-ignore]", "input[type=\\"file\\"]", "input[type=\\"password\\"]"]。追加合并。 |
maskInputOptions | 按输入类型遮罩,默认 { password: true, email: true, tel: true, text: false };与默认值逐键合并(可只覆盖其中几项)。 |
blockAllMedia | 是否屏蔽所有媒体元素,默认 true。开启时把媒体选择器(img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"])并入 blockSelector,并忽略 background-image 样式捕获。 |
autoDetectSensitiveData | 是否启用自动敏感数据检测(手机号 / 身份证 / 邮箱 / 银行卡 / IP),默认 true。置 false 且未自定义 maskTextFn 时,文本脱敏整体关闭。 |
maskTextFn | 自定义文本脱敏函数,签名 (text, element?) => string;在自动脱敏之后应用(可二次覆盖),二者同时生效。 |
挣扎事件检测(struggleMonitor)
配置项 | 说明 |
enabled | 是否开启,默认 false(未配置 struggleMonitor 时关闭);传对象时默认 true。 |
ruleVersion | 规则版本号,默认 '1.0.0'。 |
payloadAllowlist | 自定义事件 payload 字段白名单,默认 []。 |
click | |
performance | |
form | |
navigation | |
custom |
子配置(死点击 / 愤怒点击 / 错误点击检测):
click配置项 | 说明 |
click.enabled | 默认 true。 |
click.deadClickWindowMs | 死点击判定窗口(ms),默认3000。 |
click.rageClickWindowMs / click.rageClickCount | 愤怒点击窗口(1000ms)/ 次数阈值(5)。 |
click.errorClickWindowMs | 错误点击判定窗口(ms),默认3000。 |
click.resolveOnDomMutation | DOM 变更即视为有效操作,消解死点击,默认 true。 |
click.resolveOnSpaNavigation | SPA 路由变化即消解,默认 true。 |
click.resolveOnExternalNavigation | 跳转外链或触发下载即消解,默认 true。 |
click.resolveOnWindowOpen | 调用 window.open 即消解,默认 true。 |
click.resolveOnPageUnload | 页面卸载即消解,默认 true。 |
click.resolveOnFormAction | 表单变更或提交即消解,默认 true。 |
click.resolveOnApiSuccess | 接口请求成功即消解,默认 true。 |
click.ignoreDeadClickSelectors | 追加死点击忽略选择器,默认 []。 |
click.effectiveActionSelectors | 追加有效操作选择器,默认 []。 |
click.ignoreDeadClickSelectors / click.effectiveActionSelectors 与 HTML 属性 data-tdem-ignore-dead-click / data-tdem-effective-action 会合并消费,属性式标记适合逐个元素标注,选择器式适合批量匹配。子配置(慢页面检测):
performance 配置项 | 说明 |
performance.enabled | 默认 true。 |
performance.slowPageLcpThreshold | 慢页面 LCP 阈值(ms),默认4000。 |
performance.slowPageInpThreshold | 慢页面 INP 阈值(ms),默认500。 |
子配置(表单交互异常检测):
form配置项 | 说明 |
form.enabled | 默认 true。 |
form.fieldIdAttr / form.formIdAttr | 字段 / 表单标识属性,默认 'data-field-id' / 'data-form-id'。 |
form.longFocusThresholdMs | 长时间聚焦阈值(ms),默认20000。 |
form.incompleteMinFieldRatio | 未完成表单的最小已填写字段比例,默认0.3。 |
form.zigzagMinCount | 表单来回切换最低次数,默认2。 |
子配置(前进后退检测):
配置项 | 说明 |
navigation.enabled | 默认 true。 |
navigation.backForwardAsStruggle | 默认 true。开启后前进 / 后退操作会上报 back_forward 类型的挣扎事件,用于识别「用户反复回退说明没找到想要的内容」。 |
子配置(自定义检测器):
custom配置项 | 说明 |
custom.enabled | 默认 true。 |
custom.detector | 自定义检测函数,签名 ({ event, tdem }) => ReportStruggleParams | ReportStruggleParams[] | null | void。入参可拿到当前事件与 SDK 实例,返回一个或多个挣扎参数对象;返回空值表示不产生挣扎事件。SDK 会为返回值自动补齐未填字段:type 默认 custom_struggle。ownerType 默认 app。ownerKey 默认当前页面地址。ruleVersion 与 payloadAllowlist 继承顶层配置。detectionSource 固定为 custom。 |