@evertro/monitor-miniapp
v1.2.0
Published
Evertro Monitor 小程序平台采集插件
Readme
@evertro/monitor-miniapp
Evertro Monitor 微信/抖音小程序平台采集插件集
概述
本包提供小程序环境下的全部采集插件,利用小程序特有的 API(wx.onError、wx.onAppRoute、wx.request 等)实现错误捕获、性能采集、操作链路和白屏检测。
本包是插件集合,不包含引擎和传输层。需要通过 @evertro/monitor-taro 统一入口使用,或手动组装。
安装
pnpm add @evertro/monitor-miniapp插件清单
| 插件 | 类名 | 采集内容 |
|------|------|---------|
| 错误捕获 | MiniappErrorPlugin | JS 运行时错误、未捕获 Promise 拒绝 |
| 操作链路 | MiniappBreadcrumbPlugin | 路由变化、前后台切换 |
| 性能采集 | MiniappPerformancePlugin | 页面加载耗时、内存告警、慢接口 |
| 网络监控 | MiniappNetworkPlugin | wx.request/uploadFile/downloadFile 拦截 |
| 白屏检测 | MiniappWhiteScreenPlugin | SelectorQuery 节点采样检测 |
插件详解
errorPlugin.ts — 错误捕获插件
采集方式:
| 错误类型 | 监听方式 | EventId |
|---------|---------|---------|
| JS 运行时错误 | wx.onError(callback) | E100000015 |
| 未捕获 Promise 拒绝 | wx.onUnhandledRejection(callback) | E100000023 |
核心逻辑:
wx.onError(errMsg)
├── 解析错误消息和堆栈
├── 附加当前 breadcrumbs 快照
└── report({ type: 'js_error', priority: BATCH, ... })
wx.onUnhandledRejection({ reason })
├── 提取 Promise 拒绝原因
├── 附加当前 breadcrumbs 快照
└── report({ type: 'promise_rejection', priority: BATCH, ... })事件数据结构:
{
id: 'uuid',
type: 'js_error',
message: 'TypeError: Cannot read property "x" of undefined',
stack: 'TypeError: ...\n at pages/index/index.js:12:5',
timestamp: 1713200000000,
pageUrl: '/pages/index/index',
pageStack: ['/pages/index/index', '/pages/home/home'],
breadcrumbs: [/* 最近 30 条操作记录 */],
context: { user: {...}, device: {...} },
priority: EventPriority.BATCH,
// 以下由引擎自动填充
eventId: 'E100000015',
eventName: '错误事件',
eventCode: 'ERROR-/pages/index/index',
}breadcrumbPlugin.ts — 操作链路插件
记录用户的操作路径,在错误发生时回溯上下文。
采集方式:
| 链路类型 | 监听方式 | 降级方案 |
|---------|---------|---------|
| 路由变化 | wx.onAppRoute() | PageInterceptor onShow hook |
| 进入前台 | wx.onAppShow() | — |
| 进入后台 | wx.onAppHide() | — |
设计决策: 不劫持 Taro 的 navigateTo / redirectTo 等路由方法。原因是 domus_miniapp 已经通过 NavigationInterceptor 全局覆写了 Taro 导航方法(路由守卫/登录校验),SDK 再次劫持会产生双重包装问题。
wx.onAppRoute 回调数据:
{
type: 'navigation',
category: 'navigateTo', // openType
message: 'navigateTo → /pages/detail/index',
data: { path: '/pages/detail/index', query: { id: '123' } },
level: 'info',
timestamp: 1713200000000,
}performancePlugin.ts — 性能采集插件
采集指标:
1. 页面加载耗时
onLoad 时刻 → onReady 时刻
│ │
└── WeakMap 记录开始时间 ──┘
duration = onReady.timestamp - onLoad.timestamp使用 WeakMap<PageInstance, startTime> 关联每个页面实例的开始时间,避免多页面并发加载时闭包变量互相覆盖。页面实例被 GC 后时间戳自动释放。
上报数据:
{
type: 'page_load',
pageUrl: '/pages/index/index',
duration: 850, // ms
priority: EventPriority.SCHEDULED,
}2. 内存告警
wx.onMemoryWarning((res) => {
// res.level: 5 / 10 / 15
// 5 = TRIM_MEMORY_RUNNING_MODERATE
// 10 = TRIM_MEMORY_RUNNING_LOW
// 15 = TRIM_MEMORY_RUNNING_CRITICAL
});3. 慢接口检测
通过 EventHub 监听 NetworkPlugin 广播的 network:complete 事件,筛选耗时 > 3s 的接口:
NetworkPlugin ──emit('network:complete', { url, duration })──→ EventHub
│
PerformancePlugin ←──on('network:complete', filter > 3000ms)──────┘networkPlugin.ts — 网络监控插件
通过 Proxy 劫持 wx.request / wx.uploadFile / wx.downloadFile,拦截所有网络请求。
防自我监控: SDK 自己发出的上报请求会被标记 _evertro_skip = true,NetworkPlugin 检测到此标记后跳过记录。
拦截流程:
wx.request(options)
│
├── if (options._evertro_skip) → 直接调用原始方法
│
├── 记录开始时间
│
├── 包装 success/fail 回调
│ ├── 计算耗时
│ ├── 添加 breadcrumb (type: 'request')
│ ├── 广播 EventHub('network:complete', { url, duration, statusCode })
│ └── if (statusCode >= 400) → report({ type: 'network_error' })
│
└── 调用原始 wx.requestbreadcrumb 数据:
{
type: 'request',
category: 'wx.request',
message: 'POST /api/user/info → 200 (350ms)',
data: { url, method, statusCode, duration },
level: statusCode >= 400 ? 'error' : 'info',
}whiteScreenPlugin.ts — 白屏检测插件
在页面 onReady 后延迟检测页面是否有有效渲染内容。
检测原理:
onReady 触发
└── setTimeout(whiteScreenTimeout ms)
└── wx.createSelectorQuery()
└── 采样 N 个预设选择器(或自定义 whiteScreenSelector)
└── 计算 empty 节点比例
└── if (empty / total > 阈值) → 判定白屏
└── report({ type: 'white_screen', priority: IMMEDIATE })预设选择器: .page, #app, view, text, image
上报数据:
{
type: 'white_screen',
pageUrl: '/pages/index/index',
duration: 5000, // 等待了多久
checkMethod: 'selector_query',
sampleResult: { total: 5, empty: 5 },
priority: EventPriority.IMMEDIATE, // P0 即时上报
}pageInterceptor.ts — 统一页面拦截器
解决多个插件竞争劫持 Page() 构造器的问题,提供统一的 hook 注册 API。
import { registerPageHook } from '@evertro/monitor-miniapp';
// 注册 onLoad hook(PerformancePlugin 使用)
registerPageHook('onLoad', function() {
pageLoadStartMap.set(this, Date.now());
});
// 注册 onReady hook(PerformancePlugin + WhiteScreenPlugin 使用)
registerPageHook('onReady', function() {
const start = pageLoadStartMap.get(this);
if (start) {
const duration = Date.now() - start;
// 上报 page_load 性能事件
}
});为什么需要统一拦截器?
如果每个插件各自 Proxy Page(),会造成:
- 多层嵌套包装,执行顺序不可控
- 某个插件的错误可能导致其他插件的 hook 不执行
- 难以维护和调试
PageInterceptor 只包装一次 Page(),内部维护 hook 列表,按注册顺序依次执行。
依赖关系
@evertro/monitor-types
@evertro/monitor-core → @evertro/monitor-miniapp导出一览
export { MiniappErrorPlugin } from './errorPlugin';
export { MiniappBreadcrumbPlugin } from './breadcrumbPlugin';
export { MiniappPerformancePlugin } from './performancePlugin';
export { MiniappNetworkPlugin } from './networkPlugin';
export { MiniappWhiteScreenPlugin } from './whiteScreenPlugin';
export { registerPageHook } from './pageInterceptor';