更新记录

1.0.0(2026-09-07) 下载此版本

Sensors Wave 原生崩溃采集插件


平台兼容性

uni-app(4.0)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 - - 1.0.0 1.0.0 11.0 1.0.0 14 1.0.0 1.0.0
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(4.0)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
- - 11.0 1.0.0 14 1.0.0 1.0.0 -

sensorswave-crash 原生崩溃采集插件(UTS)

UniApp 原生崩溃(fatal)采集插件,配合 @sensorswave/uniapp-sdkenableCrashTrack: true)实现 「装一个 SDK 完成全部数据采集,包括原生崩溃」。

插件是纯崩溃捕获器:只负责崩溃瞬间在本地落盘规范化记录(sw_crash_records/<13位时间戳>_<id>.json), 无网络、无分析逻辑。下次启动时 JS SDK 经桥拉取(最旧优先 ≤10 条)、构造 $Exception$exception_level='fatal'time 回填崩溃时刻)入队上报, 入队成功后 ack 删除。数据面统一在 JS,与埋点共用同一管道 / 合规门禁(opt-out 时不拉取不删除)。

形态:纯 UTS 源码插件(uni_modules),零预编译产物、零三方依赖。三端(Android / iOS / HarmonyOS)共用一份源码目录,UTS 在云打包 / 自定义基座时才编译为 Kotlin / Swift / ArkTS。

背景:DCloud 自 2024-08-01 起插件市场不再接收新的 App 原生语言插件,本插件由旧 原生语言方案(Android AAR + iOS framework,git 历史可查)整体迁移而来。

目录结构

uni_modules/sensorswave-crash/
├── README.md                          # 本文件(随插件目录分发,插件市场详情页展示)
├── package.json                       # uni_modules 清单(三端 App 矩阵全 "y",vue3 only)
└── utssdk/
    ├── interface.uts                  # 桥契约声明(4 个同步 JSON 方法)
    ├── crash-record.uts               # 跨端共享纯逻辑(常量 + JSON 组装/转义/文件名/TTL)
    ├── app-android/index.uts          # Thread.setDefaultUncaughtExceptionHandler 链式
    ├── app-ios/index.uts              # NSSetUncaughtExceptionHandler(经混编层注册)
    │   ├── SwCrashHybrid.swift        #   UTS 原生混编:C 函数指针注册 + 旧 handler 链式
    │   ├── exception-frames.uts       #   callStackSymbols 文本帧解析(纯函数)
    │   └── exception-image.uts        #   主二进制 LC_UUID 读取(debug_id 富化)
    └── app-harmony/index.uts          # errorManager + hiAppEvent watcher 双通道

demo 侧 demo/uni_modules/sensorswave-crash/ 是本目录的拷贝(yarn sync:demo 维护)。

📌 HBuilderX 识别 UTS 插件要求插件位于打开的 uni-app 工程内(本仓库根目录无 manifest.json,不会被当成工程):本地开发 / 制作自定义基座 / 发布插件市场均在 demo 工程中操作,本目录是唯一的源码真源。

覆盖范围

