@arms/rum-reactnative 0.1.3-beta.5 → 0.1.4-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 +9 -2
- package/android/build.gradle +12 -1
- package/android/gradle.properties +3 -1
- package/android/src/main/AndroidManifest.xml +4 -1
- package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativeModule.kt +67 -14
- package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativePackage.kt +9 -2
- package/docs/ReactNative SDK/351/205/215/347/275/256/345/217/202/350/200/203.md" +25 -0
- package/docs/SDK /347/211/210/346/234/254/350/257/264/346/230/216.md" +20 -0
- package/docs//346/216/245/345/205/245ReactNative/345/272/224/347/224/250.md +2 -1
- package/ios/RumAppStateRecorder.h +69 -0
- package/ios/RumAppStateRecorder.mm +547 -0
- package/ios/RumCrashReporter.h +32 -0
- package/ios/RumCrashReporter.mm +97 -11
- package/ios/RumSdkReactnative.h +11 -1
- package/ios/RumSdkReactnative.mm +115 -29
- package/lib/commonjs/NativeRumSdkReactnative.js +1 -54
- package/lib/commonjs/collector/action/index.js +1 -108
- package/lib/commonjs/collector/exception/crash.js +1 -148
- package/lib/commonjs/collector/exception/index.js +1 -238
- package/lib/commonjs/collector/exception/oom.js +1 -0
- package/lib/commonjs/collector/index.js +1 -42
- package/lib/commonjs/collector/resource/api.js +1 -292
- package/lib/commonjs/collector/view/perf.js +1 -79
- package/lib/commonjs/collector/view/pv.js +1 -88
- package/lib/commonjs/expoConfigPlugin.js +1 -37
- package/lib/commonjs/index.js +1 -29
- package/lib/commonjs/plugin.js +1 -47
- package/lib/commonjs/processor/default-processor.js +1 -27
- package/lib/commonjs/processor/session-processor.js +1 -47
- package/lib/commonjs/reporter/index.js +1 -148
- package/lib/commonjs/reporter/monitor-reporter.js +1 -43
- package/lib/commonjs/shell.js +1 -91
- package/lib/commonjs/types/client.js +1 -6
- package/lib/commonjs/types/crash.js +1 -37
- package/lib/commonjs/types/index.js +1 -2
- package/lib/commonjs/types/oom.js +1 -0
- package/lib/commonjs/utils/action.js +1 -305
- package/lib/commonjs/utils/api.js +1 -84
- package/lib/commonjs/utils/base.js +1 -95
- package/lib/commonjs/utils/crash.js +1 -149
- package/lib/commonjs/utils/platform.js +1 -162
- package/lib/commonjs/utils/session.js +1 -158
- package/lib/commonjs/utils/url.js +1 -59
- package/lib/commonjs/utils/view.js +1 -93
- package/lib/module/NativeRumSdkReactnative.js +1 -51
- package/lib/module/collector/action/index.js +1 -103
- package/lib/module/collector/exception/crash.js +1 -143
- package/lib/module/collector/exception/index.js +1 -233
- package/lib/module/collector/exception/oom.js +1 -0
- package/lib/module/collector/index.js +1 -8
- package/lib/module/collector/resource/api.js +1 -287
- package/lib/module/collector/view/perf.js +1 -79
- package/lib/module/collector/view/pv.js +1 -83
- package/lib/module/expoConfigPlugin.js +1 -33
- package/lib/module/index.js +1 -7
- package/lib/module/plugin.js +1 -42
- package/lib/module/processor/default-processor.js +1 -22
- package/lib/module/processor/session-processor.js +1 -42
- package/lib/module/reporter/index.js +1 -144
- package/lib/module/reporter/monitor-reporter.js +1 -38
- package/lib/module/shell.js +1 -84
- package/lib/module/types/client.js +1 -4
- package/lib/module/types/crash.js +1 -33
- package/lib/module/types/index.js +1 -2
- package/lib/module/types/oom.js +1 -0
- package/lib/module/utils/action.js +1 -299
- package/lib/module/utils/api.js +1 -76
- package/lib/module/utils/base.js +1 -90
- package/lib/module/utils/crash.js +1 -144
- package/lib/module/utils/platform.js +1 -150
- package/lib/module/utils/session.js +1 -154
- package/lib/module/utils/url.js +1 -54
- package/lib/module/utils/view.js +1 -88
- package/lib/typescript/commonjs/src/NativeRumSdkReactnative.d.ts +10 -0
- package/lib/typescript/commonjs/src/collector/exception/oom.d.ts +34 -0
- package/lib/typescript/commonjs/src/collector/index.d.ts +1 -0
- package/lib/typescript/commonjs/src/shell.d.ts +1 -1
- package/lib/typescript/commonjs/src/types/client.d.ts +27 -0
- package/lib/typescript/commonjs/src/types/oom.d.ts +58 -0
- package/lib/typescript/commonjs/src/utils/crash.d.ts +13 -0
- package/lib/typescript/module/src/NativeRumSdkReactnative.d.ts +10 -0
- package/lib/typescript/module/src/collector/exception/oom.d.ts +34 -0
- package/lib/typescript/module/src/collector/index.d.ts +1 -0
- package/lib/typescript/module/src/shell.d.ts +1 -1
- package/lib/typescript/module/src/types/client.d.ts +27 -0
- package/lib/typescript/module/src/types/oom.d.ts +58 -0
- package/lib/typescript/module/src/utils/crash.d.ts +13 -0
- package/package.json +1 -2
- package/src/NativeRumSdkReactnative.ts +18 -0
|
@@ -1,59 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.getPathByURL = getPathByURL;
|
|
7
|
-
exports.getURL = getURL;
|
|
8
|
-
/**
|
|
9
|
-
* 根据指定 URL 获取视图的名称,默认为其path部分
|
|
10
|
-
* @param url 需要提取路径部分的 URL
|
|
11
|
-
* @param isHash 是否为 hash 模式路由的 URL
|
|
12
|
-
*/
|
|
13
|
-
function getPathByURL(url, isHash = false) {
|
|
14
|
-
try {
|
|
15
|
-
const urlObj = getURL(url);
|
|
16
|
-
if (!urlObj) return;
|
|
17
|
-
if (isHash) {
|
|
18
|
-
return urlObj.hash;
|
|
19
|
-
} else {
|
|
20
|
-
return urlObj.pathname;
|
|
21
|
-
}
|
|
22
|
-
} catch (error) {
|
|
23
|
-
return '';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// /**
|
|
28
|
-
// * 检查是否为上报域名
|
|
29
|
-
// * @param ctx
|
|
30
|
-
// * @param url 需要提取路径部分的 URL
|
|
31
|
-
// */
|
|
32
|
-
// export function isEndpoint(ctx: IContext, url: string): boolean {
|
|
33
|
-
// const { endpoint } = ctx.getConfig();
|
|
34
|
-
// try {
|
|
35
|
-
// return getURL(url).hostname === getURL(endpoint).hostname
|
|
36
|
-
// } catch (e) {
|
|
37
|
-
// return false;
|
|
38
|
-
// }
|
|
39
|
-
// }
|
|
40
|
-
|
|
41
|
-
const REGEX = /^(?:([^:\/?#]+):\/\/)?((?:([^\/?#@]*)@)?([^\/?#:]*)(?:\:(\d*))?)?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n)*))?/i;
|
|
42
|
-
function getURL(url) {
|
|
43
|
-
const parts = decodeURIComponent(url).match(REGEX);
|
|
44
|
-
if (!parts) return null;
|
|
45
|
-
const auth = (parts[3] || '').split(':');
|
|
46
|
-
const host = auth.length ? (parts[2] || '').replace(/(.*\@)/, '') : parts[2];
|
|
47
|
-
return {
|
|
48
|
-
uri: parts[0],
|
|
49
|
-
protocol: parts[1],
|
|
50
|
-
host: host,
|
|
51
|
-
hostname: parts[4],
|
|
52
|
-
port: parts[5],
|
|
53
|
-
pathname: parts[6],
|
|
54
|
-
search: parts[7],
|
|
55
|
-
// query: mapSearchParams(parts[7]),
|
|
56
|
-
hash: parts[8]
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=url.js.map
|
|
1
|
+
"use strict";function getPathByURL(t,e=!1){try{const n=getURL(t);if(!n)return;return e?n.hash:n.pathname}catch(t){return""}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getPathByURL=getPathByURL,exports.getURL=getURL;const REGEX=/^(?:([^:\/?#]+):\/\/)?((?:([^\/?#@]*)@)?([^\/?#:]*)(?:\:(\d*))?)?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n)*))?/i;function getURL(t){const e=decodeURIComponent(t).match(REGEX);if(!e)return null;const n=(e[3]||"").split(":").length?(e[2]||"").replace(/(.*\@)/,""):e[2];return{uri:e[0],protocol:e[1],host:n,hostname:e[4],port:e[5],pathname:e[6],search:e[7],hash:e[8]}}
|
|
@@ -1,93 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.addNavigateListener = addNavigateListener;
|
|
7
|
-
exports.getCurView = getCurView;
|
|
8
|
-
exports.removeNavigateListener = removeNavigateListener;
|
|
9
|
-
exports.startTrackingViews = startTrackingViews;
|
|
10
|
-
exports.stopTrackingViews = stopTrackingViews;
|
|
11
|
-
var _rumCore = require("@arms/rum-core");
|
|
12
|
-
// import { IPage } from "../types/client";
|
|
13
|
-
|
|
14
|
-
// export const PAGE_ID = '__rum_view_id';
|
|
15
|
-
//
|
|
16
|
-
// /**
|
|
17
|
-
// * 获取 RUM 内置的view_id标识
|
|
18
|
-
// * @param page
|
|
19
|
-
// * @param session
|
|
20
|
-
// * @returns
|
|
21
|
-
// */
|
|
22
|
-
// export async function getRumViewId(page: IPage, session?: IRumSession): Promise<string> {
|
|
23
|
-
// if (!page) return;
|
|
24
|
-
// if (page.id) {
|
|
25
|
-
// return page.id;
|
|
26
|
-
// }
|
|
27
|
-
// if (!session) return;
|
|
28
|
-
// return page.id = session.getViewId();
|
|
29
|
-
// }
|
|
30
|
-
//
|
|
31
|
-
// /**
|
|
32
|
-
// * 获取当前 view
|
|
33
|
-
// * @param viewId
|
|
34
|
-
// * @param ctx
|
|
35
|
-
// * @returns
|
|
36
|
-
// */
|
|
37
|
-
// export function getViewById(viewId: string, ctx: IContext): IViewData {
|
|
38
|
-
// if (!viewId || !ctx) return;
|
|
39
|
-
// return ctx.getViews().find(view => view.id === viewId);
|
|
40
|
-
// }
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 获取当前 view
|
|
44
|
-
* @param ctx
|
|
45
|
-
* @returns
|
|
46
|
-
*/
|
|
47
|
-
function getCurView(ctx) {
|
|
48
|
-
if (!ctx) return;
|
|
49
|
-
const views = ctx.getViews();
|
|
50
|
-
if (views && views.length) {
|
|
51
|
-
return views[views.length - 1];
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
let currentNavigationRef;
|
|
56
|
-
const navigateListeners = [];
|
|
57
|
-
function addNavigateListener(listener) {
|
|
58
|
-
if ((0, _rumCore.isFunction)(listener)) {
|
|
59
|
-
navigateListeners.push(listener);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function removeNavigateListener(listener) {
|
|
63
|
-
navigateListeners.splice(navigateListeners.indexOf(listener), 1);
|
|
64
|
-
}
|
|
65
|
-
function triggerNavigateChange(event) {
|
|
66
|
-
if (!currentNavigationRef) return;
|
|
67
|
-
let route = event;
|
|
68
|
-
if ((0, _rumCore.isFunction)(currentNavigationRef.getCurrentRoute)) {
|
|
69
|
-
route = currentNavigationRef.getCurrentRoute();
|
|
70
|
-
}
|
|
71
|
-
if (!route) return;
|
|
72
|
-
navigateListeners.forEach(listener => {
|
|
73
|
-
listener(route);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
function startTrackingViews(nav) {
|
|
77
|
-
if (!nav || currentNavigationRef) return;
|
|
78
|
-
currentNavigationRef = nav;
|
|
79
|
-
if ((0, _rumCore.isFunction)(nav.events)) {
|
|
80
|
-
// react-native-navigation
|
|
81
|
-
nav.events().registerComponentDidAppearListener(triggerNavigateChange);
|
|
82
|
-
} else {
|
|
83
|
-
// @react-native/navigation
|
|
84
|
-
nav.addListener('state', triggerNavigateChange);
|
|
85
|
-
}
|
|
86
|
-
triggerNavigateChange({});
|
|
87
|
-
}
|
|
88
|
-
function stopTrackingViews() {
|
|
89
|
-
if (!currentNavigationRef) return;
|
|
90
|
-
currentNavigationRef.removeListener('state', triggerNavigateChange);
|
|
91
|
-
currentNavigationRef = undefined;
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=view.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.addNavigateListener=addNavigateListener,exports.getCurView=getCurView,exports.removeNavigateListener=removeNavigateListener,exports.startTrackingViews=startTrackingViews,exports.stopTrackingViews=stopTrackingViews;var _rumCore=require("@arms/rum-core");function getCurView(e){if(!e)return;const t=e.getViews();return t&&t.length?t[t.length-1]:null}let currentNavigationRef;const navigateListeners=[];function addNavigateListener(e){(0,_rumCore.isFunction)(e)&&navigateListeners.push(e)}function removeNavigateListener(e){navigateListeners.splice(navigateListeners.indexOf(e),1)}function triggerNavigateChange(e){if(!currentNavigationRef)return;let t=e;(0,_rumCore.isFunction)(currentNavigationRef.getCurrentRoute)&&(t=currentNavigationRef.getCurrentRoute()),t&&navigateListeners.forEach(e=>{e(t)})}function startTrackingViews(e){e&&!currentNavigationRef&&(currentNavigationRef=e,(0,_rumCore.isFunction)(e.events)?e.events().registerComponentDidAppearListener(triggerNavigateChange):e.addListener("state",triggerNavigateChange),triggerNavigateChange({}))}function stopTrackingViews(){currentNavigationRef&&(currentNavigationRef.removeListener("state",triggerNavigateChange),currentNavigationRef=void 0)}
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 原生设备信息,对应存储模型中的公共属性:
|
|
7
|
-
* - device.* (id / type / brand / model / name)
|
|
8
|
-
* - os.* (type / version / container / container_version)
|
|
9
|
-
* - app.package
|
|
10
|
-
* - viewport.width / viewport.height
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 获取原生模块。
|
|
15
|
-
*
|
|
16
|
-
* 优先使用 TurboModuleRegistry(RN 0.68+ TurboModule 架构),
|
|
17
|
-
* 若未找到则降级到 Legacy Bridge 的 NativeModules(RN 0.68-0.70 未开启 New Arch 时),
|
|
18
|
-
* 若均不可用则返回一组空实现兜底。
|
|
19
|
-
*
|
|
20
|
-
* 使用非强制的 `TurboModuleRegistry.get`(而非 `getEnforcing`),避免在原生
|
|
21
|
-
* 二进制未注册该模块时于「模块加载阶段」直接抛异常——那会连锁导致整个 JS
|
|
22
|
-
* bundle 评估失败(表现为所有路由丢失 default export、App 白屏)。
|
|
23
|
-
*
|
|
24
|
-
* 当原生模块缺失时(通常是新增原生代码后未重新编译原生 App,只重跑了 Metro),
|
|
25
|
-
* 返回一组空实现兜底:崩溃采集与原生设备信息降级为不可用,但 PV / 行为 / API /
|
|
26
|
-
* JS 异常等纯 JS 能力不受影响。需重新构建原生工程(如 `expo run:ios` /
|
|
27
|
-
* `pod install` + 重新编译)后崩溃采集才会真正生效。
|
|
28
|
-
*/
|
|
29
|
-
const nativeModule = TurboModuleRegistry.get('RumSdkReactnative') ?? NativeModules.RumSdkReactnative;
|
|
30
|
-
if (!nativeModule) {
|
|
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.');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 原生模块缺失时的空实现,保证调用方不会因缺方法而抛错。 */
|
|
35
|
-
const fallback = {
|
|
36
|
-
getItem: async () => null,
|
|
37
|
-
setItem: async () => {},
|
|
38
|
-
removeItem: async () => {},
|
|
39
|
-
clear: async () => {},
|
|
40
|
-
getAllKeys: async () => [],
|
|
41
|
-
getDeviceInfo: async () => ({}),
|
|
42
|
-
getPendingCrashes: async () => '[]',
|
|
43
|
-
clearCrashReports: async () => {},
|
|
44
|
-
clearAllCrashReports: async () => {},
|
|
45
|
-
cleanupOldCrashes: async () => {},
|
|
46
|
-
crashedLastRun: async () => false,
|
|
47
|
-
updateCrashContext: () => {},
|
|
48
|
-
triggerTestCrash: () => {}
|
|
49
|
-
};
|
|
50
|
-
export default nativeModule ?? fallback;
|
|
51
|
-
//# sourceMappingURL=NativeRumSdkReactnative.js.map
|
|
1
|
+
"use strict";import{NativeModules,TurboModuleRegistry}from"react-native";const nativeModule=TurboModuleRegistry.get("RumSdkReactnative")??NativeModules.RumSdkReactnative;nativeModule||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.");const fallback={getItem:async()=>null,setItem:async()=>{},removeItem:async()=>{},clear:async()=>{},getAllKeys:async()=>[],getDeviceInfo:async()=>({}),getPendingCrashes:async()=>"[]",clearCrashReports:async()=>{},clearAllCrashReports:async()=>{},cleanupOldCrashes:async()=>{},crashedLastRun:async()=>!1,updateCrashContext:()=>{},triggerTestCrash:()=>{},getPendingOomReports:async()=>"[]",clearOomReports:async()=>{},updateCollectorsConfig:()=>{}};export default nativeModule??fallback;
|
|
@@ -1,103 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { RumEventType } from '@arms/rum-core';
|
|
4
|
-
import { getCurView } from "../../utils/view.js";
|
|
5
|
-
import { addActionListener, removeActionListener, startTrackingActions, stopTrackingActions } from "../../utils/action.js";
|
|
6
|
-
export default class ActionCollector {
|
|
7
|
-
name = 'action-collector';
|
|
8
|
-
DELAY_TIME = 300;
|
|
9
|
-
events = ['tap'];
|
|
10
|
-
setup(ctx, sendEvent) {
|
|
11
|
-
const {
|
|
12
|
-
collectors = {}
|
|
13
|
-
} = ctx.getConfig();
|
|
14
|
-
if (collectors.action === false) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
this.ctx = ctx;
|
|
18
|
-
this.sendEvent = sendEvent;
|
|
19
|
-
// 开启 React Native 触摸自动采集,并监听标准化的 tap 事件
|
|
20
|
-
startTrackingActions();
|
|
21
|
-
addActionListener(this.onEvent);
|
|
22
|
-
}
|
|
23
|
-
onEvent = e => {
|
|
24
|
-
if (e && this.events.includes(e.type) && e.target) {
|
|
25
|
-
const {
|
|
26
|
-
target,
|
|
27
|
-
currentTarget,
|
|
28
|
-
type
|
|
29
|
-
} = e;
|
|
30
|
-
const {
|
|
31
|
-
dataset,
|
|
32
|
-
id
|
|
33
|
-
} = target;
|
|
34
|
-
if (!id) return;
|
|
35
|
-
if (currentTarget && id !== currentTarget.id) return;
|
|
36
|
-
let view;
|
|
37
|
-
if (this.ctx) {
|
|
38
|
-
view = getCurView(this.ctx);
|
|
39
|
-
}
|
|
40
|
-
const event = {
|
|
41
|
-
event_type: RumEventType.ACTION,
|
|
42
|
-
type,
|
|
43
|
-
name: e.name || id,
|
|
44
|
-
target_name: e.targetName,
|
|
45
|
-
duration: e.duration,
|
|
46
|
-
position: e.position,
|
|
47
|
-
target: this.buildTarget(target),
|
|
48
|
-
snapshots: dataset && Object.keys(dataset).length ? JSON.stringify(dataset).slice(0, 1000) : undefined,
|
|
49
|
-
times: 1,
|
|
50
|
-
view
|
|
51
|
-
};
|
|
52
|
-
clearTimeout(this.timer);
|
|
53
|
-
if (this.lastEvent && this.lastActionId === id) {
|
|
54
|
-
// 同一元素的连续点击:仅累加次数
|
|
55
|
-
// @ts-ignore
|
|
56
|
-
this.lastEvent.times += 1;
|
|
57
|
-
} else {
|
|
58
|
-
// 切换到不同元素:立即上报上一个,再记录新事件
|
|
59
|
-
if (this.lastEvent) {
|
|
60
|
-
this.sendAction();
|
|
61
|
-
}
|
|
62
|
-
this.lastEvent = event;
|
|
63
|
-
this.lastActionId = id;
|
|
64
|
-
}
|
|
65
|
-
this.timer = setTimeout(this.sendAction, this.DELAY_TIME);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 组装热力图相关的 target 信息(selector/宽高/可交互性/可信度)。
|
|
71
|
-
* 当没有任何字段时返回 undefined,避免上报空对象。
|
|
72
|
-
*/
|
|
73
|
-
buildTarget(target) {
|
|
74
|
-
const {
|
|
75
|
-
selector,
|
|
76
|
-
width,
|
|
77
|
-
height,
|
|
78
|
-
reaction,
|
|
79
|
-
trust
|
|
80
|
-
} = target;
|
|
81
|
-
if (selector === undefined && width === undefined && height === undefined && reaction === undefined && trust === undefined) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
selector,
|
|
86
|
-
width,
|
|
87
|
-
height,
|
|
88
|
-
reaction,
|
|
89
|
-
trust
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
sendAction = () => {
|
|
93
|
-
// @ts-ignore
|
|
94
|
-
this.sendEvent(this.lastEvent);
|
|
95
|
-
this.lastEvent = undefined;
|
|
96
|
-
this.lastActionId = undefined;
|
|
97
|
-
};
|
|
98
|
-
destroy() {
|
|
99
|
-
removeActionListener(this.onEvent);
|
|
100
|
-
stopTrackingActions();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";import{RumEventType}from"@arms/rum-core";import{getCurView}from"../../utils/view.js";import{addActionListener,removeActionListener,startTrackingActions,stopTrackingActions}from"../../utils/action.js";export default class ActionCollector{name="action-collector";DELAY_TIME=300;events=["tap"];setup(t,e){const{collectors:i={}}=t.getConfig();!1!==i.action&&(this.ctx=t,this.sendEvent=e,startTrackingActions(),addActionListener(this.onEvent))}onEvent=t=>{if(t&&this.events.includes(t.type)&&t.target){const{target:e,currentTarget:i,type:s}=t,{dataset:n,id:o}=e;if(!o)return;if(i&&o!==i.id)return;let r;this.ctx&&(r=getCurView(this.ctx));const c={event_type:RumEventType.ACTION,type:s,name:t.name||o,target_name:t.targetName,duration:t.duration,position:t.position,target:this.buildTarget(e),snapshots:n&&Object.keys(n).length?JSON.stringify(n).slice(0,1e3):void 0,times:1,view:r};clearTimeout(this.timer),this.lastEvent&&this.lastActionId===o?this.lastEvent.times+=1:(this.lastEvent&&this.sendAction(),this.lastEvent=c,this.lastActionId=o),this.timer=setTimeout(this.sendAction,this.DELAY_TIME)}};buildTarget(t){const{selector:e,width:i,height:s,reaction:n,trust:o}=t;if(void 0!==e||void 0!==i||void 0!==s||void 0!==n||void 0!==o)return{selector:e,width:i,height:s,reaction:n,trust:o}}sendAction=()=>{this.sendEvent(this.lastEvent),this.lastEvent=void 0,this.lastActionId=void 0};destroy(){removeActionListener(this.onEvent),stopTrackingActions()}}
|
|
@@ -1,143 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { logger } from '@arms/rum-core';
|
|
4
|
-
import NativeRumSdkReactnative from "../../NativeRumSdkReactnative.js";
|
|
5
|
-
import { CRASH_CONTEXT_EVENT, CRASH_ID_FIELD, CRASH_REPORT_ACK_EVENT } from "../../types/crash.js";
|
|
6
|
-
import { convertCrashToRumEvent, safeNativeCall } from "../../utils/crash.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 崩溃采集器(Exception 的子类型):
|
|
10
|
-
* 1. 初始化时读取上次运行写入的原生崩溃文件,转换为 RumEvent 通过标准管线上报;
|
|
11
|
-
* 2. 通过 ctx.emitter 订阅上下文变更事件,同步到原生侧
|
|
12
|
-
* (用于下一次启动时崩溃报告携带正确上下文)。
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export default class CrashCollector {
|
|
16
|
-
name = 'crash-collector';
|
|
17
|
-
initialized = false;
|
|
18
|
-
/** 已上报待确认(ack)的 crash_id,成功后才删文件 */
|
|
19
|
-
pendingAckIds = new Set();
|
|
20
|
-
setup(ctx, sendEvent) {
|
|
21
|
-
const {
|
|
22
|
-
collectors = {}
|
|
23
|
-
} = ctx.getConfig();
|
|
24
|
-
if (collectors.crash === false) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
this.ctx = ctx;
|
|
28
|
-
this.sendEvent = sendEvent;
|
|
29
|
-
|
|
30
|
-
// 订阅上下文变更事件,同步到原生侧
|
|
31
|
-
ctx.emitter.on(CRASH_CONTEXT_EVENT, this.onCrashContextChange);
|
|
32
|
-
// 订阅上报确认事件:reporter 上报成功后回 ack,再删除对应崩溃文件
|
|
33
|
-
ctx.emitter.on(CRASH_REPORT_ACK_EVENT, this.onCrashReportAck);
|
|
34
|
-
|
|
35
|
-
// 顺序执行:先处理待上报崩溃,再清理旧文件(避免竞态)
|
|
36
|
-
this.processPendingCrashes().then(() => this.cleanupOldCrashes()).catch(e => {
|
|
37
|
-
logger.error('crash-collector', 'Failed to process crash reports', e);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
async processPendingCrashes() {
|
|
41
|
-
if (this.initialized) return;
|
|
42
|
-
this.initialized = true;
|
|
43
|
-
|
|
44
|
-
// 不以 crashedLastRun 作为 gating:KSCrash 的 crashedLastLaunch 只在崩溃后的下一次
|
|
45
|
-
// 启动为 true,再下一次正常启动就清零,会导致遗留崩溃文件永远不被处理。
|
|
46
|
-
// 只要磁盘上有 pending 文件就读取并上报。
|
|
47
|
-
await safeNativeCall(() => NativeRumSdkReactnative.crashedLastRun(), 'crashedLastRun');
|
|
48
|
-
const crashesJson = await safeNativeCall(() => NativeRumSdkReactnative.getPendingCrashes(), 'getPendingCrashes');
|
|
49
|
-
if (!crashesJson) return;
|
|
50
|
-
let crashes = [];
|
|
51
|
-
try {
|
|
52
|
-
crashes = JSON.parse(crashesJson) || [];
|
|
53
|
-
} catch (e) {
|
|
54
|
-
logger.error('crash-collector', 'Failed to parse crashes JSON', e);
|
|
55
|
-
// 清理损坏的文件,避免每次启动重复尝试解析
|
|
56
|
-
await safeNativeCall(() => NativeRumSdkReactnative.clearAllCrashReports(), 'clearAllCrashReports');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (!Array.isArray(crashes) || crashes.length === 0) return;
|
|
60
|
-
const crashIds = [];
|
|
61
|
-
const hasFilesWithoutId = crashes.some(c => !c.crash_id);
|
|
62
|
-
for (let i = 0; i < crashes.length; i++) {
|
|
63
|
-
try {
|
|
64
|
-
const event = convertCrashToRumEvent(crashes[i]);
|
|
65
|
-
const cid = crashes[i].crash_id;
|
|
66
|
-
// 将瞬态 crash_id 注入 snapshots(而非根级),reporter 上报前从中提取
|
|
67
|
-
// 用于 ack,并在序列化前从 snapshots 中剥离,不会发送到后端
|
|
68
|
-
if (cid) {
|
|
69
|
-
try {
|
|
70
|
-
const snapshotsObj = JSON.parse(event.snapshots);
|
|
71
|
-
snapshotsObj[CRASH_ID_FIELD] = cid;
|
|
72
|
-
event.snapshots = JSON.stringify(snapshotsObj);
|
|
73
|
-
} catch {
|
|
74
|
-
// snapshots 解析失败时降级:仍挂载到根级以保证 ack 机制可用
|
|
75
|
-
event[CRASH_ID_FIELD] = cid;
|
|
76
|
-
}
|
|
77
|
-
this.pendingAckIds.add(cid);
|
|
78
|
-
crashIds.push(cid);
|
|
79
|
-
}
|
|
80
|
-
// 最后一个崩溃事件使用 immediate 立即 flush 整个队列
|
|
81
|
-
const isLast = i === crashes.length - 1;
|
|
82
|
-
this.sendEvent?.(event, isLast ? {
|
|
83
|
-
immediate: true
|
|
84
|
-
} : undefined);
|
|
85
|
-
} catch (e) {
|
|
86
|
-
logger.warn('crash-collector', 'Failed to convert crash', e);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 有 crash_id 的文件:不在此处删除,等 reporter 上报成功后由 onCrashReportAck
|
|
91
|
-
// 删除;上报失败则保留,下次启动重试(cleanupOldCrashes 兜底防止无限增长)。
|
|
92
|
-
|
|
93
|
-
// 无 crash_id 的旧版 NDK 崩溃文件无法 ack:仅当本次没有待 ack 的带 id 文件时
|
|
94
|
-
// 才安全清理(clearAllCrashReports 会删除全部文件,避免误删 ack-pending)。
|
|
95
|
-
if (hasFilesWithoutId && crashIds.length === 0) {
|
|
96
|
-
await safeNativeCall(() => NativeRumSdkReactnative.clearAllCrashReports(), 'clearAllCrashReports');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** 兜底清理:删除过期和超限的崩溃文件,防止存储耗尽 */
|
|
101
|
-
async cleanupOldCrashes() {
|
|
102
|
-
// 最多保留 10 个崩溃文件,7 天过期
|
|
103
|
-
const MAX_FILES = 10;
|
|
104
|
-
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
105
|
-
await safeNativeCall(() => NativeRumSdkReactnative.cleanupOldCrashes(MAX_FILES, MAX_AGE_MS), 'cleanupOldCrashes');
|
|
106
|
-
}
|
|
107
|
-
onCrashContextChange = context => {
|
|
108
|
-
try {
|
|
109
|
-
NativeRumSdkReactnative.updateCrashContext(JSON.stringify(context));
|
|
110
|
-
} catch {
|
|
111
|
-
//
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* reporter 上报确认回调:
|
|
117
|
-
* - 成功:删除对应 crash_id 的崩溃文件(确保上报成功后才删,不丢数据);
|
|
118
|
-
* - 失败:保留文件,下次启动重试,仅从内存待确认集合中移除。
|
|
119
|
-
*/
|
|
120
|
-
onCrashReportAck = ack => {
|
|
121
|
-
if (!ack || !Array.isArray(ack.crashIds) || ack.crashIds.length === 0) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
// 只处理本采集器登记过的 id
|
|
125
|
-
const toClear = ack.crashIds.filter(id => this.pendingAckIds.has(id));
|
|
126
|
-
if (toClear.length === 0) return;
|
|
127
|
-
if (!ack.success) {
|
|
128
|
-
// 上报失败:文件保留,从待确认集合移除(下次启动重新登记与上报)
|
|
129
|
-
for (const id of toClear) this.pendingAckIds.delete(id);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
safeNativeCall(() => NativeRumSdkReactnative.clearCrashReports(JSON.stringify(toClear)), 'clearCrashReports').finally(() => {
|
|
133
|
-
for (const id of toClear) this.pendingAckIds.delete(id);
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
|
-
destroy() {
|
|
137
|
-
this.ctx?.emitter.off(CRASH_CONTEXT_EVENT, this.onCrashContextChange);
|
|
138
|
-
this.ctx?.emitter.off(CRASH_REPORT_ACK_EVENT, this.onCrashReportAck);
|
|
139
|
-
this.pendingAckIds.clear();
|
|
140
|
-
this.sendEvent = undefined;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
//# sourceMappingURL=crash.js.map
|
|
1
|
+
"use strict";import{logger}from"@arms/rum-core";import NativeRumSdkReactnative from"../../NativeRumSdkReactnative.js";import{CRASH_CONTEXT_EVENT,CRASH_ID_FIELD,CRASH_REPORT_ACK_EVENT}from"../../types/crash.js";import{convertCrashToRumEvent,safeNativeCall}from"../../utils/crash.js";export default class CrashCollector{name="crash-collector";initialized=!1;pendingAckIds=new Set;setup(e,t){const{collectors:s={}}=e.getConfig();!1!==s.crash&&(this.ctx=e,this.sendEvent=t,e.emitter.on(CRASH_CONTEXT_EVENT,this.onCrashContextChange),e.emitter.on(CRASH_REPORT_ACK_EVENT,this.onCrashReportAck),this.processPendingCrashes().then(()=>this.cleanupOldCrashes()).catch(e=>{logger.error("crash-collector","Failed to process crash reports",e)}))}async processPendingCrashes(){if(this.initialized)return;this.initialized=!0,await safeNativeCall(()=>NativeRumSdkReactnative.crashedLastRun(),"crashedLastRun");const e=await safeNativeCall(()=>NativeRumSdkReactnative.getPendingCrashes(),"getPendingCrashes");if(!e)return;let t=[];try{t=JSON.parse(e)||[]}catch(e){return logger.error("crash-collector","Failed to parse crashes JSON",e),void await safeNativeCall(()=>NativeRumSdkReactnative.clearAllCrashReports(),"clearAllCrashReports")}if(!Array.isArray(t)||0===t.length)return;const s=[],a=t.some(e=>!e.crash_id);for(let e=0;e<t.length;e++)try{const a=convertCrashToRumEvent(t[e]),r=t[e].crash_id;if(r){try{const e=JSON.parse(a.snapshots);e[CRASH_ID_FIELD]=r,a.snapshots=JSON.stringify(e)}catch{a[CRASH_ID_FIELD]=r}this.pendingAckIds.add(r),s.push(r)}const i=e===t.length-1;this.sendEvent?.(a,i?{immediate:!0}:void 0)}catch(e){logger.warn("crash-collector","Failed to convert crash",e)}a&&0===s.length&&await safeNativeCall(()=>NativeRumSdkReactnative.clearAllCrashReports(),"clearAllCrashReports")}async cleanupOldCrashes(){await safeNativeCall(()=>NativeRumSdkReactnative.cleanupOldCrashes(10,6048e5),"cleanupOldCrashes")}onCrashContextChange=e=>{try{NativeRumSdkReactnative.updateCrashContext(JSON.stringify(e))}catch{}};onCrashReportAck=e=>{if(!e||!Array.isArray(e.crashIds)||0===e.crashIds.length)return;const t=e.crashIds.filter(e=>this.pendingAckIds.has(e));if(0!==t.length)if(e.success)safeNativeCall(()=>NativeRumSdkReactnative.clearCrashReports(JSON.stringify(t)),"clearCrashReports").finally(()=>{for(const e of t)this.pendingAckIds.delete(e)});else for(const e of t)this.pendingAckIds.delete(e)};destroy(){this.ctx?.emitter.off(CRASH_CONTEXT_EVENT,this.onCrashContextChange),this.ctx?.emitter.off(CRASH_REPORT_ACK_EVENT,this.onCrashReportAck),this.pendingAckIds.clear(),this.sendEvent=void 0}}
|