@arms/rum-reactnative 0.1.3-beta.0 → 0.1.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/RumSdkReactnative.podspec +19 -2
- package/android/build.gradle +68 -5
- package/android/src/main/jni/rum_crash_writer.c +13 -19
- package/android/src/main/jni/rum_module_info.c +39 -0
- package/android/src/main/jni/rum_module_info.h +4 -0
- package/lib/commonjs/NativeRumSdkReactnative.js +5 -1
- package/lib/commonjs/utils/crash.js +3 -0
- package/lib/commonjs/utils/platform.js +1 -1
- package/lib/module/NativeRumSdkReactnative.js +6 -2
- package/lib/module/utils/crash.js +3 -0
- package/lib/module/utils/platform.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -418,6 +418,32 @@ SDK 仅持久化一个键 `_arms_uid`(匿名用户 UID),用于跨冷启动
|
|
|
418
418
|
2. **隔离命名空间,不污染宿主 App**:iOS 走独立 `NSUserDefaults` suite、Android 走独立 prefs 文件,与宿主 App 自身的偏好数据互不干扰;用户卸载 / 抹机时会一并清除。
|
|
419
419
|
3. **从旧版本(依赖 AsyncStorage)升级会重置一次 UID**:原 AsyncStorage 中的 `_arms_uid` 不会自动迁移到新存储,老用户首次升级后会被分配新 UID(仅影响一次 UV 统计的连续性,不影响功能与后续使用)。如对历史 UID 连续性有强诉求,请提 issue。
|
|
420
420
|
|
|
421
|
+
## 版本要求
|
|
422
|
+
|
|
423
|
+
SDK 的 peerDependencies 声明为 `react-native: >=0.68.0`,原生构建配置已做多版本条件适配,不同功能点的实际要求如下:
|
|
424
|
+
|
|
425
|
+
| 功能点 | 关键依赖 / API | 最低 RN 版本 | 说明 |
|
|
426
|
+
|--------|--------------|-------------|------|
|
|
427
|
+
| **原生崩溃采集** | TurboModule + codegen | 0.68 | podspec / build.gradle 按版本条件分支,自动适配 0.68-0.70 和 0.71+ |
|
|
428
|
+
| NDK 崩溃采集(C/C++) | CMake + NDK `externalNativeBuild` | 0.68 | 随原生崩溃采集一起启用 |
|
|
429
|
+
| iOS 崩溃采集(KSCrash) | KSCrash ~2.0(CocoaPods 源码依赖) | 0.68 | `pod install` 时自动编译 |
|
|
430
|
+
| **JS 异常采集** | `ErrorUtils.setGlobalHandler` | 0.68 | 全版本可用 |
|
|
431
|
+
| Hermes Promise rejection | `HermesInternal.enablePromiseRejectionTracker` | 0.70(推荐) | Hermes 为默认引擎时完整支持 |
|
|
432
|
+
| JSC Promise rejection 兜底 | `globalThis.onunhandledrejection` | 0.68 | 非 Hermes 引擎有降级方案 |
|
|
433
|
+
| console.error 监听 | `interceptFunction(console, 'error')` | 0.68 | 全版本可用 |
|
|
434
|
+
| **API 性能监控** | `XMLHttpRequest.prototype` 拦截 | 0.68 | 全版本可用 |
|
|
435
|
+
| fetch 拦截 | `globalThis.fetch` patch | 0.68 | 当前已注释,仅启用 XHR 拦截 |
|
|
436
|
+
| 分布式链路追踪 | tracecontext / b3 / jaeger / sw8 | 0.68 | 纯 JS 实现,无原生依赖 |
|
|
437
|
+
| **页面导航追踪(PV)** | React Navigation `addListener('state')` | 0.68 | 兼容 React Navigation v5 / v6 |
|
|
438
|
+
| **用户行为采集(Action)** | patch `React.createElement` | 0.68 | 经典 JSX 运行时 |
|
|
439
|
+
| Pressable 组件支持 | `RN.Pressable` | 0.68 | Pressable 在 0.63 引入,下限受 peerDependencies 约束 |
|
|
440
|
+
| automatic JSX runtime patch | `react/jsx-runtime` 的 `jsx` / `jsxs` / `jsxDEV` | 0.73(推荐) | automatic runtime 在 RN 0.73+ 为默认 |
|
|
441
|
+
| **设备信息采集** | `Platform.OS` / `Dimensions.get()` | 0.68 | 全版本可用,原生桥失败有 JS 降级 |
|
|
442
|
+
| **自监控(Self-Monitoring)** | `fetch` 上报 Protobuf 至 `/rum/monitor` | 0.68 | 纯 JS 实现 |
|
|
443
|
+
| **Expo 集成** | Expo Config Plugin | Expo SDK 49+(RN 0.72+) | 仅配置透传,零运行时依赖 |
|
|
444
|
+
|
|
445
|
+
> **总结**:SDK 声明支持 RN ≥ 0.68,原生构建配置(podspec / build.gradle)已做多版本条件适配。RN 0.71+ 使用 `install_modules_dependencies` / `react-android`;RN 0.68-0.70 使用手动依赖 / `react-native:+`。JS 侧通过 `TurboModuleRegistry.get` + `NativeModules` 双路径获取原生模块,原生模块缺失时自动降级为空实现,不影响纯 JS 能力。
|
|
446
|
+
|
|
421
447
|
## 相关文档
|
|
422
448
|
|
|
423
449
|
- [ARMS用户体验监控文档](https://help.aliyun.com/zh/arms/user-experience-monitoring/)
|
|
@@ -10,7 +10,10 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# RN 0.73+ 通过 min_ios_version_supported 变量提供 iOS 最低版本;
|
|
14
|
+
# RN 0.68-0.72 无此变量,回退到 iOS 13.0。
|
|
15
|
+
min_ios = defined?(min_ios_version_supported) ? min_ios_version_supported : '13.0'
|
|
16
|
+
s.platforms = { :ios => min_ios }
|
|
14
17
|
s.source = { :git => "https://code.alibaba-inc.com/aliyun-rum/rum-sdk-reactnative.git", :tag => "#{s.version}" }
|
|
15
18
|
|
|
16
19
|
s.source_files = "ios/**/*.{h,m,mm,c,cpp}"
|
|
@@ -24,5 +27,19 @@ Pod::Spec.new do |s|
|
|
|
24
27
|
# 故不自行预编译,直接由宿主工程 `pod install` 时源码编译。
|
|
25
28
|
s.dependency "KSCrash", "~> 2.0"
|
|
26
29
|
|
|
27
|
-
install_modules_dependencies
|
|
30
|
+
if defined?(install_modules_dependencies)
|
|
31
|
+
# RN 0.71+ 使用 install_modules_dependencies 自动解析 React 依赖
|
|
32
|
+
install_modules_dependencies(s)
|
|
33
|
+
else
|
|
34
|
+
# RN 0.68-0.70 手动声明 React 依赖
|
|
35
|
+
s.dependency 'React-Core'
|
|
36
|
+
if ENV["RCT_NEW_ARCH_ENABLED"] == "1"
|
|
37
|
+
s.dependency "React-RCTFabric"
|
|
38
|
+
s.dependency "React-Codegen"
|
|
39
|
+
s.dependency "RCT-Folly"
|
|
40
|
+
s.dependency "RCTRequired"
|
|
41
|
+
s.dependency "RCTTypeSafety"
|
|
42
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
28
45
|
end
|
package/android/build.gradle
CHANGED
|
@@ -2,13 +2,71 @@ ext.getExtOrDefault = {name ->
|
|
|
2
2
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['RumSdkReactnative_' + name]
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
def getExtOrIntegerDefault(name) {
|
|
6
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RumSdkReactnative_" + name]).toInteger()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 定位宿主工程的 react-native 安装路径。
|
|
11
|
+
* 优先使用 REACT_NATIVE_NODE_MODULES_DIR 扩展属性,
|
|
12
|
+
* 其次尝试通过 node 解析(Gradle 7.5+),
|
|
13
|
+
* 最后搜索常见 node_modules 路径。
|
|
14
|
+
*/
|
|
15
|
+
def resolveReactNativeDir() {
|
|
16
|
+
def override = getExtOrDefault("REACT_NATIVE_NODE_MODULES_DIR")
|
|
17
|
+
if (override != null) {
|
|
18
|
+
return file(override)
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
// Gradle 7.5+ (RN 0.71+)
|
|
22
|
+
def resolved = providers.exec {
|
|
23
|
+
workingDir = rootDir
|
|
24
|
+
commandLine("node", "--print", "require.resolve('react-native/package.json')")
|
|
25
|
+
}.standardOutput.asText.get().trim()
|
|
26
|
+
return file(resolved).parentFile
|
|
27
|
+
} catch (Exception ignored) {
|
|
28
|
+
// Gradle < 7.5 (RN 0.68-0.70): 搜索常见路径
|
|
29
|
+
def candidates = [
|
|
30
|
+
file("${rootDir}/../node_modules/react-native"),
|
|
31
|
+
file("${projectDir}/../../node_modules/react-native"),
|
|
32
|
+
file("${rootDir}/node_modules/react-native")
|
|
33
|
+
]
|
|
34
|
+
for (def c : candidates) {
|
|
35
|
+
if (c.exists()) return c
|
|
36
|
+
}
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 判断宿主工程的 React Native 版本是否 >= 指定的 major.minor。
|
|
43
|
+
* 无法检测时默认返回 true(假设为较新版本)。
|
|
44
|
+
*/
|
|
45
|
+
def isRnVersionAtLeast(int minMajor, int minMinor) {
|
|
46
|
+
try {
|
|
47
|
+
def rnDir = resolveReactNativeDir()
|
|
48
|
+
if (rnDir == null) return true
|
|
49
|
+
def pkgFile = new File(rnDir, "package.json")
|
|
50
|
+
if (!pkgFile.exists()) return true
|
|
51
|
+
def version = new groovy.json.JsonSlurper().parse(pkgFile).version as String
|
|
52
|
+
def parts = version.tokenize(".")
|
|
53
|
+
def major = parts[0].toInteger()
|
|
54
|
+
def minor = parts[1].toInteger()
|
|
55
|
+
return major > minMajor || (major == minMajor && minor >= minMinor)
|
|
56
|
+
} catch (Exception ignored) {
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
def isRn71Plus = isRnVersionAtLeast(0, 71)
|
|
62
|
+
|
|
5
63
|
apply plugin: "com.android.library"
|
|
6
64
|
apply plugin: "kotlin-android"
|
|
7
65
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
66
|
+
// RN 0.71+ 提供 com.facebook.react Gradle 插件用于 codegen,
|
|
67
|
+
// RN 0.68-0.70 无此插件,跳过即可。
|
|
68
|
+
if (isRn71Plus) {
|
|
69
|
+
apply plugin: "com.facebook.react"
|
|
12
70
|
}
|
|
13
71
|
|
|
14
72
|
android {
|
|
@@ -78,6 +136,11 @@ repositories {
|
|
|
78
136
|
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
79
137
|
|
|
80
138
|
dependencies {
|
|
81
|
-
|
|
139
|
+
// RN 0.71+ 使用 react-android 制品,RN 0.68-0.70 使用 react-native:+
|
|
140
|
+
if (isRn71Plus) {
|
|
141
|
+
implementation "com.facebook.react:react-android"
|
|
142
|
+
} else {
|
|
143
|
+
implementation "com.facebook.react:react-native:+"
|
|
144
|
+
}
|
|
82
145
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
83
146
|
}
|
|
@@ -191,7 +191,7 @@ static void emit_frame(int fd, char *buf, size_t *pos,
|
|
|
191
191
|
|
|
192
192
|
int si = rum_module_index_for_addr(addr);
|
|
193
193
|
if (si >= 0) {
|
|
194
|
-
uintptr_t base =
|
|
194
|
+
uintptr_t base = rum_module_base_for_seg(si);
|
|
195
195
|
|
|
196
196
|
// 记录被引用模块的 image_addr(去重),供 binary_images 过滤
|
|
197
197
|
if (base != 0 && *referenced_count < MAX_REFERENCED_MODULES) {
|
|
@@ -316,6 +316,9 @@ void rum_write_crash(int fd, int signum, siginfo_t *info, ucontext_t *uc,
|
|
|
316
316
|
|
|
317
317
|
// 解析 /proc/self/maps(async-signal-safe),为帧提供模块归属与相对偏移
|
|
318
318
|
rum_modules_parse();
|
|
319
|
+
// 构建去重模块列表,供 emit_frame 获取模块级基址和 binary_images 枚举使用。
|
|
320
|
+
// 必须在 frames 循环前调用,确保 rum_module_base_for_seg() 能查到模块。
|
|
321
|
+
int mod_count = rum_module_build_distinct();
|
|
319
322
|
|
|
320
323
|
// binary_images 按需过滤:仅保留被栈帧引用的模块。
|
|
321
324
|
// 参照 Sentry 实践,服务端符号化时忽略未被任何栈帧引用的模块,
|
|
@@ -328,36 +331,28 @@ void rum_write_crash(int fd, int signum, siginfo_t *info, ucontext_t *uc,
|
|
|
328
331
|
// "frames":[{"address","library","image_addr","offset"}, ...]
|
|
329
332
|
// library/offset 供后端结合 binary_images 的 build_id 精确符号化
|
|
330
333
|
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
334
|
+
// NDK 信号处理器最多采集 MAX_FRAMES(512) 帧。frame_count 达到
|
|
335
|
+
// TRUNCATION_THRESHOLD(512) 时说明实际栈可能更长(已达到采集上限),
|
|
336
|
+
// 此时全部输出 512 帧 + 尾部标记帧(omitted_count=-1 表示实际总帧数
|
|
337
|
+
// 未知),不做双侧截断——因为 NDK 只能获取前 512 帧,前 256 + 后 256
|
|
338
|
+
// 恰好覆盖全部 512 帧,中间无省略,插入中间标记帧会造成冗余。
|
|
336
339
|
// frame_count < TRUNCATION_THRESHOLD 时全部输出,不截断。
|
|
337
340
|
// 所有操作仅为栈上数组读写与整数比较,符合 async-signal-safe。
|
|
338
341
|
buf_append(fd, buf, &pos, ",\"frames\":[");
|
|
339
|
-
int
|
|
342
|
+
int needs_tail_marker = (frame_count >= TRUNCATION_THRESHOLD);
|
|
340
343
|
int first = 1;
|
|
341
344
|
|
|
342
|
-
int
|
|
343
|
-
for (int i = 0; i < head_count; i++) {
|
|
345
|
+
for (int i = 0; i < frame_count; i++) {
|
|
344
346
|
emit_frame(fd, buf, &pos, (uintptr_t)frames[i],
|
|
345
347
|
referenced_addrs, &referenced_count, &first);
|
|
346
348
|
}
|
|
347
349
|
|
|
348
|
-
if (
|
|
349
|
-
//
|
|
350
|
+
if (needs_tail_marker) {
|
|
351
|
+
// 尾部标记帧(omitted_count=-1 表示实际总帧数未知)
|
|
350
352
|
if (!first) {
|
|
351
353
|
buf_append(fd, buf, &pos, ",");
|
|
352
354
|
}
|
|
353
355
|
buf_append(fd, buf, &pos, "{\"truncated\":true,\"omitted_count\":-1}");
|
|
354
|
-
first = 0;
|
|
355
|
-
|
|
356
|
-
int tail_start = frame_count - FRAMES_PER_SIDE;
|
|
357
|
-
for (int i = tail_start; i < frame_count; i++) {
|
|
358
|
-
emit_frame(fd, buf, &pos, (uintptr_t)frames[i],
|
|
359
|
-
referenced_addrs, &referenced_count, &first);
|
|
360
|
-
}
|
|
361
356
|
}
|
|
362
357
|
buf_append(fd, buf, &pos, "]");
|
|
363
358
|
|
|
@@ -375,7 +370,6 @@ void rum_write_crash(int fd, int signum, siginfo_t *info, ucontext_t *uc,
|
|
|
375
370
|
// 仅上报带 GNU build-id 的 file-backed 模块(build-id 等价于 iOS image uuid,
|
|
376
371
|
// 用于后端精确匹配符号文件)。
|
|
377
372
|
buf_append(fd, buf, &pos, ",\"binary_images\":[");
|
|
378
|
-
int mod_count = rum_module_build_distinct();
|
|
379
373
|
int emitted = 0;
|
|
380
374
|
char bid[BUILD_ID_HEX_CAP];
|
|
381
375
|
for (int m = 0; m < mod_count; m++) {
|
|
@@ -152,6 +152,27 @@ uintptr_t rum_seg_load_base(int seg_index) {
|
|
|
152
152
|
return g_segs[seg_index].start - g_segs[seg_index].offset;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
uintptr_t rum_module_base_for_seg(int seg_index) {
|
|
156
|
+
if (seg_index < 0 || seg_index >= g_seg_count) {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
// 查找该段所属模块(同路径),返回模块级基址(image_addr)。
|
|
160
|
+
// 与 binary_images 的 rum_module_image_addr() 一致,确保 emit_frame
|
|
161
|
+
// 收集的 referenced_addrs 与 binary_images 过滤比较的基址相同。
|
|
162
|
+
// 若 rum_module_build_distinct() 尚未调用(g_mod_count == 0),
|
|
163
|
+
// 回退到段级基址。
|
|
164
|
+
const char *path = g_segs[seg_index].path;
|
|
165
|
+
int path_len = g_segs[seg_index].path_len;
|
|
166
|
+
for (int m = 0; m < g_mod_count; m++) {
|
|
167
|
+
if (g_mods[m].path_len == path_len &&
|
|
168
|
+
memcmp(g_mods[m].path, path, (size_t)path_len) == 0) {
|
|
169
|
+
return g_mods[m].image_addr;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Fallback: 段级基址
|
|
173
|
+
return g_segs[seg_index].start - g_segs[seg_index].offset;
|
|
174
|
+
}
|
|
175
|
+
|
|
155
176
|
const char *rum_seg_path(int seg_index, int *out_len) {
|
|
156
177
|
if (seg_index < 0 || seg_index >= g_seg_count) {
|
|
157
178
|
if (out_len) *out_len = 0;
|
|
@@ -296,6 +317,24 @@ int rum_module_build_id_hex(uintptr_t load_base, char *out, int out_cap) {
|
|
|
296
317
|
const unsigned char *name = note + sizeof(ElfW(Nhdr));
|
|
297
318
|
const unsigned char *desc = name + ((namesz + 3u) & ~3u);
|
|
298
319
|
|
|
320
|
+
// 边界检查:确保 name 区域不超出 note section。
|
|
321
|
+
// 损坏的 namesz 可导致 name 指针越界,memcmp 读取未映射内存
|
|
322
|
+
// 触发二次 SIGSEGV。
|
|
323
|
+
if (name + namesz > note_end) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 边界检查:desc 起始地址(含对齐填充)不得超出 note section。
|
|
328
|
+
if (desc > note_end) {
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 边界检查:将 descsz clamp 到实际可用范围,防止 desc[b] 循环
|
|
333
|
+
// 读取超出 note section 的内存,触发二次 SIGSEGV。
|
|
334
|
+
if (desc + descsz > note_end) {
|
|
335
|
+
descsz = (uint32_t)(note_end - desc);
|
|
336
|
+
}
|
|
337
|
+
|
|
299
338
|
if (type == NT_GNU_BUILD_ID && namesz == 4 &&
|
|
300
339
|
memcmp(name, "GNU", 4) == 0) {
|
|
301
340
|
static const char hexc[] = "0123456789abcdef";
|
|
@@ -24,6 +24,10 @@ int rum_module_index_for_addr(uintptr_t addr);
|
|
|
24
24
|
/** 段的加载基址(= start - file_offset)。索引非法返回 0。 */
|
|
25
25
|
uintptr_t rum_seg_load_base(int seg_index);
|
|
26
26
|
|
|
27
|
+
/** 段所属模块的加载基址(模块级 image_addr,与 binary_images 一致)。
|
|
28
|
+
* 需在 rum_module_build_distinct() 之后调用;未构建时回退到段级基址。 */
|
|
29
|
+
uintptr_t rum_module_base_for_seg(int seg_index);
|
|
30
|
+
|
|
27
31
|
/** 段对应的模块路径(指向内部缓冲,非 null 结尾)。out_len 输出长度。 */
|
|
28
32
|
const char *rum_seg_path(int seg_index, int *out_len);
|
|
29
33
|
|
|
@@ -16,6 +16,10 @@ var _reactNative = require("react-native");
|
|
|
16
16
|
/**
|
|
17
17
|
* 获取原生模块。
|
|
18
18
|
*
|
|
19
|
+
* 优先使用 TurboModuleRegistry(RN 0.68+ TurboModule 架构),
|
|
20
|
+
* 若未找到则降级到 Legacy Bridge 的 NativeModules(RN 0.68-0.70 未开启 New Arch 时),
|
|
21
|
+
* 若均不可用则返回一组空实现兜底。
|
|
22
|
+
*
|
|
19
23
|
* 使用非强制的 `TurboModuleRegistry.get`(而非 `getEnforcing`),避免在原生
|
|
20
24
|
* 二进制未注册该模块时于「模块加载阶段」直接抛异常——那会连锁导致整个 JS
|
|
21
25
|
* bundle 评估失败(表现为所有路由丢失 default export、App 白屏)。
|
|
@@ -25,7 +29,7 @@ var _reactNative = require("react-native");
|
|
|
25
29
|
* JS 异常等纯 JS 能力不受影响。需重新构建原生工程(如 `expo run:ios` /
|
|
26
30
|
* `pod install` + 重新编译)后崩溃采集才会真正生效。
|
|
27
31
|
*/
|
|
28
|
-
const nativeModule = _reactNative.TurboModuleRegistry.get('RumSdkReactnative');
|
|
32
|
+
const nativeModule = _reactNative.TurboModuleRegistry.get('RumSdkReactnative') ?? _reactNative.NativeModules.RumSdkReactnative;
|
|
29
33
|
if (!nativeModule) {
|
|
30
34
|
console.warn("[rum] Native module 'RumSdkReactnative' not found in the native binary. " + 'Crash reporting and native device info are disabled. ' + 'Rebuild the native app (e.g. `expo run:ios` / `expo run:android`, ' + 'or `pod install` + rebuild) so the module gets registered.');
|
|
31
35
|
}
|
|
@@ -119,6 +119,9 @@ function formatStack(frames) {
|
|
|
119
119
|
// 截断标记帧:双侧截断时插入的中间标记
|
|
120
120
|
if (frame.truncated) {
|
|
121
121
|
const count = frame.omitted_count ?? -1;
|
|
122
|
+
if (count < 0) {
|
|
123
|
+
return `#${index} ... frames omitted (unknown count) ...`;
|
|
124
|
+
}
|
|
122
125
|
return `#${index} ... ${count} frames omitted ...`;
|
|
123
126
|
}
|
|
124
127
|
const library = frame.library || '<unknown>';
|
|
@@ -14,7 +14,7 @@ var _rumCore = require("@arms/rum-core");
|
|
|
14
14
|
var _reactNative = require("react-native");
|
|
15
15
|
var _NativeRumSdkReactnative = _interopRequireDefault(require("../NativeRumSdkReactnative"));
|
|
16
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
-
const _pkg = {"version":"0.1.3-beta.
|
|
17
|
+
const _pkg = {"version":"0.1.3-beta.2"};
|
|
18
18
|
const VERSION = exports.VERSION = _pkg.version;
|
|
19
19
|
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { TurboModuleRegistry } from 'react-native';
|
|
3
|
+
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* 原生设备信息,对应存储模型中的公共属性:
|
|
@@ -13,6 +13,10 @@ import { TurboModuleRegistry } from 'react-native';
|
|
|
13
13
|
/**
|
|
14
14
|
* 获取原生模块。
|
|
15
15
|
*
|
|
16
|
+
* 优先使用 TurboModuleRegistry(RN 0.68+ TurboModule 架构),
|
|
17
|
+
* 若未找到则降级到 Legacy Bridge 的 NativeModules(RN 0.68-0.70 未开启 New Arch 时),
|
|
18
|
+
* 若均不可用则返回一组空实现兜底。
|
|
19
|
+
*
|
|
16
20
|
* 使用非强制的 `TurboModuleRegistry.get`(而非 `getEnforcing`),避免在原生
|
|
17
21
|
* 二进制未注册该模块时于「模块加载阶段」直接抛异常——那会连锁导致整个 JS
|
|
18
22
|
* bundle 评估失败(表现为所有路由丢失 default export、App 白屏)。
|
|
@@ -22,7 +26,7 @@ import { TurboModuleRegistry } from 'react-native';
|
|
|
22
26
|
* JS 异常等纯 JS 能力不受影响。需重新构建原生工程(如 `expo run:ios` /
|
|
23
27
|
* `pod install` + 重新编译)后崩溃采集才会真正生效。
|
|
24
28
|
*/
|
|
25
|
-
const nativeModule = TurboModuleRegistry.get('RumSdkReactnative');
|
|
29
|
+
const nativeModule = TurboModuleRegistry.get('RumSdkReactnative') ?? NativeModules.RumSdkReactnative;
|
|
26
30
|
if (!nativeModule) {
|
|
27
31
|
console.warn("[rum] Native module 'RumSdkReactnative' not found in the native binary. " + 'Crash reporting and native device info are disabled. ' + 'Rebuild the native app (e.g. `expo run:ios` / `expo run:android`, ' + 'or `pod install` + rebuild) so the module gets registered.');
|
|
28
32
|
}
|
|
@@ -114,6 +114,9 @@ function formatStack(frames) {
|
|
|
114
114
|
// 截断标记帧:双侧截断时插入的中间标记
|
|
115
115
|
if (frame.truncated) {
|
|
116
116
|
const count = frame.omitted_count ?? -1;
|
|
117
|
+
if (count < 0) {
|
|
118
|
+
return `#${index} ... frames omitted (unknown count) ...`;
|
|
119
|
+
}
|
|
117
120
|
return `#${index} ... ${count} frames omitted ...`;
|
|
118
121
|
}
|
|
119
122
|
const library = frame.library || '<unknown>';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { AppType } from '@arms/rum-core';
|
|
4
4
|
import { Dimensions, Platform } from 'react-native';
|
|
5
5
|
import NativeRumSdkReactnative from "../NativeRumSdkReactnative.js";
|
|
6
|
-
const _pkg = {"version":"0.1.3-beta.
|
|
6
|
+
const _pkg = {"version":"0.1.3-beta.2"};
|
|
7
7
|
export const VERSION = _pkg.version;
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arms/rum-reactnative",
|
|
3
|
-
"version": "0.1.3-beta.
|
|
3
|
+
"version": "0.1.3-beta.2",
|
|
4
4
|
"description": "RUM SDK For ReactNative",
|
|
5
5
|
"homepage": "https://help.aliyun.com/zh/arms/user-experience-monitoring/",
|
|
6
6
|
"main": "./lib/commonjs/index.js",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"peerDependencies": {
|
|
110
110
|
"@arms/rum-core": ">=0.1.4",
|
|
111
111
|
"react": "*",
|
|
112
|
-
"react-native": "
|
|
112
|
+
"react-native": ">=0.68.0"
|
|
113
113
|
},
|
|
114
114
|
"workspaces": [
|
|
115
115
|
"example"
|