本文详细介绍用户体验监控 Electron SDK 的各功能接口,帮助您更灵活、深度地使用 SDK。
两个进程的入口与 API 边界
SDK 只公开两个入口,各自返回不同的 client,API 面不完全相同:
入口 | 返回 |
tdem-electron-sdk/main | ElectronMainClient(同步返回) |
tdem-electron-sdk/renderer | Promise<ElectronRendererClient> |
共同的上报 API
两个 client 都实现同一套
ElectronReporter 接口:captureException(error: Error | string, payload?): void;captureMessage(message: string, payload?): void;track(name: string, payload?): void;measure(name: string, duration: number, payload?): void;
四个方法各自上报什么事件、以及入参校验如下(两侧一致):
captureException(error, payload?):上报 JS 异常,落 event_type=js_error,异常栈在 data.exception_stacks。error 传字符串时会先包装为 Error;传其它类型仅输出警告、不上报。captureMessage(message, payload?):上报日志,落 event_type=log,消息文本在 data.event_properties.message。message 须为非空字符串。track(name, payload?):上报业务自定义事件,落 event_type=custom、data.event_name=name。name 须为非空字符串。measure(name, duration, payload?):上报业务自定义耗时,落 event_type=custom、data.measurements.duration(毫秒)。duration 须为数字且满足 0 ≤ duration ≤ 2147483646。调用示例(两侧方法签名一致)。main 侧可直接调用;renderer 侧
init() 返回 Promise,须先 await 取得 client 再调用(见下方示例注释),详见 SDK 初始化-操作步骤:// ⚠️ main 侧可直接调用;renderer 侧 init() 返回 Promise,须先 await 取得 client:// const tdem = await init(); —— 未 await 直接调用会抛 TypeError// 异常tdem.captureException(new Error('支付失败'), { tags: { module: 'pay' } });tdem.captureException('字符串错误');// 消息tdem.captureMessage('checkout retry', { level: 'warn' });// 自定义事件tdem.track('pay_start', {tags: { source: 'banner' },properties: { amount: '99' },});// 自定义测速(毫秒)tdem.measure('onload', 1200);
事件选项并非每个字段都对所有方法生效(类型上
ElectronCapturePayload 加 level,ElectronTrackPayload / ElectronMeasurePayload 加 payload):选项 | 类型 | captureException | captureMessage | track | measure |
tags | Record<string, string> | ✓ | ✓ | ✓ | ✓ |
properties | Record<string, string> | ✓ | ✓ | ✓ | ✓ |
pageUrl | string | ✓ | ✓ | ✗ | ✓ |
replayId | string | ✓ | ✓ | ✗ | ✓ |
level | 'info' | 'warn' | 'error' | ✓ | ✓ | ✗ | ✗ |
payload | Record<string, any> | ✗ | ✗ | ✗ | ✓ |
tags:本条事件的标签。properties:本条事件的属性(Record<string, string>)。pageUrl:覆盖本条事件的页面 URL。track() 会忽略该字段,自定义事件的页面沿用当前会话值。replayId:关联到指定回放。track() 会忽略该字段,自定义事件的回放沿用当前会话值。level:仅 captureException / captureMessage 支持,取值 info / warn / error。captureMessage 写入 data.log_level,其他值静默回落 info。captureException 写入 data.event_properties.capture_level,原值透传、不做校验。说明:
两者落库字段名不同(
log_level 与 capture_level),排查时不要找错位置。payload:仅 measure 真正写入。captureException / captureMessage 的选项中没有该字段;track 虽在类型上声明,运行时不会被读取。保留原始类型(
number / boolean 原样写入,不做字符串化);结构受限:字符串 ≤256字符、数组 ≤10项、对象每层 ≤20键、深度 ≥2折叠为 JSON(≤256字符);
与
properties 的区别:后者强制 String() 转换且每值上限1024字符。注意:
properties 的值类型为 string,非字符串值请自行序列化;所有值会被 String() 强制转换并每值截断至1024字符。main client 专属 API
interface ElectronMainClient extends ElectronReporter {readonly enabled: boolean;readonly initializationError?: Error;readonly config: ElectronMainConfig;readonly sessionId: string;readonly context: ElectronContextFields;getContext(): ElectronContextFields;dumpMemory(): Promise<MemoryDumpResult>;dispose(): void;flush(options?: { timeoutMs?: number }): Promise<void>;close(options?: { timeoutMs?: number }): Promise<void>;}
enabled:main SDK 是否已完成初始化并在上报。禁用态为 false。initializationError:enabled 为 false 时保留初始化失败原因。运行时为 TdemElectronInitializationError 实例(继承自 Error),可通过只读的 code 字段区分失败类型(INVALID_PROJECT_ID / INVALID_COLLECTOR_URL / ELECTRON_RUNTIME_UNAVAILABLE / INVALID_ELECTRON_RUNTIME / UNSUPPORTED_ELECTRON_VERSION / INITIALIZATION_IN_PROGRESS),错误类与 init 同入口从 tdem-electron-sdk/main 导出。详见 初始化失败与禁用态。config / sessionId / context:只读配置、会话 ID 与上下文字段。getContext():获取当前上下文字段快照。dumpMemory():手动内存 dump,返回 { ok, filePath?, error? }。dispose():同步释放本次 main SDK 实例。只停止生产新事件,不等待已提交上报。flush(options?):冲刷已提交事件,返回 Promise。close(options?):先停生产,再走与 flush 相同的 drain → 注册 send → 快照 barrierSeq 顺序,最后才释放 scheduler。注意:
dispose() 不是 barrier。需要确保数据落盘时,请在 dispose() 前 await tdem.flush() 或 await tdem.close()。flush() / close() 默认超时 5000ms,可通过 { timeoutMs } 覆盖。同一个 Electron 主进程只会创建一个 main 实例。重复调用
init() / initElectronMain(),或混用两个入口,都不会重复注册全局资源、也不会抛重复初始化错误;SDK 输出一条诊断警告并返回首次创建的 client,始终以第一次初始化配置为准。如需换配置,须先 dispose() 完整释放,再重新 init()。运行时 client 对象还挂了两个未在
ElectronMainClient 类型面声明的方法,属高级用法,常规接入不建议使用:reportTDEMEvent(event):直接投递完整的 TDEMEvent(单条或数组),绕过 captureException / captureMessage / track / measure 的封装。request(config):经 main 的 HTTP runtime 发送原始请求,返回 Promise。优雅退出示例如下:
app.on('before-quit', async () => {await tdem.close({ timeoutMs: 3000 });});
renderer client 专属 API
interface ElectronRendererClient extends ElectronReporter {readonly config: ElectronRendererConfig;readonly context: ElectronContextFields;getContext(): ElectronContextFields;getOriginalPageIdentity(): { pageUrl: string; from: string };dispose(): void;}
getOriginalPageIdentity():获取原始页面身份(pageUrl 与来源 from)。dispose():卸载文件系统 IO 包装、释放 renderer transport,并允许下一次 init() 重新安装新 wrapper。init() 返回的是稳定 facade:main 侧同步返回 client,renderer 侧返回 Promise<ElectronRendererClient>,须先 await init() 再使用其 captureException / captureMessage / track / measure / dispose 方法;SDK 不会暴露底层 Web SDK / Core 实例。上下文字段
context / getContext() 返回以下字段:字段 | 说明 |
runtime | 固定为 electron |
processType | main 或 renderer |
sessionId | 会话 ID |
windowId | 窗口 ID |
webContentsId | webContents ID |
appVersion | 应用版本 |
Main 会覆盖 renderer 请求中的这些可信字段,renderer 无法覆盖(下列为请求体字段名,命名口径与上方
context 对象不同,例如 process_type 对应 processType):project_keysessionId / session_idruntimeprocess_typewindowIdwebContentsIdappVersionIPC 代理约束
renderer 的采集请求经 main 的 IPC 代理转发,代理有明确的安全约束:
允许的 endpoint 类型仅
config / events / replay 三类。允许的请求头仅
content-type 与 x-app-id,其他头会被拒绝(INVALID_HEADER)。请求体有字节上限(10 MiB),超出返回
REQUEST_TOO_LARGE。请求方法仅
GET / POST。违反上述约束时返回结构化错误码:
INVALID_HEADER(请求头不在白名单)/ INVALID_ENDPOINT_KIND(kind 非 config、events、replay)/ INVALID_REQUEST(缺少 requestId 或方法非 GET、POST)/ REQUEST_TOO_LARGE(超出 10 MiB)。本地事件批量与背压
main 本地事件会在 SDK 内按有界队列批量发送。每次 IO 仍保留为一条独立事件,不等于每次 IO 单独创建一次 HTTP 请求。网络拥塞或队列达到安全上限时,SDK 会优先保护错误 / 崩溃事件,并允许丢弃
file_io 等 bulk 遥测,避免监控拖垮宿主应用。静默降级清单
Electron SDK 在多种异常路径上都是「失败降级」,不打断宿主:
main 初始化失败:返回禁用态 client,不抛异常,不影响应用启动;修正后可重试。
renderer 拿不到 bootstrap:直接拒绝初始化,不降级为浏览器直传。
Node 子进程注入识别 / 改写 / preload 缺失或自动初始化抛错:不向业务 throw、不调用
process.exit,原进程继续启动。Windows 拿不到 Node
fs 时:filesystemIo 静默不生效,不失败。native crash 附件处理:Worker 超时放弃附件并继续上报 crash;ZIP 超限只降级日志附件,minidump 仍正常上报。
支持范围与限制
需 Electron
>=23。主进程 API 覆盖
electron.net.request / net.fetch、Node http / https、undici / 全局 fetch。Node 注入只针对明确目标:可执行文件名为
node / node.exe,或本次启动设置了 ELECTRON_RUN_AS_NODE=1 且可执行文件等于当前壳 process.execPath;fork 始终按 Node 模块处理。git / python / shell / 未信任的 Electron 可执行文件默认不注入。本期不覆盖:GPU / Network / Crashpad 等 Chromium 工具进程、
utilityProcess、C++ posix_spawn,以及 SDK 初始化前已缓存的原始 spawn 引用。注入后的 Node 进程采集 HTTP、SSE、
file_io 与未捕获异常 / unhandledRejection;不采集 DOM、页面、Electron preload IPC、Session Replay、renderer 卡顿、Node native crash、Electron main crash dump。SSE 不采集消息正文。诊断日志不会输出 Cookie、Authorization 或请求 body。
注意:
在 macOS 上,SDK 会对
node-pty / @lydell/node-pty 的 JS spawn 做同样的 -r 改写。PTY 库缺失或包装失败时失败降级,原始启动不受影响。