# 将 Electron 应用接入 ARMS 用户体验监控

阿里云 ARMS 用户体验监控通过 `@arms/rum-electron` SDK 监控 Electron 桌面应用。SDK 在主进程一次 `init()` 即覆盖主进程与渲染进程，自动采集异常、原生崩溃、应用启动指标、主进程 HTTP/tRPC 调用、渲染进程 PV、性能、Web Vitals、API、白屏等数据，并由主进程统一上报至 ARMS 控制台。

## 前提条件

| 项目 | 要求 |
|------|------|
| Electron | `>= 28.0.0` |
| `@trpc/server` | 可选，仅在使用 tRPC 监控时由业务侧安装 |
| ARMS 服务 | 已开通用户体验监控服务，并在控制台创建应用获得 `endpoint` |

## 创建应用

1. 登录 [ARMS 控制台](https://arms.console.aliyun.com/#/home)
2. 在左侧导航栏选择 **用户体验监控** > **应用列表**，并在顶部菜单栏选择目标地域
3. 单击 **添加应用**
4. 在 **创建应用** 面板选择 **Electron**
5. 输入应用名称与描述后单击 **创建**。创建成功后系统自动生成 `endpoint`，记录用于初始化

---

## 安装 SDK

在 Electron 项目根目录执行：

```bash
npm install @arms/rum-electron
```

> `electron` 与 `@trpc/server` 都是 `peerDependency`（optional）。`electron` 需由项目自行安装且版本 `>= 28.0.0`；`@trpc/server` 仅在使用 tRPC 监控时安装。

---

## 初始化 SDK

在主进程入口（如 `main/index.ts`）**最顶部** `import` SDK 并调用 `init()`。

> **重要时序** SDK 模块顶层会调用 `protocol.registerSchemesAsPrivileged()` 注册 `rum-event` 协议，**必须在 Electron `app.ready` 之前完成** `import`，否则 Electron 会抛 warning 且协议注册失败。

```typescript
// main/index.ts —— 文件最顶部
import armsRum from '@arms/rum-electron';
import { app } from 'electron';

armsRum.init({
  endpoint: '<your-endpoint>',  // 控制台获取
  env: 'prod',                  // 'prod' | 'gray' | 'pre' | 'daily' | 'local'
  version: '1.0.0',             // 应用版本号
});

```

初始化后的默认行为：

- `autoInject: true`：监听 `web-contents-created`，在每个 `BrowserWindow` 的 `dom-ready` 时机注入 Browser SDK 脚本
- 渲染进程**不需要**任何代码改动，也**不需要**修改 preload
- 主进程异常 / 崩溃 / 应用启动指标 / fetch 调用默认全部采集
- 事件经 `arms:rum-bridge` IPC 通道由主进程统一上报

---

## 验证接入

启动应用后进行一些操作（页面切换、发起请求等），通过 `beforeReport` 在主进程 stdout 查看待上报数据：

```typescript
armsRum.init({
  endpoint: '<your-endpoint>',
  beforeReport(bundle) {
    console.log('[RUM]', bundle);
    return bundle;     // 返回 undefined 也不会丢弃；如需丢弃请显式处理
  },
});
```

约 1–2 分钟后，可在 ARMS 控制台 **用户体验监控** > **应用列表** 中确认数据上报情况：

- **实时概览**：PV、UV、JS 错误数、API 请求数等核心指标
- **会话详情**：用户会话轨迹、页面浏览路径
- **异常分析**：JS 错误堆栈、原生崩溃分析、错误分布
- **性能分析**：API/tRPC 耗时分布、慢接口 TOP 榜

---

## 高级用法

### 手动注入模式

如果不希望 SDK 自动注入 Browser SDK，可设置 `autoInject: false`，然后在渲染进程手动初始化：

```typescript
// 主进程
armsRum.init({
  endpoint: '<your-endpoint>',
  autoInject: false,
});

// 渲染进程
import armsRum from '@arms/rum-electron/browser';
armsRum.init({ endpoint: '<your-endpoint>' });
```

> **注意** `autoInject: true` 模式下**禁止**在渲染进程手动 `import '@arms/rum-electron/browser'`，会导致重复初始化与事件双采。

### 自定义 partition 支持

`BrowserWindow` 使用了自定义 `partition`（如 `'persist:main'`）时，必须显式声明：

**方式一：`init()` 时声明（单 partition 推荐）**

```typescript
import armsRum from '@arms/rum-electron';
import { BrowserWindow } from 'electron';

await armsRum.init({
  endpoint: '<your-endpoint>',
  partition: 'persist:main',
});

new BrowserWindow({
  webPreferences: { partition: 'persist:main' },
});
```

**方式二：`init()` 之后动态注册（多 partition 场景）**

```typescript
await armsRum.init({ endpoint: '<your-endpoint>' });
await armsRum.registerSession('persist:main');
await armsRum.registerSession('persist:other');
```

> **调用时机** 必须在创建对应 `BrowserWindow` **之前**调用，否则首次页面加载渲染进程内 `window.ArmsEventBridge` 将为 `undefined`，事件无法回流。

### 启用 SPA 路由追踪

对于使用 SPA 路由的渲染进程，启用 `spaMode` 后自动采集路由切换事件：

```typescript
armsRum.init({
  endpoint: '<your-endpoint>',
  spaMode: true,  // 'auto' | 'hash' | 'history' | true | false
});
```

| 取值 | 行为 |
|------|------|
| `false`（默认） | 禁用 SPA 路由追踪，仅追踪完整页面加载 |
| `true` / `'auto'` | 自动检测，优先 hash 后 pathname |
| `'hash'` | Hash 路由（如 React HashRouter） |
| `'history'` | History API 路由（如 React BrowserRouter） |

### 启用分布式链路追踪

通过 `tracing` 启用链路追踪，将主进程 fetch / tRPC 调用与后端服务关联：

```typescript
armsRum.init({
  endpoint: '<your-endpoint>',
  tracing: {
    enable: true,
    sample: 10,                       // 10% 采样（0–100）
    propagatorTypes: ['tracecontext'],
  },
});
```

完整配置项（`allowedUrls`、多协议支持等）见 [`Electron SDK配置参考.md`](./Electron%20SDK配置参考.md#tracing-配置)。

### 启用主进程 tRPC 监控

若主进程使用 tRPC 定义 server router（典型如 `electron-trpc`），通过 `armsRum.instrumentTRPC()` 一行接入：

```typescript
import { initTRPC } from '@trpc/server';
import armsRum from '@arms/rum-electron';

// 包装 t 后，t.procedure 自动带监控 middleware
const t = armsRum.instrumentTRPC(initTRPC.create());

export const appRouter = t.router({
  greeting: t.procedure.input(...).query(...),         // 自动采集
  createUser: t.procedure.input(...).mutation(...),
});
```

> 主进程作为 tRPC client 调用云端 HTTP 服务时无需额外接入，底层 fetch 会被自动采集。更多配置（`filters`、`evaluateApi`）见 [`Electron SDK配置参考.md`](./Electron%20SDK配置参考.md#collectors-配置主进程采集器)。

### 启用 ANR（应用未响应）监控

ANR 监控检测主进程与渲染进程的事件循环长时间阻塞。默认关闭，需显式开启：

```typescript
// main.ts 顶部（import SDK 之前）
import { app } from 'electron';
app.commandLine.appendSwitch('enable-features', 'DocumentPolicyIncludeJSCallStacksInCrashReports');

import armsRum from '@arms/rum-electron';

await armsRum.init({
  endpoint: '<your-endpoint>',
  collectors: { anr: true },
});
```

> ℹ️ Feature Flag 用于渲染进程调用栈采集（需 Electron >= 34），未设置时仍会上报 ANR 事件但 `stack` 为空。完整配置项见 [`Electron SDK配置参考.md`](./Electron%20SDK配置参考.md#anr-采集器ianrcollectorconfig)。

### 自定义上报

主进程直接调用 SDK 实例方法；渲染进程通过 preload 自动暴露的 `window.ArmsRum` 门面调用，无需任何额外接入：

```typescript
// 主进程
armsRum.sendCustom({ type: 'lifecycle', name: 'app_ready', value: 1 });

// 渲染进程（不依赖 autoInject，页面早期即可调用）
window.ArmsRum?.sendCustom({ type: 'biz', name: 'checkout_click', value: 1 });
window.ArmsRum?.sendException(new Error('biz error'));
```

两端均提供 `sendCustom` / `sendView` / `sendException` / `sendResource` 四个方法，字段校验规则完全一致。字段说明与注意事项（如 `sendView` 的 URL 自动采集约束）见 [`Electron SDK配置参考.md`](./Electron%20SDK配置参考.md#渲染进程自定义上报windowarmsrum)。

### 关闭/精细化采集器

```typescript
armsRum.init({
  endpoint: '<your-endpoint>',
  collectors: {
    crash: false,                  // 关闭崩溃采集
    consoleError: false,           // 关闭 console.error 拦截
    api: {
      enable: true,
      filters: [/\.internal\.example\.com/],   // 命中 URL 不上报
    },
  },
  browserCollectors: {
    longTask: false,               // 关闭渲染进程长任务采集
    whiteScreen: false,
  },
});
```

完整字段表见 [`Electron SDK配置参考.md`](./Electron%20SDK配置参考.md)。
