Scout 反应
0-配置 OpenTeleometry RUM(Real User Monitor)用于回放(浏览器)和回放原生. 安装软件包, 调用 Scout.initialize() - 就这样.
自动抓取点击,导航,出错,生命周期,网络,性能,网络活性,以及设备上下文. 出口超过OTLP/HTTP.
import Scout from '@base-14/scout-react';
await Scout.initialize({
serviceName: 'my-app',
endpoint: 'https://otlp.example.com:4318',
});
这就是你写的代码.
安装
npm install @base-14/scout-react
端口道具由主机应用按需安装(网络条目不需要任何端口道具):
| 使用大小写 | 端点 |
|---|---|
| Web (浏览器) | react (仅在使用时使用) /react 帮助者) |
| 原生反应 | react-native, (中文(简体) ), @react-native-async-storage/async-storage |
| RN + 导航跟踪 | @react-navigation/native |
| RN + 连接跟踪 | @react-native-community/netinfo |
| RN + 设备模型/制造器 | react-native-device-info |
| RN + 电池级 | expo-battery (首选)或 react-native-device-info |
裸体反应原生( 无博览会)
原生地层ScoutCrash:ANR / UI-Hang 探测,本土崩溃捕捉,准确的冷起时间) 上建于 展览模块 API这是世博会唯一使用的部分——没有管理下的工作流程,没有"世博会CLI",也没有"世博会去". 赤子应用需要安装过一次的博览会的模块基础设施:
npx install-expo-modules@latest
npx pod-install
这增加了 expo 软件包和电线自动连接 MainApplication 编号 : AppDelegate。安装 expo-modules-core 单靠自己是不够的——模块不会被链接.
原始捕捉在赤裸和博览会的应用中行为相同. 如果模块没有链接,SDK不会失败:它会倒回JS只抓取,这意味着没有ANR,挂起,或者本土-崩溃的跨度,从JS捆绑负载中测出冷起而不是过程起步. 为确认其链接,请检查在两个平台上创建的版本:
import { requireOptionalNativeModule } from 'expo-modules-core';
const mod = requireOptionalNativeModule('ScoutCrash');
console.log('ScoutCrash linked:', !!mod && (await mod.isInstalled()));
快速启动 — react (浏览器)
import Scout from '@base-14/scout-react';
await Scout.initialize({
serviceName: 'my-app',
endpoint: 'https://otlp.example.com:4318',
});
就是这样。 点击,路由更改,出错,取取出+XHR,生命周期,网络生命体征,长任务,内存,相撞都自动被捕获. 没有按键仪表.
可选反应助手 /react 子路径 :
import { ScoutProvider, useScout, ScoutErrorBoundary, ScoutAction } from '@base-14/scout-react/react';
<ScoutProvider config={{ serviceName: 'my-app', endpoint: '...' }}>
<ScoutErrorBoundary fallback={(e) => <ErrorPage error={e} />}>
<App />
</ScoutErrorBoundary>
</ScoutProvider>;
// Override the tap label for a widget the SDK can't auto-name:
<ScoutAction description="Add to cart">
<MyFancyButton />
</ScoutAction>
快速启动 — react 原生
您的条目文件中有两个行 。 在你的构件树上什么都没有.
// index.ts
import Scout from '@base-14/scout-react/native';
import App from './App';
Scout.registerRootComponent(App);
registerRootComponent 通过 RN 安装根级错误 + 触摸边界 AppRegistry.setWrapperComponentProvider每一个水龙头变成了 user_interaction 间距,每个渲染错误都变成一个 error 间距( 与 error.component_stack自动.
在平原节点下运行本地入口 (Jest without a React Industrial preset, SSR,脚本)——使用命名导出,
import { Scout } from '@base-14/scout-react/native'。 。 。 .dist/native是普通的JS,所以Metro可以看到文字require()s; 节点的无害环境管理接口不尊重其__esModule标记,所以默认的导入会解决模块命名空间而不是类。 Metro和Babel正确处理默认的导入,因此应用程序代码不受影响.
在你身边 App.tsx数字 :
import Scout from '@base-14/scout-react/native';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
export default function App() {
const navRef = useNavigationContainerRef();
useEffect(() => {
Scout.initialize({
serviceName: 'my-app',
endpoint: 'https://otlp.example.com:4318',
});
}, []);
return (
<NavigationContainer
ref={navRef}
onReady={() => Scout.attachNavigationContainer(navRef)}>
{/* ... */}
</NavigationContainer>
);
}
在Android USB设备上,OTLP端点运行在您的dev机器上——指向它 http://localhost:<port> 运行 adb reverse tcp:<port> tcp:<port> 电话通过USB传送 iOS 模拟器共享主机网络 localhost {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}直接工作.
捕捉到的东西
跨平台(网络+RN)
| 信号 | 宽度/ 公尺 | 页:1 |
|---|---|---|
| 点击/ 窃听 | user_interaction | user_interaction.target, (中文(简体) ), target.type |
| 数值更改、提交、文本编辑 | user_interaction | 只有网络. user_interaction.type 实值 click | change | submit | input; 见 interactionEvents |
| 导航 | screen_view, (中文(简体) ), view_session | 屏幕 视图成为根跨度——屏幕上的所有跨度共享其痕量id |
| 屏幕加载时间 | screen_load | screen.load_time 以秒计 |
| 应用程序启动 | app_startup | 冷 + 温暖 |
| 生命周期 | app_paused, (中文(简体) ), app_resumed | Web:能见度变化;RN: App State |
| 错误( 未发生) | error 间距 + error.count 计量 | error.handled=false |
| 出错( 手工 / 边界) | error 间距 | error.handled=true, (中文(简体) ), error.component_stack 如果从反应边界 |
| 未处理的拒绝 | error 间距 | Web: 窗口事件; RN: Hermes Internal |
| 长期任务 | long_task | 默认阈值 100ms |
| 冷冻框 | frozen_frame | 长任务 ? 700ms; 背景 / 暂停时间, 上限为 frozenFrameMaxMs |
| ANR 国家 | anr | 网:工人监督. RN:计时器-驱动监视器. |
| HTTP( 牵引 + XHR) | http.request | 方法、URL、状态、期限、内容长度 |
| Unclean 退出( OOM / 强制杀害 / 标签丢弃) | app_unclean_exit 在下一次发射时 | 持久性会话标记 (localStorage on web, AsyncStorage on RN). 不是坠机.. pagehide 也未能向普通的关闭点开火,所以它从未触及到无坠机速率. 默认关闭嵌入式 Web 视图( E)enableUncleanExitDetection(中文(简体) ) |
| 原生崩溃( RN) | native_crash 在下一次发射时 | iOS: (英语) KSCrash 2.5+ (韩语) (每个例外,POSIX信号,C++,NSException,主线僵局)+ 度量衡 (单位:千美元)MXCrashDiagnostic, (中文(简体) ), MXHangDiagnostic) 在iOS 14+上. 安卓:未被发现的爪哇/科特林 (Thread.setDefaultUncaughtExceptionHandler页:1 NDK 信号处理器 本地崩溃 + 应用程序ExitInfo (API 30+),包括ANR在内的OS记录过程死亡(低度死亡作为排放) app_exit而不是像坠机一样 报告持续到磁盘,并在下一次发射时连同完整的收存/堆放/二进制图像堆放、先前的面包屑和 crash.type 编号 : crash.reason 编号 : crash.stack_trace |
| 日志 | OTLP 日志 | Scout.logDebug/Info/Warning/Error 和(可选入) console.* 捕获 |
仅网络
| 信号 | 页:1 |
|---|---|
| 网络要素 | LCP、INP、CLS、TTFB、FCP通过 web-vitals (有子部分——INP) input_delay_ms编号 :processing_duration_ms编号 :presentation_delay_ms, LCP 数据 load_delay_ms编号 :load_time_ms编号 :render_delay_ms, CLS 排版- 相变矩形) |
| 内存 | web.memory.usage 测量(仅铬) |
| 框架指标 | web.frame.build_time 通过长动画框架 API 的直方图 |
| 电池 | device.battery.level, (中文(简体) ), device.battery.state 通过 navigator.getBattery (铬) |
| 控制台抓取 | 选择通过 captureConsole: true |
只有原生反应
| 信号 | 页:1 |
|---|---|
| 电池 | device.battery.level, (中文(简体) ), device.battery.state 通过 expo-battery 或者说 react-native-device-info |
| 设备 | 模型、制造商、品牌、物理、屏幕尺寸 react-native-device-info + 键 RN.Dimensions |
| 网络连接类型 | 通过 @react-native-community/netinfo |
| 操作系统名称/ 版本 | 通过 Platform.OS, (中文(简体) ), Platform.Version |
| 内存 | react_native.memory.usage 测量-民意测验 react-native-device-info.getUsedMemory() 每10个 |
| 框架指标 | react_native.frame.build_time 直方图+ react_native.frame.dropped 通过取样的测量仪 requestAnimationFrame |
| 长任务/ 被冻结的框架 | long_task 财务报告和审定财务报表 frozen_frame 由同一 RAF 环得到的相距( ) longTaskThresholdMs页:1 |
| 控制台抓取 | 选择通过 captureConsole: true |
资源属性( 在所有信号中共享)
附随 ResourceSpans 编号 : ResourceMetrics 编号 : ResourceLogs 批次 :
service.name, (中文(简体) ),service.version, (中文(简体) ),environmentos.name, (中文(简体) ),os.versiondevice.model.name, (中文(简体) ),device.manufacturer, (中文(简体) ),device.brand, (中文(简体) ),device.is_physicalscreen.width, (中文(简体) ),screen.height, (中文(简体) ),screen.pixel_ratiodevice.battery.level, (中文(简体) ),device.battery.state- 任何你通过
config.resourceAttributes(例如:.deployment.region, (中文(简体) ),team(中文(简体) )
仪器范围
这个SDK所排放的每个跨度、度量和日志都包含一个 OpenTelemetics 仪器范围 :
name: base14.scout.react
version: <package.json version>
由 CI 守护测试执行——后端可以过滤上 scope.name
以识别所有来自童子军的遥测,而不论该信号或
是自动仪器制造的.
分布式追踪
列出的主机 firstPartyHosts 接收 W3C traceparent 离任时的页眉 fetch 编号 : XHR。页眉使用SDK的微量+跨度ID http.request 因此,在浏览器的要求下,您的后端的跨度是父的,整个流量以一个跟踪出现.
firstPartyHosts: [
'api.acme.com', // exact host
'*.acme.com', // matches all subdomains AND the apex
/^api\d+\.acme\.com$/, // regex tested against the URL host
],
第三方主机(Stripe,Google Fonts,分机等)仍被捕获为 http.request 横跨当地——他们只是没有得到标题.
身份和面包屑
Scout.setUser('user-123', { email: '[email protected]', plan: 'pro' });
Scout.clearUser();
// With user-based sampling in play, switching to a different user (or
// clearUser) starts a new session. See "User-based sampling" below.
Scout.addBreadcrumb('checkout', 'added item to cart');
Scout.addBreadcrumb('checkout', 'entered payment details');
// The last 20 breadcrumbs are JSON-serialized onto every `error` and
// `app_crash` span so you can see what the user did before the crash.
Scout.reportError(new Error('payment failed'));
Scout.logEvent('purchase_completed', { sku: 'SKU-1', amount: '49.99' });
过滤和PII洗涤 - beforeSend
导出前按每个跨度 / 度/ 日志运行 。 返回时 null 中,返回事件以发送修改后.
beforeSend: (event) => {
// Drop health checks
if (String(event['http.url'] ?? '').includes('/health')) return null;
// Scrub PII
delete event['enduser.email'];
return event;
},
抽样——默认情况,错误如何保持高音
童军默认为 1% 会话抽样 (单位:千美元)sessionSampleRate: 1) (中文(简体) ). 在每~100次用户会话中,只有一次保留了完整的仪表跟踪;而另外99次则一无所获. 这保持了遥测体积(和采集器成本)与您的应用尺度的界限.
错误从未在默认情况下被取出 . 何时 alwaysCaptureErrors: true (默认),这些信号绕过会话-实例门,总是被导出,无论会话是否在:
| 信号 | 平面/ 日志 | 为什么它绕道 |
|---|---|---|
错误报告通过 Scout.reportError(...) | error 间距 | 你总是想要看到失败. |
| 未发现JS例外,未处理拒绝,反应错误界限捕获 | error 间距 | 同样——倒地是不可接受的. |
Scout.logError(...) / 错误- 严重性日志 | 日志记录 | 高强度日志被当做错误处理. |
本地崩溃标记( RN ) app_crash(中文(简体) ) | app_crash 在下一次发射时 | 失去前一会话的崩溃必须到达您。 (app_unclean_exit 不是崩溃类:它只在它描述的会话被采样时报告。) |
| 原生相撞(iOS Mach 例外 / Android NDK 信号 / JVM 未发现) | native_crash 在下一次发射时 | 同样的——致命级信号. |
| ANR( 应用不响应) | anr 间距 | 致命级UX故障. |
如果你准备好了 alwaysCaptureErrors: false,错误得到同样的1%的治疗 和其他所有。 大部分球队应将其留作默认.
其它的都一样.. user_interaction, (中文(简体) ), screen_view, (中文(简体) ), http.request, (中文(简体) ), long_task, (中文(简体) ), frozen_frame、网络动态、业绩计量——尊重 sessionSampleRate. 开始吧 100 为德夫,留作 1 用于生产高流量应用软件,或将其设定为介于(例如)之间. 10) 对于攻击性较弱的样本.
Scout.initialize({
serviceName: 'my-app',
endpoint: 'https://otel.example.com:4318',
sessionSampleRate: 1, // default — 1% of sessions
alwaysCaptureErrors: true, // default — errors always export
// For local dev / staging:
// sessionSampleRate: 100, // capture every session
});
进行取样决定 每届会议一次,然后冻结 在那届的一生。 一个会话要么在(每个信号流)中被完全采样,要么被取出(只有错误类信号流). 这保持了个别会话的一致性——你永远得不到半个跟踪.
基于用户的样本——让你的应用程序决定每个用户
逐会抽样是指一个用户最终会保留一些会话,而其他会话则会下降. 决定 每个用户 相反,例如,通过群集、计划或地理,从您自己的远程配置或特性旗服务中传递一个值。 童子军没有网络呼吁 目标完全属于你.
// At startup: the last-fetched remote-config value, read synchronously from its cache.
Scout.initialize({ serviceName: 'my-app', endpoint, userSampling: remoteConfig.cached('scout_user_sampling') });
// When the value arrives or changes (fetch completes, user logs in):
Scout.setUserSampling(true); // keep this user's sessions
Scout.setUserSampling(false); // drop this user's spans; logs and metrics still flow
Scout.setUserSampling(null); // no opinion: fall back to sessionSampleRate
| 会话开始值 | 西班牙语 | 日志和衡量标准 |
|---|---|---|
true | 全部发送 | 发送 |
false | 没有,只有错误等级的间距 alwaysCaptureErrors 开始 | 仍然发送 |
null 从未设置 | sessionSampleRate 卷起,如上 | 见上文 |
- 应用在下一次会话开始,从不在会中. 新会话在闲置超时后开始,在最大持续时间后开始,在会话到期后重新加载,或者在用户开关上开始. 在会话中重新装入保留其决定.
- 烦恼. 最新的值被存储,所以一个没有经过的负载仍然能尊重它. 无核
userSampling在 init 中胜过存储值。 A 类setUserSampling以前打过电话initialize()将举行决定并决定第一届会议. - 用户开关. 用户取样时,
clearUser()(同时重排值),和setUser()使用不同于上一个的 ID 开始一个新的会话。 在账户开关,呼叫setUserSampling(newValue)在此之前setUser(newId)。从匿名到第一个登录会保留当前会话. - 在童子军中验证. 每个跨度、日志和度量载荷
session.sampling_source=:user|fallback。 。 。 . - 不是开关. SDK持续运行一个"不"用户;只有他们的跨度停止.
完整配置
await Scout.initialize({
// Required
serviceName: 'my-app',
endpoint: 'https://otlp.example.com:4318',
// Identity / pipeline
serviceVersion: '1.0.0',
environment: 'production',
secure: true, // https when scheme omitted
headers: { Authorization: 'Bearer ...' }, // sent on every OTLP export
// Auto-instrumentation toggles (all default true)
enableAutoTapTracking: true,
enableErrorTracking: true,
enableLifecycleTracking: true,
enableStartupTracking: true,
enableConnectivityTracking: true,
enablePerformanceMetrics: true,
enableLongTaskDetection: true,
enableAnrDetection: true,
enableFrameMetrics: true, // web + RN
enableMemoryMetrics: true, // web + RN
enableWebVitals: true, // web
enableBatteryTracking: true,
enableNetworkTracking: true,
enableLogging: true,
captureConsole: false,
capturePrintStatements: false, // alias for captureConsole
// Thresholds
longTaskThresholdMs: 100,
anrThresholdMs: 5000,
// Sessions
sessionSampleRate: 1, // 0..100, default 1 (1% of sessions)
alwaysCaptureErrors: true, // errors bypass sessionSampleRate (default true)
sessionTimeoutMinutes: 30,
// Network
firstPartyHosts: ['api.example.com', '*.example.com'],
ignoreUrlPatterns: [/\/health$/],
// Storage
maxOfflineStorageMb: 5,
// Filtering
beforeSend: (event) => event,
// Custom resource attributes
resourceAttributes: {
'deployment.region': 'us-east-1',
team: 'mobile',
},
// Export pacing (defaults match production needs)
metricExportIntervalMs: 30000,
logExportScheduledDelayMs: 5000,
// Retry on retryable failures (network errors, 408, 429, 5xx)
exportRetry: {
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 30000,
},
// On-disk offline buffer — replayed on next init / foreground / online
offlineBuffer: {
enabled: true,
maxItems: { traces: 5000, metrics: 2000, logs: 5000 },
},
// Debug
debug: false,
});
背景冲洗
在RN,SDK电话 Scout.flush() 何时 AppState 从 active
改为 background 编号 : inactive在网络上,它冲出 visibilitychange = hidden 财务报告和审定财务报表 pagehide这排出BatchSpanProcessor,读取器,
和在操作系统暂停/杀死进程之前登录处理器——没有它,
在最后几秒钟内发生的事件(导致坠毁的事件)
{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}会随着记忆的排队而死.
如果您的内幕输出方仍未及时交货( 例如操作系统杀死)
我们中间的 离线缓冲器 将批次固定为磁盘
下次重播时 Scout.initialize()。见 配置 ~ 离线
缓冲。 。 。 .
嵌入本地应用程序中? — WebView桥
当此页面运行在同样报告 RUM (一个 Flustter app) 的本地应用程序内时
使用 scout_flutter说,两个
SDKs 否则会为一个用户流打开两个无关的会话. 东道主
修正通过发送页面的身份:
Scout.setWebViewBridge({
sessionId: 'native-session-id',
anonymousId: 'native-anonymous-id',
});
安全呼叫之前 initialize() ——电话一经停放,即予使用
SDK升起,这是让一个主机在页面开始加载时注入它的方法.
三种模式,选择您通过的字段 :
| 模式 | 字段 | 行为 |
|---|---|---|
| 届会通过 (建议) | sessionId, (中文(简体) ), anonymousId | Page 继续导出给收藏家, 标记为主机会话 。 每一个信号的拷贝, 完全忠诚. |
| 中继 | + send, (中文(简体) ), relay: true | 页面停止 POSTING 跨度; 由主机发送 。 当WebView无法联系到收藏家时使用. 仅限垃圾邮件 - 日志和度量衡仍然通过HTTP. |
| 镜像 | + send | 页面导出 财务报告和审定财务报表 将副本交给东道主。 两者都到达后端,所以预计重复跨度. 只有在主机需要局部观察网页事件时才有用. |
Scout.isExportingSpans 报告该页面是否仍然拥有交付量——价值
在烟雾测试中声称,因为一个错误的接力是无声的.
优先通过会话,除非WebView真的无法到达 收取器; 它不需要主机侧接发码, 也没有任何损失 .
浏览器支持
Web 构建 ES2020 语法并测试到 Chrome / Android 系统
网络视频 87 (Android 8.1号)和 萨法里 14. SDK 调用无原型方法
比那层楼还新.. make check-compat 失败的构建如果 dist/ 是否为 --
和运行时间检查的门依赖特性: web-vitalsCLS 和 INP 数据
跟踪需求 Array.prototype.at (Chrome 92),被跳过,而
LCP,FCP和TTFB仍然报告. 没有运送多填充物 。 错误从
在 SDK 捆绑中 error.origin: sdk 编号 :
error.category: sdk_internal时,每页一次明显失败,而不是
数到 error.count。 。 。 .
范围外(目前)
| 信号 | 为什么不呢 |
|---|---|
| 会话重放( DOM/ 视图- 树记录) | 重量级载荷+隐私影响. 地图上没有. |
| 剖析(连续CPU取样) | 没有可靠的跨平台采样器. 地图上没有. |
| 框架光栅时间(网) | 浏览器仅通过长动画框架曝光全帧持续时间. 我们发射 web.frame.build_time 覆盖整个框架. |
| CPU 使用量 (网络) | 没有浏览器 API. |
可运行样本
| 样本 | 堆栈 | 如何运行 |
|---|---|---|
examples/platform-design-web | Vite + 反应 | cd examples/platform-design-web && npm run dev |
examples/platform-design-mobile | 博览会+反应原生 | cd examples/platform-design-mobile && npx expo run:ios (或 run:android) (中文(简体) ). 移动示例捆绑 ScoutCrash 博览会单元- npx expo prebuild 第一次自动运行. |
两者均默认为本地OTel 收藏器 :34318。Repo中包含一个样本收集器配置 dev/ (gitignored)——将你的收藏家指向它或者使用自己的.
发展
make install # npm ci
make build # tsup for web entries, tsc for native (preserves literal require())
make typecheck
make lint
make fmt # prettier --write
make test # 90 unit + jsdom integration tests
make test-coverage # core/** ≥ 75% lines, ≥ 70% functions
make audit # npm audit prod + all
make ci # fmt-check → lint → typecheck → test → build
见 变动情况 为释放历史.
许可证
麻省理工学院