aegis-uni-sdk
v1.39.5
Published
腾讯云 Aegis(RUM) 前端监控的 uni-app 全端 SDK:微信小程序自动走官方 aegis-mp-sdk,H5 / App / 支付宝 / 抖音 / 快手 / 京东走协议逐字段对齐的自研实现,业务代码零条件编译。
Maintainers
Readme
aegis-uni-sdk
腾讯云 Aegis(RUM 前端性能监控)的 uni-app 全端 SDK。
腾讯官方只提供 aegis-mp-sdk(微信 / QQ 小程序)和 aegis-web-sdk(Web),uni-app 项目一旦要同时发 H5、App、支付宝 / 抖音 / 快手 / 京东小程序,就没有一个包能全端覆盖。
本包解决这件事:
- 微信小程序 → 自动使用官方
aegis-mp-sdk,白拿崩溃 / 卡顿 / 白屏 / 内存等私有能力 - 其余各端 → 自动使用自研实现,上报协议与官方
[email protected]逐字段对齐(同一份数据落到同一个看板,维度可横向比较) - 业务代码零条件编译,一行接入,不需要知道自己跑在哪个端
分流在编译期由 uni-app 的条件编译完成,所以各端产物只含自己那一份实现:微信端不含自研代码,其余端完全不引入 aegis-mp-sdk。
安装
pnpm add aegis-uni-sdk
# npm i aegis-uni-sdk / yarn add aegis-uni-sdkaegis-mp-sdk 是本包的依赖,无需单独安装。
快速开始
// App.vue
import { installAegis } from "aegis-uni-sdk";
onLaunch(() => {
installAegis({
id: "你的上报 id", // aegis 控制台分配
version: "3.4.8", // 应用版本,强烈建议传,否则看板「版本」维度会回落成 SDK 版本号
});
});挂好之后全项目可用:
uni.$aegis?.report({ msg: "下单失败", ext1: `orderNo=${orderNo}` });
uni.$aegis?.reportEvent({ name: "click_submit" });
uni.$aegis?.reportTime("home_first_screen", 1200);类型提示
在项目 tsconfig.json 里加一条,uni.$aegis 全项目就有类型了:
{
"compilerOptions": {
"types": ["aegis-uni-sdk/types"]
}
}登录后回填用户标识
const aegis = installAegis({ id: "xxx" });
// 拿到用户信息之后
aegis.setConfig({ uin: userId });
// 或者 uni.$aegis?.setConfig({ uin: userId });平台支持
| 平台 | 支持 | 实现 |
| ------------------------- | :--: | ------------------------------------------------------------------------------- |
| 微信小程序 | ✅ | 官方 aegis-mp-sdk |
| H5 / Web | ✅ | 自研 |
| App(Android / iOS) | ✅ | 自研 |
| App 鸿蒙(app-harmony) | ✅ | 自研 |
| 支付宝小程序 | ✅ | 自研 |
| 抖音小程序 | ✅ | 自研 |
| 快手小程序 | ✅ | 自研 |
| 京东小程序 | ✅ | 自研 |
| QQ 小程序 | ⚠️ | 走自研分支。官方 SDK 支持 QQ,但本包目前只对 MP-WEIXIN 分流,需要的话提 issue |
| 百度小程序 | ✅ | 自研(未实测,理论可用) |
★ 微信端与其余端的差异
两端共有的能力(业务可以放心跨端调用):
| 能力 | 说明 |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| 自定义上报 report | POST /collect |
| 普通日志 info / infoAll | 同上,level 不同 |
| 错误上报 error | 同上 |
| 自定义事件 reportEvent | POST /collect/events |
| 自定义测速 reportTime / reportT / time / timeEnd | POST /speed/custom |
| PV 转发 reportPv | GET /collect/{id} |
| 动态配置 setConfig | 登录后回填 uin 等 |
| 销毁 destroy | |
| bean 全维度 | id uin aid version env sessionId referer platform netType model vp sr from + ext1~3 |
| 聚合上报 / 日志去重 / 超长截断 / 采样 / 失败熔断 | 见「可靠性机制」 |
只有微信端有的能力(官方 SDK 私有,依赖微信专有 API,自研无法实现):
| 能力 | 官方配置项 | 为什么自研做不到 |
| ----------------------------------- | ------------------------------- | -------------------------------------------------------------- |
| 崩溃监控 | crashMonitor | 依赖 wx.onAppHide + 存储的崩溃指纹推断,非微信端没有等价语义 |
| 卡顿 / 无响应监控 | lagMonitor | 依赖微信 setData 耗时与渲染层信号 |
| 白屏检测 | blankScreen | 依赖 createSelectorQuery() 查询渲染层节点 |
| 内存告警 | memoryMonitor | 依赖 wx.onMemoryWarning |
| 分包加载测速 | reportLoadPackageSpeed | 依赖微信分包加载生命周期 |
| 接口自动测速 | reportApiSpeed | 需要劫持 wx.request,见下方「未实现」 |
| 自动 PV | spa | 需要劫持路由生命周期,见下方「未实现」 |
| trace header 注入 | api.injectTraceHeader | 需要劫持 wx.request |
| gzip 压缩上报 | gzip | 需要引入 pako,收益不足以换体积 |
| 远程采样白名单 | whiteListUrl / rateLimitUrl | 服务端下发,见下方「未实现」 |
| 页面性能 / webVitals / setData 上报 | pagePerformance 等 | 依赖微信性能 API |
⚠️ 本包默认把官方的
reportApiSpeed和spa关掉。原因:自研实现没有对应能力,开着会让微信端与其余端的数据无法横向比较。想开就显式打开(见下)。
给微信端传官方专属配置
通用配置对所有端生效;微信独有的选项走 mpWeixin 透传,其余端会忽略:
installAegis({
id: "xxx",
version: "3.4.8",
// 只有微信端会用到,其余端自动忽略
mpWeixin: {
crashMonitor: true,
lagMonitor: { threshold: 2000 },
blankScreen: { contentSelectors: [".page-container"] },
reportApiSpeed: true, // 覆盖本包的默认关闭
},
});★ 未实现的能力
自研实现(即非微信端)没有下列能力,微信端仍由官方 SDK 提供:
| 未实现 | 原因 |
| -------------------------------------------------- | --------------------------------------------------------------------------------------- |
| gzip 压缩上报 | 需引入 pako;官方也只在 payload > 1KB 时压缩,收益不足以换体积 |
| 远程采样白名单 /collect/whitelist、/rateConfig | 由服务端下发采样率与白名单,属于服务侧策略。本包只实现本地会话级采样(random) |
| 白屏检测 | 依赖 createSelectorQuery() 查询渲染层节点,其余端无等价能力 |
| 卡顿 / 无响应监控 | 依赖微信渲染层信号 |
| 崩溃监控 | 依赖微信生命周期指纹推断 |
| 内存告警 | 依赖 wx.onMemoryWarning |
| trace header 注入 | 需劫持 uni.request,会与业务自己的请求拦截器打架,交由使用方决定 |
| 离线日志 /offline | 需要本地日志库与回捞通道 |
| 页面性能 / webVitals / hippy 性能 | 依赖各端差异极大的性能 API |
| 自动接口测速 | 需劫持 uni.request,同上。已留好 reportTime(),想接就在自己的请求拦截器里调一行 |
| 自动 PV | 需劫持路由生命周期。已留好 reportPv(),想接就在页面 onShow 里调 |
后两项是有意留给使用方的:uni-app 项目通常已经有自己的请求拦截器与路由拦截器,由 SDK 再劫持一次容易冲突。
API
installAegis(options) / createAegis(options)
两者都返回上报实例;installAegis 会额外把实例挂到 uni.$aegis。
配置项
| 字段 | 类型 | 默认 | 说明 |
| ---------------- | --------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------- |
| id | string | — | 必填,aegis 控制台分配的上报 id |
| uin | string \| number | "" | 用户唯一标识,登录后用 setConfig 回填 |
| version | string \| number | SDK 版本号 | 应用版本。建议必传,不传会回落成 SDK 版本号(官方行为),看板的版本维度就没意义了 |
| env | AegisEnv | others | production / development / gray / pre / daily / local / test / others,非法值回落 others |
| hostUrl | string | https://rumt-zh.com | 上报域名 |
| delay | number | 1000 | 聚合发送的防抖时长(ms) |
| repeat | number | 60 | 同一条日志在 1 分钟窗口内的上报次数上限 |
| random | number | 1 | 会话级采样率 [0, 1],1 为全量 |
| maxLength | number | 10240 | 单条日志 msg 的最大长度,超出截断 |
| pageUrl | string | — | 固定 from 值,传了就不再从页面栈推导 |
| ext1 ~ ext10 | string | — | 扩展字段默认值,单条日志上的同名字段优先 |
| beforeReport | (log) => log \| boolean \| void | — | 上报前最后一次改写机会,返回 false 丢弃该条 |
| onReport | (logs, isErr) => void | — | 上报完成回调 |
| mpWeixin | Record<string, any> | — | 仅微信端生效,透传给官方 SDK 构造函数 |
实例方法
| 方法 | level | 说明 |
| ------------------------------------------------ | -------------- | ------------------------------------------- |
| report(params) | REPORT(2048) | 自定义上报。入参给了 level 就用入参的 |
| info(...msg) | INFO(2) | 普通日志,仅白名单用户上报 |
| infoAll(...msg) | INFO_ALL(-1) | 普通日志,全量用户上报 |
| error(...msg) | ERROR(4) | js 错误。强制覆盖入参 level |
| reportEvent(name \| params) | — | 自定义事件 |
| reportTime(name, duration) / reportT(params) | — | 自定义测速 |
| time(key) / timeEnd(key) | — | 配对计时并自动上报耗时 |
| reportPv(id) | — | 把 PV 转发到另一个 aegis id |
| setConfig(partial) | — | 动态改配置 |
| destroy() | — | 先冲掉缓冲再销毁 |
report / info / infoAll / error 都支持两种调用形态:
aegis.report("多个", "参数", "会用空格拼起来");
aegis.report({ msg: "对象形态", level: LogType.AJAX_ERROR, ext1: "x", trace: err.stack });只用自研实现(含微信端)
想让所有端行为完全一致(放弃微信端的私有监控能力),直接用子路径:
import { CustomAegis } from "aegis-uni-sdk/custom";
const aegis = new CustomAegis({ id: "xxx" });
// CustomAegis 独有:flush() 立即冲掉缓冲、sessionId 只读属性
aegis.flush();只要枚举
import { LogType } from "aegis-uni-sdk/log-type";上报协议
自研实现完全对齐官方 [email protected]。bean 以 querystring 挂在每个上报请求上。
| 用途 | 请求 | Body |
| ---------- | ---------------------- | -------------------------------------------------------------------------------------------- |
| 普通日志 | POST /collect | 列式 JSON:{level:[...], msg:[...], ext1:[...], count:N},Content-Type: application/json |
| 自定义事件 | POST /collect/events | {payload: "[{\"name\":\"...\"}]"} |
| 自定义测速 | POST /speed/custom | {payload: "{\"custom\":[{...}]}"} |
| PV 转发 | GET /collect/{id} | — |
列式载荷是这个协议最容易踩坑的地方:N 条日志压成「每个字段一个数组」,字段取所有日志的并集,某条缺该字段就补空串,最后带 count。
// [{level:"4",msg:"a"}, {level:"2",msg:"b",ext1:"x"}] 会被压成
{ "level": ["4", "2"], "msg": ["a", "b"], "ext1": ["", "x"], "count": 2 }各字段数组长度必须等于 count,否则服务端按下标对齐时会整体串位。
枚举(platform / netType 的未命中值都是 100 而不是 0):
platform:android=1 ios=2 windows=3 macos=4 linux=5 devtools=6 other=100netType:wifi=1 2g=2 3g=3 4g=4 5g=5 6g=6 unknown=100LogType:32 项,见aegis-uni-sdk/log-type
其它细节:sessionId = session-{启动时间戳}(进程级常量)· aid 存在本地 AEGIS_ID(与官方同 key,换用本包不会丢设备标识)· referer 取 uni.getLaunchOptionsSync().scene · vp = 视口 宽 * 高、sr = 屏幕分辨率 宽 * 高
可靠性机制
自研实现照搬了官方的全套流控,不是「一条日志一个请求」的裸实现:
| 机制 | 行为 |
| ---------- | --------------------------------------------------------------------------------------- |
| 聚合发送 | 攒够 5 条立即发,否则防抖 delay(1000ms) 后发。错误风暴不会变成请求风暴 |
| 按页面分组 | 聚合窗口内跨页的日志会拆成多个请求,各带自己的 from |
| 超长截断 | msg 超过 maxLength(10240) 截断 |
| 重复限流 | 同一条日志(level + msg 相同)1 分钟窗口内超过 repeat(60) 次丢弃 |
| 会话级采样 | random 在会话内只判定一次,命中即整会话上报,避免同一次崩溃的上下文日志被撕成半截 |
| 失败熔断 | 连续失败 60 次后停止上报,不再骚扰服务端 |
| 就绪队列 | 网络类型未取到时日志先入队(上限 200 条),就绪后一次性冲出,启动期的错误不会缺维度 |
| 退后台冲刷 | uni.onAppHide 时立即发出缓冲,最后一批日志不随进程消失 |
| 静默失败 | 上报请求带 hideErrorToast 标记,且组装/发送全程 try-catch——监控自己出问题绝不影响业务 |
与官方 SDK 的行为差异
刻意保留 / 刻意修正的几处,都是有原因的:
report的 level 是默认值,info/infoAll/error是强制覆盖官方
report的Object.assign末尾不覆盖level,而info/error覆盖——这是个不对称行为。本包照搬,因为业务往往靠report({ level: LogType.AJAX_ERROR })把接口错误投进「ajax 错误」桶。别顺手统一掉,有单测锁着。from在日志创建时捕获,不是发送时官方在发送时才计算
from,聚合窗口(1s)内若发生页面跳转,日志会被记到新页面上。本包在日志创建时就捕获所属页面,发送时按from分组——页面维度更准。用的是官方协议本就支持的log.from机制,不是协议偏离。bean 值统一编码一次
官方
getBean直接字符串拼接、不做encodeURIComponent,遇到model="iPhone 14 Pro"(含空格)或含&的值会污染后续参数。本包在 querystring 组装时统一编码一次。reportApiSpeed与spa默认关闭见上方「微信端与其余端的差异」。
原理与注意事项
端分流是编译期完成的
dist/index.js 长这样(注释就是分流指令):
// #ifndef MP-WEIXIN
import { createCustomReporter } from "./adapters/custom";
// #endif
// #ifdef MP-WEIXIN
import { createMpWeixinReporter } from "./adapters/mp-weixin";
// #endifuni-app 的条件编译会处理 node_modules 里的代码——它的 uni:pre 插件(@dcloudio/uni-cli-shared)只排除了 vue / vite / vuex / vue-router / vue-i18n 等少数几个包,node_modules 整体并不在排除列表里;H5 的依赖预构建阶段也挂了 uni:dep-scan(esbuild plugin)做同样的事。所以:
- 微信端产物:只有官方
aegis-mp-sdk,一行自研代码都没有 - 其余端产物:只有自研实现,完全不引入
aegis-mp-sdk(92KB 一点不占) - 零配置,不需要
optimizeDeps.exclude
⚠️ 产物绝对不能 minify
条件编译的载体是注释。一旦产物被压缩或 removeComments: true,指令会被剥掉,分流静默失效——包照样能装能跑,只是微信端把两份实现都打进包、其余端莫名引入 aegis-mp-sdk,且没有任何报错。
所以本仓库:
- 构建用
tsc逐文件输出 ESM(保留注释与目录结构),不用 bundler tsconfig.build.json里removeComments: false带着醒目注释pnpm build末尾跑scripts/verify-dist.js,用 uni-app 自己那套正则断言产物里的指令完整且配对,CI 也跑
如果你 fork 后想换构建工具,务必保住这条。
依赖 uni-app 运行时
本包直接使用 uni 全局对象(uni.request / uni.getSystemInfoSync / uni.getStorageSync 等)与 getCurrentPages(),只能在 uni-app 项目里用。
版本策略
版本号跟随官方 aegis-mp-sdk:1.39.5 表示对齐 [email protected] 的上报协议。这样一眼就知道协议基线是哪一版,也方便判断该不该升。
本包自身的修复会以第四位或预发布号体现,协议基线不变时主版本不动。
开发
pnpm install
pnpm test # 84 条单测
pnpm typecheck
pnpm lint
pnpm build # 含产物自检 verify:dist
pnpm check # 以上全跑发布
- 改
package.json的version - 提交
git tag v<version> && git push --tags
GitHub Actions(.github/workflows/release.yml)会校验 tag 与 package.json 版本一致,跑完全部检查后 npm publish --provenance。
需要在仓库 Secrets 里配 NPM_TOKEN。--provenance 要求仓库为 public,私有仓库请去掉该参数。
License
MIT © liujiayii
