BugSpla
为繁忙的开发者构建了崩溃和错误报告 .
导言
BugSplate 的标签 @bugsplat/expo 软件包为跨越iOS、Android和Web的Expo应用程序提供崩溃和错误报告。 BugSplact为您提供了宝贵的洞察力,帮助您了解用户遇到的问题。 我们的"博览会"集成收集了本土崩溃报告,JavaScript出错,以及自定义元数据,以便您能够修复错误并提供更好的用户体验.
安装
npx expo install @bugsplat/expo expo-blob expo-file-system @bugsplat/react
expo-blob, (中文(简体) ), expo-file-system 财务报告和审定财务报表 @bugsplat/react 是同行的依赖性,所以每个拷贝都最终会出现在您的应用程序中. 支持SDK 55至58.
expo-blob 船舶本地代码必须匹配您的项目 Expo SDK, 所以安装您的 SDK 的放行.. 58.x 为SDK 58, 57.x 用于SDK 57等(例如: npm install expo-blob@~58.0.3) (中文(简体) ). 它尚未列入博览会捆绑的本地模块清单,所以 expo install 不会为您定下责任.
配置
将配置插件添加到您的 app.json 或者说 app.config.js数字 :
符号上传的证书可以通过环境变量设置(BUGSPLAT_CLIENT_ID, (中文(简体) ), BUGSPLAT_CLIENT_SECRET)或插件配置中.
{
"expo": {
"plugins": [
["@bugsplat/expo", {
"database": "<your-database>",
"enableSymbolUpload": true
}],
["expo-build-properties", {
"android": {
"minSdkVersion": 26
}
}]
]
}
}
替换
<your-database>使用您真正的 BugSplate 数据库名称 。 插件拒绝角括号占位符expo prebuild所以这个准确的片段不会意外地作为配置船出航.
这个 bugsplat-android SDK需要Android minSdk 26 (Android 8.0+). 如果你的项目的minSdk 已经 \ 26 expo-build-properties 不需要插件 .
插件设置了所需的本地权限(Android),可选配置两个平台的自动符号上传. 通过代码配置您的数据库 init()。 。 。 .
插件选项
| 选项 | 需求 | 说明 |
|---|---|---|
database | 对 | BugSplate 数据库名称 。 掷出 expo prebuild 如果缺少, 空的, 或设置到一个占位符, 如 "<your-database>"成为真相的来源——也为运行时间所用 init() 调用从应用程序代码读取. |
enableSymbolUpload | 没有 | 启用iOS(dSYMs + JS源地图)和Android(.so文件 + Hermes JS源地图)的自动符号上传. |
symbolUploadClientId | 何时 enableSymbolUpload: true | BugSplate API 客户端 ID( 或设置) BUGSPLAT_CLIENT_ID (原始内容存档于2019-09-21) (英语). env var |
symbolUploadClientSecret | 何时 enableSymbolUpload: true | BugSplate API 客户端秘密( 或设置) BUGSPLAT_CLIENT_SECRET (原始内容存档于2019-09-21) (英语). env var |
插件也验证 expo.name, (中文(简体) ), expo.version,以及 expo.android.package 在预建时 —— 这些流入到符号加载脚本中, 因此上传的身份匹配您崩溃时的应用程序报告 .
符号上传
生产崩溃报告需要调试符号才能产生可读的堆栈痕迹. 何时 enableSymbolUpload 设置中,配置插件在iOS和Android发行过程中自动上传符号.
用于手动上传或CI/CD工作流程,使用 @bugsplat/symbol-upload 直接 :
npm install --save-dev @bugsplat/symbol-upload
# Upload iOS dSYMs
npx @bugsplat/symbol-upload \
-b your-database -a YourApp -v 1.0.0 \
-i $BUGSPLAT_CLIENT_ID -s $BUGSPLAT_CLIENT_SECRET \
-d /path/to/build/Products/Release-iphoneos \
-f "**/*.dSYM"
# Upload Android .so files (converted to .sym)
npx @bugsplat/symbol-upload \
-b your-database -a YourApp -v 1.0.0 \
-i $BUGSPLAT_CLIENT_ID -s $BUGSPLAT_CLIENT_SECRET \
-d android/app/build/intermediates/merged_native_libs \
-f "**/*.so" -m
# Upload JavaScript source maps (after npx expo export --source-maps)
npx @bugsplat/symbol-upload \
-b your-database -a YourApp -v 1.0.0 \
-i $BUGSPLAT_CLIENT_ID -s $BUGSPLAT_CLIENT_SECRET \
-d dist \
-f "**/*.map"
运行 npx @bugsplat/symbol-upload --help 所有选项.
使用量
初始化
import { init } from '@bugsplat/expo';
await init('your-database', 'YourApp', '1.0.0', {
userName: '[email protected]',
userEmail: '[email protected]',
appKey: 'optional-key',
});
报告错误
import { post } from '@bugsplat/expo';
try {
riskyOperation();
} catch (error) {
const result = await post(error);
console.log(result.success ? 'Reported!' : result.error);
}
设置用户信息
import { setUser } from '@bugsplat/expo';
setUser('Jane Doe', '[email protected]');
设置自定义属性
import { setAttribute } from '@bugsplat/expo';
setAttribute('environment', 'production');
测试崩溃
import { crash } from '@bugsplat/expo';
// Triggers a native crash (iOS/Android) or throws an error (web)
crash();
触发原生吊( ANR)
import { hang } from '@bugsplat/expo';
// Freezes the UI thread in a native loop. On Android the system ANR detector
// fires after ~5s of unresponsive input and BugSplat captures a dump with
// symbolicated native frames. On iOS the bundled BugSplat-Apple hang tracker
// persists a fatal-hang report after the main thread stays unresponsive past
// its threshold (~2s by default); the report uploads on the next app launch.
hang();
错误边界
将组件树包入 <ErrorBoundary> 以捕获 React 生成错误并自动报告到 BugSplate 。 在iOS,Android,和Web上同样工作.
import { ErrorBoundary } from '@bugsplat/expo';
function App() {
return (
<ErrorBoundary fallback={<Text>Something went wrong</Text>}>
<MyComponent />
</ErrorBoundary>
);
}
倒置道具接受反应节点或渲染函数:
<ErrorBoundary
fallback={({ error, resetErrorBoundary }) => (
<View>
<Text>{error.message}</Text>
<Button title="Try again" onPress={resetErrorBoundary} />
</View>
)}
>
<MyComponent />
</ErrorBoundary>
发布前收集用户输入
默认, <ErrorBoundary> 当错误发生时, 便张贴到 BugSplat 。 如果你宁可给用户一个机会来描述他们首先正在做的事情——将它捆绑在一份报告中而不是两份报告中——设定 disablePost 在边界上,并手动从后退位置张贴:
<ErrorBoundary> 是类成分,所以钩子不能直接活在它内部 fallback 使道具——将回落到自己的功能组件中:
import { useRef, useState } from 'react';
import { ErrorBoundary, post, type FallbackProps } from '@bugsplat/expo';
import { Text, TextInput, Button, View } from 'react-native';
function ErrorFallback({ error, componentStack, resetErrorBoundary }: FallbackProps) {
const [description, setDescription] = useState('');
const posted = useRef(false);
const submit = async () => {
if (posted.current) return;
posted.current = true;
await post(error, {
description,
attributes: { route: 'tasks/123' },
attachments: componentStack
? [{
filename: 'componentStack.txt',
data: new Blob([componentStack], { type: 'text/plain' }),
}]
: undefined,
});
};
return (
<View>
<Text>Something went wrong: {error.message}</Text>
<TextInput value={description} onChangeText={setDescription} />
<Button title="Submit" onPress={submit} />
<Button title="Dismiss" onPress={() => { submit(); resetErrorBoundary(); }} />
</View>
);
}
<ErrorBoundary disablePost fallback={(props) => <ErrorFallback {...props} />}>
<App />
</ErrorBoundary>
关于这种模式的一些说明:
post()实值 没有 一相能发. 这个useRef警卫是消费者的责任——没有它,一个快的双塔普(或"投降后解散")会发射出两个报告.useRef同步更新,它会保护同一窗口的地盘;useState不会的.componentStack被包裹在一个Blob在本地,附着物也可以是一种Uint8Array或一个{ uri, type }文件引用( 如 a)expo-image-picker资产;@bugsplat/expo从磁盘中读取文件引用expo-file-system当报告发布时.attributes成为BugSplate仪表板中的可查询列——用于按路由,特征旗,构建通道等过滤崩溃.- 如果发布失败, 您需要重试, 请检查
success返回值的属性post()并重设posted.current相应. 食谱上并没有显示它能保持最小.
摇摆以发送反馈
常见模式:让用户摇动设备打开反馈对话框. @bugsplat/expo 不捆绑摇动探测器(以保持软件包的收缩),但接线需要~30行 expo-sensors。 。 。 .
npx expo install expo-sensors
import { useEffect, useRef, useState } from 'react';
import { Accelerometer } from 'expo-sensors';
import { postFeedback } from '@bugsplat/expo';
function useShake(onShake: () => void, enabled = true) {
const lastFire = useRef(0);
const cb = useRef(onShake);
cb.current = onShake;
useEffect(() => {
if (!enabled) return;
Accelerometer.setUpdateInterval(100);
const sub = Accelerometer.addListener(({ x, y, z }) => {
// values are in g-units; magnitude ≈ 1 at rest (gravity)
if (Math.abs(Math.sqrt(x * x + y * y + z * z) - 1) < 1.2) return;
const now = Date.now();
if (now - lastFire.current < 1500) return; // 1.5s cooldown
lastFire.current = now;
cb.current();
});
return () => sub.remove();
}, [enabled]);
}
function App() {
const [open, setOpen] = useState(false);
// Auto-disabled while the modal is open so the user's input shake
// can't bounce them right back into it.
useShake(() => setOpen(true), !open);
return /* ... feedback modal that calls postFeedback() on submit ... */;
}
测试:真正的iOS/Android设备在框外工作. Android模拟器可以通过扩展控制 →虚拟传感器来模拟摇晃. iOS 模拟器 Device → Shake 菜单 没有 火速计数据——它发送 UIEventSubtypeMotionShake一个不同的频道。 如果iOS 模拟测试对您很重要, 请互换 expo-sensors 页:1 react-native-shake,它会听两个频道(权衡:这是一个第三方本地模块,如果忘记运行,在启动时会崩溃应用程序 npx expo prebuild --clean 在安装后).
用户反馈
提交用户反馈到您的 BugSplate 数据库 。 在iOS,Android,和Web上工作.
紧急API——以后的任何地方的电话 init()数字 :
import { postFeedback } from '@bugsplat/expo';
const result = await postFeedback('Login button broken', {
description: 'Nothing happens when I tap sign in',
});
console.log(result.success ? `Feedback #${result.crashId} posted` : result.error);
反应钩子——用于驱动带有内置加载/误入状态的反馈表:
import { useFeedback } from '@bugsplat/expo';
function FeedbackForm() {
const { postFeedback, loading, error } = useFeedback();
return (
<Button
title={loading ? 'Sending…' : 'Send feedback'}
disabled={loading}
onPress={() =>
postFeedback('Login button broken', {
description: 'Nothing happens when I tap sign in',
})
}
/>
);
}
如何运作
| 平台 | 原生崩溃 | JS 错误报告 |
|---|---|---|
| iOS系统 | 百叶苹果 (PLCrash Reporter) (英语) | HTTP 密码为 /post/js/ |
| 机器人 | 百花板和机器人 (崩溃) | HTTP 密码为 /post/js/ |
| 网页 | 不详 | 错误平板js |
API 密码
init(database, application, version, options?)
初始化 BugSplate 崩溃报告 。 必须在其他功能之前调用 .
选项 :
appKey?: string- 可查询元数据密钥userName?: string- 报告的用户名userEmail?: string- 报告用户电子邮件autoSubmitCrashReport?: boolean- 自动提交崩溃(仅iOS,默认:真实)attributes?: Record<string, string>- 自定义密钥值属性attachments?: string[]- 要附加的文件路径( 仅本地)description?: string- 默认描述
post(error, options?)
手动报告出错 。 回返 { success: boolean, error?: string }。 。 。 .
postFeedback(title, options?)
提交用户反馈. title 简称(需要); options.description 持有更长的身体。 回返 { success: boolean, error?: string, crashId?: number }。 。 。 .
useFeedback()
反应钩子返回 { postFeedback, loading, response, error } 用于驱动反馈表.
ErrorBoundary
反应错误边界, 报告会自动向 BugSplate 提交错误 。 接受一个 fallback (反应节点或使函数接收) { error, componentStack, response, resetErrorBoundary }) (中文(简体) ).
setUser(name, email)
为以后的报告更新用户信息.
setAttribute(key, value)
设置自定义属性 。 注:不支持网上.
crash()
触发测试崩溃以验证整合 .
hang()
冻结本地循环中的 UI 线程 。 在Android上,这会产生一个真实的ANR,带有符号化的原生帧;在iOS上,BugSplate-Apple悬浮跟踪器在无响应阈值(~2s)后坚持了致命悬浮报告,并上传到下一次发射时. 需要开发建设——在博览会Go中不开放.
博览去
@bugsplat/expo 在博览会Go中工作,功能减少。 由于博览会Go没有本地模块,本地崩溃报告被禁用. JS 错误报告(init(), (中文(简体) ), post(), (中文(简体) ), postFeedback(), (中文(简体) ), setUser(), (中文(简体) ), setAttribute(), (中文(简体) ), ErrorBoundary)仍然通过HTTP倒计时操作. 警告已经登录 init() 让你知道本地崩溃报告不活跃.
为了测试全部本地崩溃报告,请使用 释放构建 (见 测试原生崩溃 页:1 开发建筑包括一个调试器,在BugSplat捕捉到之前会拦截相撞.
测试原生崩溃
要测试本地崩溃报告, 您必须运行一个 释放构建 ——调试器在调试中拦截相撞事件建立.
# iOS
npx expo run:ios --configuration Release
# Android
npx expo run:android --variant release
iOS系统: 崩溃报告被PLCrash Reporter和 上传到下一个应用程序发布 何时 init() 又被调用。 启动测试后,重新启动应用程序并呼叫 init() 上传待处理的报告.
机器人: 抓获崩溃报告 崩溃时立即上传 由 Crashpad 处理程序.
测试本地挂接( ANRs)
hang() 在一个本地环路中封锁主线程。 每个平台的处理略有不同:
- 机器人:系统ANR探测器在~5秒的无响应输入后起火,BugSplatz立即用符号化的本土帧来捕获垃圾.
- iOS系统:被捆绑的 BugSplat-Apple 悬浮跟踪器在主线被挡在阈值(~2s)后,坚持了致命的悬浮报告. 报告在下个应用程序发布会上传.
触发和核实:
- 打电话
hang()(例如从调试按钮). - 机器人:将屏幕敲了几下——系统"App Not response"对话框出现,垃圾被捕获.
- iOS系统: 一旦UI冻结(或等待OS监视器),应用程序切换器的强制-quit.
- 重启应用程序. 悬浮报告在下一次发射时上传到任何未决的坠机报告.
解决问题
Android: 没有上传到模拟器上的崩溃
Crashpad 处理程序要求本地库被取入磁盘. 这个 @bugsplat/expo 配置插件通过设置自动打开遗留的 JNI 容器 expo.useLegacyPackaging=true 内 android/gradle.properties (设置与 expo-build-properties' android.useLegacyPackaging,所以不要设置一个到 false) (中文(简体) ). 它没有设置 android:extractNativeLibs 在清单中,因为 Expo SDK 58 使用的Android Gradle插件失败了其中的构建。 如果你还没有看到坠机:
- 使用一个
google_apis模拟图像( 不)google_apis_playstore) (中文(简体) ). Play Store模拟器的图像有限制,阻止了Crashpad的处理程序执行. - 或者,在 物理和机器人设备 如果这不是一个问题.
iOS: 测试崩溃后没有崩溃报告
确保你 重新启动应用程序并呼叫 init() 再来一次 坠机后 PLCrash Reporter将崩溃保存到磁盘上,并在下一次发布时上传.
许可证
麻省理工学院