平台 覆盖 不覆盖 捕获时机 kind 值
Android ✅ Java/Kotlin 未捕获异常(UEH 链式包装,写完委托原 handler;结构化帧直取 stackTrace ❌ NDK/C++ 崩溃、SIGSEGV 等信号级(未来可经 config.json 引 xCrash 补齐,JS 契约不动) HookProxy(Application.onCreate,HBuilderX 3.96+)+ startCapture(SDK init) java
iOS ✅ ObjC/Swift NSException(含 raise();结构化帧来自 callStackSymbols 解析 + debug_id 读盘 LC_UUID;handler 经原生混编 SwCrashHybrid.swift 注册——UTS 闭包桥接 C 函数指针为死桩,注册后落盘后链式调用前一 handler 不覆盖宿主) ❌ SIGSEGV/SIGABRT 等 BSD 信号级(Swift fatalError/force-unwrap 属此类;信号级捕获无法纯 UTS 移植) HookProxy(applicationDidFinishLaunchingWithOptions,3.97+)+ startCapture(SDK init) oc
HarmonyOS ✅ ArkTS 未捕获异常(errorManager 通道,崩溃瞬间当场落盘,富堆栈)+ C++ 崩溃 / APP_FREEZE 卡死(hiAppEvent watcher 通道,系统 faultlog 下个启动回放,无堆栈属预期) ❌ external_log 故障日志文件不消费不代删(系统 5MB 滚动上限自管) 模块导入时(双通道同时注册) arkts / cpp / freeze

采集注册早于 enableCrashTrack 门控:是否回放由 JS 侧合规状态决定,opt-out 期间 记录保留在设备上,授权恢复后自动补报。

业务接入

  1. 插件市场导入(或拷贝)sensorswave-crash 到工程 uni_modules/
  2. HBuilderX 制作自定义基座(UTS 插件在云打包/基座制作时才编译,标准基座不生效)。 本地调试同理:直接「运行到手机」跑的是标准基座,采集不生效——需 「运行 → 运行到手机或模拟器 → 制作自定义调试基座」后用该基座运行; 改插件源码后须重新制作基座(非热更新)
  3. SDK init 开启 enableCrashTrack: true(仅 app 平台生效;其余平台静默跳过)

接入方式(按平台):Android / HarmonyOS 下 SDK init 经 uni.requireUTSPlugin('@/uni_modules/sensorswave-crash') 自动解析插件并接线崩溃回放, 无需业务代码(自动通道可用性随 HBuilderX 版本浮动,未逐版本验证);iOS(vue 工程) 自动通道不工作——运行时按 @/uni_modules/<id> 路径解析不到本地 UTS 插件(实测报 is not found),必须走下方手动注入(根 README「原生崩溃采集」有完整说明)。 自动通道失败时(且未手动注入)init 打 warn 日志提示接入指引(采集能力静默降级, 不影响其他功能)。

手动注入(iOS 必做,各端推荐统一使用):在 #ifdef APP 下静态 import 模块后注入:

// #ifdef APP
import * as crashUts from '@/uni_modules/sensorswave-crash'
import { createUtsCrashProvider } from '@sensorswave/uniapp-sdk'
// ⚠️ pull/ack 两个导出须以对象字面量逐成员引用后传入:UTS 代理按「JS 是否引用」
//    生成并摇树(namespace 直传会被摇掉),漏引用 ackCrashRecords 会导致回放能拉到
//    记录但删除静默失效(重启重复上报)。注入时 SDK 会对缺失方法打 warn 兜底。
SensorsWave.registerCrashProvider(createUtsCrashProvider({
  getPendingCrashRecords: crashUts.getPendingCrashRecords,
  ackCrashRecords: crashUts.ackCrashRecords,
}))
// #endif

init 之前注入可完全接管(自动通道不再触发);init 之后注入会立即用注入的 provider 重放一次(已 ack 的记录拉取为空,不会产生重复事件)。

验证(demo 工程已接好)

  1. demo pages/error-track 底部「原生崩溃(fatal)」区:插件状态行 + 触发按钮
  2. 触发后约 0.5~1s 进程死亡 → 重新打开 → debug 日志出现 $Exceptionlevel=fataltime 为崩溃时刻),batchSend:false 下立即 POST /in/track
  3. 再次重启无重复事件(ack 生效)
  4. 隐私验证:optOutCapturing 下崩溃重启不上报且设备记录保留,恢复授权后补报
  5. iOS 必须脱离 Xcode 从桌面启动(挂调试器时 NSException handler 不触发)

桥契约(三端一致,4 个同步方法)

方法 签名 说明
getPendingCrashRecords (): string 同步拉取 payload(最旧优先 ≤10 条;拉取前做 TTL 清理,并逐条校验记录内容——非法/半成品记录直接删除,防毒丸阻塞回放通道)
ackCrashRecords (ids: string[]) 幂等删除已入队记录(harmony 端同时把记录时刻写入跨启动去重存储)
startCapture (debug: boolean): string 幂等注册 + TTL 清理;返回 {"started":true,"version":"...","kinds":[...]}debug=true 放开 testCrash
testCrash (kind: string): void 演示崩溃:java / oc / arkts;延迟 300~500ms 脱离 JS 调用栈执行(避免被 JS 桥/Vue errorHandler 吞成 error 级事件)。java 为后台线程真实抛出直达 UEH;oc / arkts 为模拟派发(直接调用注册的同一落盘链后终止进程——iOS 模拟器 Rosetta 下真实 raise 损坏、harmony 运行时会吞定时器回调异常,裸抛均不可达;oc-raise 为 iOS 真机真实派发通道)

payload:成功 {"code":"ok","total":N,"records":[CrashRecord...]}(最旧优先 ≤10 条); 失败 {"code":"error","message":"..."}。CrashRecord 字段契约见 src/core/native-crash-types.tsid/kind/exception_type/message/stack/timestamp/process_name/...)。

结构化 frames(仅非空才写):Android 直取 throwable.stackTraceplatform:"java"module=全限定类名,canonical 序:外层在前、崩溃点在末,>30 帧保首 5 入口 + 末 20 崩溃侧); iOS 由 callStackSymbols 文本解析(platform:"cocoa"instruction_addr PAC 已剥 16 位 hex, 未符号化帧带 image_addr,主二进制帧带 debug_id=LC_UUID 大写连字符 36 位——服务端 dSYM 符号化匹配原料)。JS 侧 sanitizeNativeCrashFrames 白名单清洗后优先使用;无 frames 的 记录回退 stack 文本解析(parseExceptionFrames)。

存储与可靠性

  • 目录:Android <filesDir>/sw_crash_records/;iOS <NSHomeDirectory>/Library/sw_crash_records/; harmony <filesDir>/sw_crash_records/
  • 文件名 <13位零填充毫秒时间戳>_<id>.json(字典序=时间序;文件名解析 id 供 ack)
  • 写入原子性:三端均为「写临时文件 + rename」(iOS atomically:true 等价)——崩溃线程 写盘中途进程死亡不留半成品 .json.tmp 残料不满足记录文件名规则,不会被误读)
  • 防毒丸:拉取时逐条校验内容(可解析为对象 + 非空 id),非法/半成品记录直接删除—— 半成品若原样嵌入 payload 会令 JS 侧整个 payload 解析失败,回放通道被永久阻塞且无法 ack
  • 边界:TTL 7 天(拉取与 startCapture 时清理)、容量上限 100 条(超出丢最旧)、 单次回放 ≤10 条(对齐 JS 队列预算)
  • 崩溃路径只允许同步 API + 手写 JSON 转义(crash-record.utsescapeJson), 不依赖序列化库;任何异常吞掉,采集链自身绝不能成为新的异常源
  • harmony 跨启动去重:filesDir/sw_crash_dedup.dat(每行一个毫秒时间戳,容量 20、24h 过期), watcher 事件以 ±5s 时间窗比对「已 ack 键 + 现存待处理记录」,防 errorManager/watcher 双计、防系统重放重生记录

已知限制与编译验证点

  1. 采集范围降级(用户已确认):无 Android NDK / iOS BSD 信号级捕获——纯 UTS 无法移植 xCrash / PLCrashReporter 的信号机制;README/docs 已注明,未来可经 config.json 引 xCrash 补齐
  2. iOS 挂调试器(Xcode attach)时崩溃不捕获——E2E 从桌面启动
  3. harmony:JS 启动前的崩溃不在 errorManager 通道覆盖(watcher 通道由系统 faultlog 兜底); watcher 送达晚于 SDK init pull 时,记录顺延一个启动周期回放(不丢失)
  4. UTS 平台类型边界标注了【需编译验证】(各实现文件头):Java 数组迭代(Kotlin .size)、 iOS String 文件读写(Swift 标签参数)、FileManager 返回值桥接等。编译不过均为单点替换, 不影响契约与 JS 侧
  5. 旧版 HBuilderX(Android <3.96 / iOS <3.97)HookProxy 不生效时,靠 startCapture (SDK init 调用)兜底安装,覆盖窗口收窄为「SDK init 后」;插件源码不依赖 模块导入即安装(顶层语句在 uts2swift 下非法,见实现文件头【跨端约束】)
  6. 插件版本:crash-record.utsPLUGIN_VERSION(与插件 package.json 保持一致); 记录 JSON 的 library_version 字段携带插件版本,便于服务端区分数据形态

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.INTERNET

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

本插件会采集终端用户行为数据并上报至开发者配置的服务端

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议