@grafana/faro-react-native 1.2.0 → 1.3.0
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/android/build.gradle +118 -36
- package/android/consumer-proguard-rules.pro +2 -0
- package/android/src/main/java/com/grafana/faro/reactnative/ANRTracker.kt +61 -9
- package/android/src/main/java/com/grafana/faro/reactnative/AnrThreadDumpParser.kt +70 -0
- package/android/src/main/java/com/grafana/faro/reactnative/ApplicationExitTraceReader.kt +142 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrCache.kt +163 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrReporter.kt +163 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +243 -50
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +75 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroNativeCrashTrace.kt +39 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +99 -8
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativePackage.kt +1 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroUncaughtExceptionHandler.kt +62 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneBacktraceFormatter.kt +117 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneModels.kt +35 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneWireParser.kt +235 -0
- package/android/src/main/proto/tombstone.proto +62 -0
- package/android/src/test/java/com/grafana/faro/reactnative/AnrStackFiltersTest.kt +28 -0
- package/android/src/test/java/com/grafana/faro/reactnative/AnrThreadDumpParserTest.kt +38 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroAnrCacheTest.kt +81 -0
- package/android/src/test/java/com/grafana/faro/reactnative/TombstoneWireParserTest.kt +51 -0
- package/dist/cjs/config/getRNInstrumentations.js +3 -1
- package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
- package/dist/cjs/config/makeRNConfig.js +48 -9
- package/dist/cjs/config/makeRNConfig.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/initialize.js +14 -8
- package/dist/cjs/initialize.js.map +1 -1
- package/dist/cjs/instrumentations/anr/anrStack.js +19 -0
- package/dist/cjs/instrumentations/anr/anrStack.js.map +1 -0
- package/dist/cjs/instrumentations/anr/instrumentation.js +374 -72
- package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/anr/types.js.map +1 -1
- package/dist/cjs/instrumentations/console/index.js +5 -1
- package/dist/cjs/instrumentations/console/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +110 -103
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +41 -0
- package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +87 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js +123 -0
- package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/index.js +12 -0
- package/dist/cjs/instrumentations/crashReporting/android/index.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js +11 -0
- package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +181 -0
- package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/index.js +35 -2
- package/dist/cjs/instrumentations/crashReporting/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +78 -0
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js +103 -0
- package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/index.js +11 -0
- package/dist/cjs/instrumentations/crashReporting/ios/index.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js +48 -0
- package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/cjs/instrumentations/errors/index.js +2 -1
- package/dist/cjs/instrumentations/errors/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/installationId.js +91 -0
- package/dist/cjs/instrumentations/session/installationId.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionAttributes.js +152 -89
- package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/cjs/metas/appBuildIdentity.js +95 -0
- package/dist/cjs/metas/appBuildIdentity.js.map +1 -0
- package/dist/cjs/testUtils/mockDeviceInfo.js +5 -0
- package/dist/cjs/testUtils/mockDeviceInfo.js.map +1 -1
- package/dist/cjs/testUtils/setupTests.js +7 -0
- package/dist/cjs/testUtils/setupTests.js.map +1 -1
- package/dist/esm/config/getRNInstrumentations.js +3 -1
- package/dist/esm/config/getRNInstrumentations.js.map +1 -1
- package/dist/esm/config/makeRNConfig.js +49 -10
- package/dist/esm/config/makeRNConfig.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/initialize.js +11 -5
- package/dist/esm/initialize.js.map +1 -1
- package/dist/esm/instrumentations/anr/anrStack.js +16 -0
- package/dist/esm/instrumentations/anr/anrStack.js.map +1 -0
- package/dist/esm/instrumentations/anr/instrumentation.js +260 -67
- package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/anr/types.js.map +1 -1
- package/dist/esm/instrumentations/console/index.js +5 -1
- package/dist/esm/instrumentations/console/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +76 -94
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +18 -0
- package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +56 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js +116 -0
- package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/index.js +4 -0
- package/dist/esm/instrumentations/crashReporting/android/index.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js +8 -0
- package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +174 -0
- package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/index.js +35 -1
- package/dist/esm/instrumentations/crashReporting/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +56 -0
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js +98 -0
- package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/index.js +4 -0
- package/dist/esm/instrumentations/crashReporting/ios/index.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js +44 -0
- package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/esm/instrumentations/errors/index.js +2 -1
- package/dist/esm/instrumentations/errors/index.js.map +1 -1
- package/dist/esm/instrumentations/session/installationId.js +48 -0
- package/dist/esm/instrumentations/session/installationId.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionAttributes.js +83 -38
- package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/esm/metas/appBuildIdentity.js +56 -0
- package/dist/esm/metas/appBuildIdentity.js.map +1 -0
- package/dist/esm/testUtils/mockDeviceInfo.js +5 -0
- package/dist/esm/testUtils/mockDeviceInfo.js.map +1 -1
- package/dist/esm/testUtils/setupTests.js +7 -0
- package/dist/esm/testUtils/setupTests.js.map +1 -1
- package/dist/types/config/makeRNConfig.d.ts +4 -3
- package/dist/types/config/types.d.ts +2 -1
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/initialize.d.ts +4 -2
- package/dist/types/instrumentations/anr/anrStack.d.ts +7 -0
- package/dist/types/instrumentations/anr/instrumentation.d.ts +23 -28
- package/dist/types/instrumentations/anr/types.d.ts +5 -1
- package/dist/types/instrumentations/appState/index.d.ts +1 -1
- package/dist/types/instrumentations/console/index.d.ts +1 -1
- package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +56 -0
- package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +12 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +26 -0
- package/dist/types/instrumentations/crashReporting/android/crashErrorMessage.d.ts +23 -0
- package/dist/types/instrumentations/crashReporting/android/index.d.ts +4 -0
- package/dist/types/instrumentations/crashReporting/android/nativeTombstoneTrace.d.ts +2 -0
- package/dist/types/instrumentations/crashReporting/android/parseAndroidCrashTrace.d.ts +45 -0
- package/dist/types/instrumentations/crashReporting/index.d.ts +25 -2
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +28 -0
- package/dist/types/instrumentations/crashReporting/ios/crashErrorMessage.d.ts +14 -0
- package/dist/types/instrumentations/crashReporting/ios/index.d.ts +4 -0
- package/dist/types/instrumentations/crashReporting/ios/parseIosCrashTrace.d.ts +38 -0
- package/dist/types/instrumentations/crashReporting/types.d.ts +18 -51
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/http/index.d.ts +1 -1
- package/dist/types/instrumentations/performance/index.d.ts +1 -1
- package/dist/types/instrumentations/session/index.d.ts +1 -1
- package/dist/types/instrumentations/session/installationId.d.ts +1 -0
- package/dist/types/instrumentations/session/sessionAttributes.d.ts +13 -4
- package/dist/types/instrumentations/startup/index.d.ts +1 -1
- package/dist/types/instrumentations/userActions/index.d.ts +1 -1
- package/dist/types/instrumentations/view/index.d.ts +1 -1
- package/dist/types/instrumentations/xhr/index.d.ts +1 -1
- package/dist/types/metas/appBuildIdentity.d.ts +16 -0
- package/dist/types/testUtils/mockFaroReactNativeTracing.d.ts +1 -1
- package/dist/types/transports/console/transport.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +1 -1
- package/dist/types/transports/offline/transport.d.ts +1 -1
- package/ios/faro-upload-composed-source-map.sh +8 -2
- package/package.json +5 -5
- package/src/config/getRNInstrumentations.ts +5 -1
- package/src/config/makeRNConfig.test.ts +59 -3
- package/src/config/makeRNConfig.ts +48 -9
- package/src/config/types.ts +2 -1
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/index.ts +2 -1
- package/src/initialize.test.ts +71 -15
- package/src/initialize.ts +11 -5
- package/src/instrumentations/anr/anrStack.test.ts +27 -0
- package/src/instrumentations/anr/anrStack.ts +19 -0
- package/src/instrumentations/anr/instrumentation.test.ts +45 -0
- package/src/instrumentations/anr/instrumentation.ts +301 -67
- package/src/instrumentations/anr/types.ts +7 -1
- package/src/instrumentations/console/index.ts +5 -1
- package/src/instrumentations/console/instrumentation.test.ts +5 -0
- package/src/instrumentations/crashReporting/{instrumentation.ts → BaseCrashReportingInstrumentation.ts} +119 -93
- package/src/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.ts +23 -0
- package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +62 -0
- package/src/instrumentations/crashReporting/android/crashErrorMessage.test.ts +182 -0
- package/src/instrumentations/crashReporting/android/crashErrorMessage.ts +135 -0
- package/src/instrumentations/crashReporting/android/index.ts +4 -0
- package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.test.ts +19 -0
- package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.ts +8 -0
- package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.test.ts +232 -0
- package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.ts +235 -0
- package/src/instrumentations/crashReporting/index.ts +43 -2
- package/src/instrumentations/crashReporting/instrumentation.test.ts +42 -0
- package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +62 -0
- package/src/instrumentations/crashReporting/ios/crashErrorMessage.test.ts +170 -0
- package/src/instrumentations/crashReporting/ios/crashErrorMessage.ts +111 -0
- package/src/instrumentations/crashReporting/ios/index.ts +4 -0
- package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.test.ts +86 -0
- package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.ts +83 -0
- package/src/instrumentations/crashReporting/types.ts +19 -58
- package/src/instrumentations/errors/index.ts +2 -1
- package/src/instrumentations/errors/instrumentation.test.ts +8 -2
- package/src/instrumentations/errors/stackTraceParser.test.ts +1 -1
- package/src/instrumentations/session/installationId.ts +43 -0
- package/src/instrumentations/session/sessionAttributes.test.ts +260 -30
- package/src/instrumentations/session/sessionAttributes.ts +112 -22
- package/src/metas/appBuildIdentity.test.ts +48 -0
- package/src/metas/appBuildIdentity.ts +50 -0
- package/src/testUtils/mockDeviceInfo.ts +5 -0
- package/src/testUtils/setupTests.ts +7 -0
- package/dist/cjs/instrumentations/crashReporting/instrumentation.js.map +0 -1
- package/dist/esm/instrumentations/crashReporting/instrumentation.js.map +0 -1
- package/dist/types/instrumentations/crashReporting/instrumentation.d.ts +0 -58
|
@@ -7,45 +7,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { NativeModules, Platform } from 'react-native';
|
|
10
|
+
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
11
11
|
import { BaseInstrumentation, VERSION } from '@grafana/faro-core';
|
|
12
|
+
import { normalizeJavaStackTraceForRetrace, parseAndroidCrashTrace } from '../crashReporting/android';
|
|
12
13
|
import { ErrorMechanism } from '../errors/const';
|
|
14
|
+
import { isInvalidAnrCaptureStack } from './anrStack';
|
|
13
15
|
/**
|
|
14
16
|
* Default timeout for ANR detection (5 seconds, matching Android's threshold)
|
|
15
17
|
*/
|
|
16
18
|
const DEFAULT_TIMEOUT = 5000;
|
|
17
19
|
/**
|
|
18
|
-
* Default polling interval for ANR status
|
|
20
|
+
* Default polling interval for ANR status. Kept short so debug ANRs surface without
|
|
21
|
+
* waiting a full minute; native also emits onANRDetected for immediate delivery.
|
|
19
22
|
*/
|
|
20
|
-
const DEFAULT_POLLING_INTERVAL =
|
|
23
|
+
const DEFAULT_POLLING_INTERVAL = 10000;
|
|
24
|
+
/** Aligns with native FaroAnrCache.MAX_TIMESTAMP_DELTA_MS */
|
|
25
|
+
const TIMESTAMP_DEDUP_WINDOW_MS = 10000;
|
|
26
|
+
/** ApplicationExitInfo ANR traces (Android 11 / API 30+). */
|
|
27
|
+
const APP_EXIT_INFO_MIN_SDK = 30;
|
|
21
28
|
/**
|
|
22
29
|
* ANR (Application Not Responding) Detection Instrumentation.
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* On Android 11+, historical ANRs are read from ApplicationExitInfo (Sentry AnrV2
|
|
32
|
+
* style) on the next launch. ANRTracker remains a pre-API-30 fallback and a
|
|
33
|
+
* persistence backup when the OS trace stream is empty.
|
|
27
34
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Sends telemetry via Faro API:
|
|
32
|
-
* - Measurement: `anr` with `anr_count` value (for dashboards)
|
|
33
|
-
* - Error: Each ANR with `type: 'ANR'`, stack trace, duration, timestamp (Sentry-aligned)
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```typescript
|
|
37
|
-
* import { initializeFaro, ANRInstrumentation } from '@grafana/faro-react-native';
|
|
38
|
-
*
|
|
39
|
-
* initializeFaro({
|
|
40
|
-
* url: 'https://collector.example.com',
|
|
41
|
-
* instrumentations: [
|
|
42
|
-
* new ANRInstrumentation({
|
|
43
|
-
* timeout: 5000, // 5 second threshold
|
|
44
|
-
* pollingInterval: 60000, // Poll every 60 seconds
|
|
45
|
-
* }),
|
|
46
|
-
* ],
|
|
47
|
-
* });
|
|
48
|
-
* ```
|
|
35
|
+
* ANRs stay out of CrashReportingInstrumentation so they are not replayed as
|
|
36
|
+
* generic previous-session `crash` rows with empty messages.
|
|
49
37
|
*/
|
|
50
38
|
export class ANRInstrumentation extends BaseInstrumentation {
|
|
51
39
|
constructor(options = {}) {
|
|
@@ -54,13 +42,15 @@ export class ANRInstrumentation extends BaseInstrumentation {
|
|
|
54
42
|
this.name = '@grafana/faro-react-native:instrumentation-anr';
|
|
55
43
|
this.version = VERSION;
|
|
56
44
|
this.pollingIntervalId = null;
|
|
45
|
+
this.anrEventSubscription = null;
|
|
46
|
+
this.reportedAnrTimestamps = new Set();
|
|
57
47
|
this.options = {
|
|
58
48
|
timeout: (_a = options.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT,
|
|
59
49
|
pollingInterval: (_b = options.pollingInterval) !== null && _b !== void 0 ? _b : DEFAULT_POLLING_INTERVAL,
|
|
60
50
|
};
|
|
51
|
+
this.preferAppExitInfoAnr = Platform.OS === 'android' && Number(Platform.Version) >= APP_EXIT_INFO_MIN_SDK;
|
|
61
52
|
}
|
|
62
53
|
initialize() {
|
|
63
|
-
// ANR detection is only available on Android
|
|
64
54
|
if (Platform.OS !== 'android') {
|
|
65
55
|
this.logDebug('ANR detection is only available on Android');
|
|
66
56
|
return;
|
|
@@ -71,11 +61,15 @@ export class ANRInstrumentation extends BaseInstrumentation {
|
|
|
71
61
|
return;
|
|
72
62
|
}
|
|
73
63
|
this.logDebug('Initializing ANR detection instrumentation');
|
|
74
|
-
|
|
64
|
+
void this.drainAllAnrSources(nativeModule);
|
|
75
65
|
this.startNativeTracking(nativeModule);
|
|
76
|
-
|
|
66
|
+
const emitter = new NativeEventEmitter(nativeModule);
|
|
67
|
+
this.anrEventSubscription = emitter.addListener('onANRDetected', (anrJson) => {
|
|
68
|
+
void this.handleLiveAnrEvent(nativeModule, anrJson);
|
|
69
|
+
});
|
|
70
|
+
void this.checkANRStatus(nativeModule);
|
|
77
71
|
this.pollingIntervalId = setInterval(() => {
|
|
78
|
-
this.checkANRStatus(nativeModule);
|
|
72
|
+
void this.checkANRStatus(nativeModule);
|
|
79
73
|
}, this.options.pollingInterval);
|
|
80
74
|
}
|
|
81
75
|
getNativeModule() {
|
|
@@ -98,45 +92,245 @@ export class ANRInstrumentation extends BaseInstrumentation {
|
|
|
98
92
|
this.logError('Failed to start native ANR tracking', error);
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
|
-
|
|
95
|
+
drainAllAnrSources(nativeModule) {
|
|
102
96
|
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
yield this.drainHistoricalAnrs(nativeModule);
|
|
98
|
+
yield this.drainPendingANRs(nativeModule);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
drainHistoricalAnrs(nativeModule) {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
if (typeof nativeModule.getHistoricalAnrReports !== 'function') {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
103
106
|
try {
|
|
104
|
-
|
|
107
|
+
const anrList = (yield nativeModule.getHistoricalAnrReports());
|
|
108
|
+
if (!(anrList === null || anrList === void 0 ? void 0 : anrList.length)) {
|
|
105
109
|
return;
|
|
106
110
|
}
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
const ackTimestamps = [];
|
|
112
|
+
let reportedCount = 0;
|
|
113
|
+
for (const anrJson of anrList) {
|
|
114
|
+
try {
|
|
115
|
+
const anr = JSON.parse(anrJson);
|
|
116
|
+
if (this.reportANRIfNew(anr)) {
|
|
117
|
+
reportedCount += 1;
|
|
118
|
+
}
|
|
119
|
+
ackTimestamps.push(anr.timestamp);
|
|
120
|
+
yield this.acknowledgeNearbyPendingAnrs(nativeModule, anr.timestamp);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.logError('Failed to parse historical ANR report', error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (reportedCount > 0) {
|
|
110
127
|
this.api.pushMeasurement({
|
|
111
128
|
type: 'anr',
|
|
112
|
-
values: { anr_count:
|
|
129
|
+
values: { anr_count: reportedCount },
|
|
113
130
|
}, { skipDedupe: true });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
this.logDebug(`Reported ${reportedCount} historical ANR event(s) from ApplicationExitInfo`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.logError('Failed to drain historical ANRs', error);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
drainPendingANRs(nativeModule) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
const anrList = yield this.fetchPendingANRs(nativeModule);
|
|
142
|
+
if (!(anrList === null || anrList === void 0 ? void 0 : anrList.length)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
yield this.reportAndAcknowledgePendingANRs(nativeModule, anrList);
|
|
146
|
+
this.logDebug(`Processed ${anrList.length} persisted ANR event(s) from tracker cache`);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
handleLiveAnrEvent(nativeModule, anrJson) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
try {
|
|
152
|
+
const anr = JSON.parse(anrJson);
|
|
153
|
+
if (this.preferAppExitInfoAnr) {
|
|
154
|
+
// Process death + ApplicationExitInfo on next launch delivers the main-thread dump.
|
|
155
|
+
this.logDebug('Deferring live ANR to ApplicationExitInfo on next launch');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!this.shouldReportTrackerAnr(anr)) {
|
|
159
|
+
yield this.acknowledgeANRs(nativeModule, [anr.timestamp]);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (this.reportANRIfNew(anr)) {
|
|
163
|
+
this.api.pushMeasurement({
|
|
164
|
+
type: 'anr',
|
|
165
|
+
values: { anr_count: 1 },
|
|
166
|
+
}, { skipDedupe: true });
|
|
167
|
+
}
|
|
168
|
+
yield this.acknowledgeANRs(nativeModule, [anr.timestamp]);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
this.logError('Failed to parse native ANR event', error);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
fetchPendingANRs(nativeModule) {
|
|
176
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
177
|
+
try {
|
|
178
|
+
if (typeof nativeModule.getPendingANRs === 'function') {
|
|
179
|
+
return (yield nativeModule.getPendingANRs());
|
|
180
|
+
}
|
|
181
|
+
if (typeof nativeModule.getANRStatus === 'function') {
|
|
182
|
+
return (yield nativeModule.getANRStatus());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
this.logError('Failed to fetch pending ANRs', error);
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
acknowledgeANRs(nativeModule, timestamps) {
|
|
192
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
if (!timestamps.length || typeof nativeModule.acknowledgeANRs !== 'function') {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
yield nativeModule.acknowledgeANRs(timestamps);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.logError('Failed to acknowledge ANRs', error);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
acknowledgeNearbyPendingAnrs(nativeModule, exitTimestamp) {
|
|
205
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
const pending = yield this.fetchPendingANRs(nativeModule);
|
|
207
|
+
if (!(pending === null || pending === void 0 ? void 0 : pending.length)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const ackTimestamps = [];
|
|
211
|
+
for (const anrJson of pending) {
|
|
212
|
+
try {
|
|
213
|
+
const anr = JSON.parse(anrJson);
|
|
214
|
+
if (this.isNearbyTimestamp(anr.timestamp, exitTimestamp)) {
|
|
215
|
+
ackTimestamps.push(anr.timestamp);
|
|
138
216
|
}
|
|
139
|
-
|
|
217
|
+
}
|
|
218
|
+
catch (_a) {
|
|
219
|
+
// ignore malformed cache entries
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (ackTimestamps.length > 0) {
|
|
223
|
+
yield this.acknowledgeANRs(nativeModule, ackTimestamps);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
reportAndAcknowledgePendingANRs(nativeModule, anrList) {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
const ackTimestamps = [];
|
|
230
|
+
let reportedCount = 0;
|
|
231
|
+
for (const anrJson of anrList) {
|
|
232
|
+
try {
|
|
233
|
+
const anr = JSON.parse(anrJson);
|
|
234
|
+
if (this.shouldReportTrackerAnr(anr) && this.reportANRIfNew(anr)) {
|
|
235
|
+
reportedCount += 1;
|
|
236
|
+
}
|
|
237
|
+
ackTimestamps.push(anr.timestamp);
|
|
238
|
+
}
|
|
239
|
+
catch (_a) {
|
|
240
|
+
const error = new Error('ANR (Application Not Responding)');
|
|
241
|
+
error.stack = undefined;
|
|
242
|
+
this.api.pushError(error, {
|
|
243
|
+
context: {
|
|
244
|
+
mechanism: ErrorMechanism.ANR,
|
|
245
|
+
raw: anrJson,
|
|
246
|
+
},
|
|
247
|
+
fatal: true,
|
|
248
|
+
type: 'ANR',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (reportedCount > 0) {
|
|
253
|
+
this.api.pushMeasurement({
|
|
254
|
+
type: 'anr',
|
|
255
|
+
values: { anr_count: reportedCount },
|
|
256
|
+
}, { skipDedupe: true });
|
|
257
|
+
}
|
|
258
|
+
if (ackTimestamps.length > 0) {
|
|
259
|
+
yield this.acknowledgeANRs(nativeModule, ackTimestamps);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
shouldReportTrackerAnr(anr) {
|
|
264
|
+
if (this.isNearbyReportedTimestamp(anr.timestamp)) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
if (isInvalidAnrCaptureStack(anr.stacktrace)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
// On API 30+, ApplicationExitInfo is preferred on launch; tracker pending entries
|
|
271
|
+
// are reported only when historical did not already cover the same incident.
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
reportANRIfNew(anr) {
|
|
275
|
+
if (this.isNearbyReportedTimestamp(anr.timestamp)) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (isInvalidAnrCaptureStack(anr.stacktrace)) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
this.rememberReportedTimestamp(anr.timestamp);
|
|
282
|
+
this.reportANR(anr);
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
rememberReportedTimestamp(timestamp) {
|
|
286
|
+
this.reportedAnrTimestamps.add(timestamp);
|
|
287
|
+
}
|
|
288
|
+
isNearbyReportedTimestamp(timestamp) {
|
|
289
|
+
for (const reported of Array.from(this.reportedAnrTimestamps)) {
|
|
290
|
+
if (this.isNearbyTimestamp(timestamp, reported)) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
isNearbyTimestamp(a, b) {
|
|
297
|
+
if (a <= 0 || b <= 0) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return Math.abs(a - b) <= TIMESTAMP_DEDUP_WINDOW_MS;
|
|
301
|
+
}
|
|
302
|
+
reportANR(anr) {
|
|
303
|
+
var _a, _b, _c, _d;
|
|
304
|
+
const rawStacktrace = (_b = (_a = anr.stacktrace) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
305
|
+
const traceForRetrace = rawStacktrace ? normalizeJavaStackTraceForRetrace(rawStacktrace) : '';
|
|
306
|
+
const parsed = traceForRetrace ? parseAndroidCrashTrace(traceForRetrace) : null;
|
|
307
|
+
const message = ((_c = anr.description) === null || _c === void 0 ? void 0 : _c.trim()) || 'ANR (Application Not Responding)';
|
|
308
|
+
const error = new Error(message);
|
|
309
|
+
error.stack = undefined;
|
|
310
|
+
const context = {
|
|
311
|
+
duration: String(anr.duration),
|
|
312
|
+
mechanism: ErrorMechanism.ANR,
|
|
313
|
+
timestamp: String(anr.timestamp),
|
|
314
|
+
};
|
|
315
|
+
if (anr.source) {
|
|
316
|
+
context['source'] = anr.source;
|
|
317
|
+
}
|
|
318
|
+
if (traceForRetrace) {
|
|
319
|
+
context['stacktrace'] = traceForRetrace;
|
|
320
|
+
}
|
|
321
|
+
const stackFrames = (_d = parsed === null || parsed === void 0 ? void 0 : parsed.frames) !== null && _d !== void 0 ? _d : [];
|
|
322
|
+
if (!stackFrames.length && rawStacktrace) {
|
|
323
|
+
this.logWarn('ANR main-thread stack present but no Java/Kotlin frames parsed');
|
|
324
|
+
}
|
|
325
|
+
this.api.pushError(error, Object.assign({ type: 'ANR', context, fatal: true }, (stackFrames.length ? { stackFrames } : {})));
|
|
326
|
+
}
|
|
327
|
+
checkANRStatus(nativeModule) {
|
|
328
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
329
|
+
try {
|
|
330
|
+
const anrList = yield this.fetchPendingANRs(nativeModule);
|
|
331
|
+
if (anrList && anrList.length > 0) {
|
|
332
|
+
yield this.reportAndAcknowledgePendingANRs(nativeModule, anrList);
|
|
333
|
+
this.logDebug(`Polled ${anrList.length} ANR event(s) from pending cache`);
|
|
140
334
|
}
|
|
141
335
|
}
|
|
142
336
|
catch (error) {
|
|
@@ -144,16 +338,15 @@ export class ANRInstrumentation extends BaseInstrumentation {
|
|
|
144
338
|
}
|
|
145
339
|
});
|
|
146
340
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Clean up resources when instrumentation is disabled.
|
|
149
|
-
*/
|
|
150
341
|
unpatch() {
|
|
151
|
-
|
|
342
|
+
var _a;
|
|
152
343
|
if (this.pollingIntervalId !== null) {
|
|
153
344
|
clearInterval(this.pollingIntervalId);
|
|
154
345
|
this.pollingIntervalId = null;
|
|
155
346
|
}
|
|
156
|
-
|
|
347
|
+
(_a = this.anrEventSubscription) === null || _a === void 0 ? void 0 : _a.remove();
|
|
348
|
+
this.anrEventSubscription = null;
|
|
349
|
+
this.reportedAnrTimestamps.clear();
|
|
157
350
|
if (Platform.OS === 'android') {
|
|
158
351
|
const nativeModule = this.getNativeModule();
|
|
159
352
|
if (nativeModule && typeof nativeModule.stopANRTracking === 'function') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/anr/instrumentation.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD;;GAEG;AACH,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B;;GAEG;AACH,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,kBAAmB,SAAQ,mBAAmB;IAOzD,YAAY,UAAqC,EAAE;;QACjD,KAAK,EAAE,CAAC;QAPD,SAAI,GAAG,gDAAgD,CAAC;QACxD,YAAO,GAAG,OAAO,CAAC;QAGnB,sBAAiB,GAA0C,IAAI,CAAC;QAItE,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE,MAAA,OAAO,CAAC,OAAO,mCAAI,eAAe;YAC3C,eAAe,EAAE,MAAA,OAAO,CAAC,eAAe,mCAAI,wBAAwB;SACrE,CAAC;IACJ,CAAC;IAED,UAAU;QACR,6CAA6C;QAC7C,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,QAAQ,CAAC,4CAA4C,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC5C,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,4CAA4C,CAAC,CAAC;QAE5D,4BAA4B;QAC5B,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAEvC,gCAAgC;QAChC,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;YACxC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACpC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAEO,eAAe;QACrB,MAAM,EAAE,qBAAqB,EAAE,GAAG,aAAa,CAAC;QAEhD,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAEO,mBAAmB,CAAC,YAAwD;QAClF,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;gBACxD,YAAY,CAAC,gBAAgB,CAAC;oBAC5B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;iBAC9B,CAAC,CAAC;gBACH,IAAI,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAEa,cAAc,CAAC,YAAwD;;YACnF,IAAI,CAAC;gBACH,IAAI,OAAO,YAAY,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;oBACpD,OAAO;gBACT,CAAC;gBAED,MAAM,OAAO,GAAG,CAAC,MAAM,YAAY,CAAC,YAAY,EAAE,CAAoB,CAAC;gBAEvE,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClC,4DAA4D;oBAC5D,IAAI,CAAC,GAAG,CAAC,eAAe,CACtB;wBACE,IAAI,EAAE,KAAK;wBACX,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE;qBACtC,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;oBAEF,2EAA2E;oBAC3E,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;wBAC9B,IAAI,CAAC;4BACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAa,CAAC;4BAE5C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE;gCAChE,OAAO,EAAE;oCACP,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;oCAC9B,SAAS,EAAE,cAAc,CAAC,GAAG;oCAC7B,UAAU,EAAE,GAAG,CAAC,UAAU;oCAC1B,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;iCACjC;gCACD,IAAI,EAAE,KAAK;6BACZ,CAAC,CAAC;wBACL,CAAC;wBAAC,WAAM,CAAC;4BACP,0CAA0C;4BAC1C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE;gCAChE,OAAO,EAAE;oCACP,SAAS,EAAE,cAAc,CAAC,GAAG;oCAC7B,GAAG,EAAE,OAAO;iCACb;gCACD,IAAI,EAAE,KAAK;6BACZ,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBAED,IAAI,CAAC,QAAQ,CAAC,YAAY,OAAO,CAAC,MAAM,eAAe,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,QAAQ,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;KAAA;IAED;;OAEG;IACH,OAAO;QACL,eAAe;QACf,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QAED,uBAAuB;QACvB,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;gBACvE,IAAI,CAAC;oBACH,YAAY,CAAC,eAAe,EAAE,CAAC;gBACjC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,QAAQ,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;gBAC7D,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;IAC/C,CAAC;CACF","sourcesContent":["import { NativeModules, Platform } from 'react-native';\n\nimport { BaseInstrumentation, VERSION } from '@grafana/faro-core';\n\nimport { ErrorMechanism } from '../errors/const';\n\nimport type { ANREvent, ANRInstrumentationOptions } from './types';\n\n/**\n * Default timeout for ANR detection (5 seconds, matching Android's threshold)\n */\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * Default polling interval for ANR status (60 seconds, matching Flutter SDK)\n */\nconst DEFAULT_POLLING_INTERVAL = 60000;\n\n/**\n * ANR (Application Not Responding) Detection Instrumentation.\n *\n * Detects when the main/UI thread is blocked for extended periods on Android.\n * Uses a background thread that posts tasks to the main thread and monitors\n * if they complete within the timeout.\n *\n * **Note**: ANR detection is only available on Android. iOS does not have\n * the same ANR concept as Android's system watchdog.\n *\n * Sends telemetry via Faro API:\n * - Measurement: `anr` with `anr_count` value (for dashboards)\n * - Error: Each ANR with `type: 'ANR'`, stack trace, duration, timestamp (Sentry-aligned)\n *\n * @example\n * ```typescript\n * import { initializeFaro, ANRInstrumentation } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://collector.example.com',\n * instrumentations: [\n * new ANRInstrumentation({\n * timeout: 5000, // 5 second threshold\n * pollingInterval: 60000, // Poll every 60 seconds\n * }),\n * ],\n * });\n * ```\n */\nexport class ANRInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-anr';\n readonly version = VERSION;\n\n private readonly options: Required<ANRInstrumentationOptions>;\n private pollingIntervalId: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: ANRInstrumentationOptions = {}) {\n super();\n this.options = {\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n pollingInterval: options.pollingInterval ?? DEFAULT_POLLING_INTERVAL,\n };\n }\n\n initialize(): void {\n // ANR detection is only available on Android\n if (Platform.OS !== 'android') {\n this.logDebug('ANR detection is only available on Android');\n return;\n }\n\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n this.logWarn('Native module not available for ANR detection');\n return;\n }\n\n this.logDebug('Initializing ANR detection instrumentation');\n\n // Start native ANR tracking\n this.startNativeTracking(nativeModule);\n\n // Set up polling for ANR status\n this.pollingIntervalId = setInterval(() => {\n this.checkANRStatus(nativeModule);\n }, this.options.pollingInterval);\n }\n\n private getNativeModule(): typeof NativeModules.FaroReactNativeModule | null {\n const { FaroReactNativeModule } = NativeModules;\n\n if (!FaroReactNativeModule) {\n return null;\n }\n\n return FaroReactNativeModule;\n }\n\n private startNativeTracking(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n try {\n if (typeof nativeModule.startANRTracking === 'function') {\n nativeModule.startANRTracking({\n timeout: this.options.timeout,\n });\n this.logDebug('Started native ANR tracking');\n }\n } catch (error) {\n this.logError('Failed to start native ANR tracking', error);\n }\n }\n\n private async checkANRStatus(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n try {\n if (typeof nativeModule.getANRStatus !== 'function') {\n return;\n }\n\n const anrList = (await nativeModule.getANRStatus()) as string[] | null;\n\n if (anrList && anrList.length > 0) {\n // Push measurement for ANR count (matching Flutter pattern)\n this.api.pushMeasurement(\n {\n type: 'anr',\n values: { anr_count: anrList.length },\n },\n { skipDedupe: true }\n );\n\n // Push each ANR as an error with type='ANR' for filtering (Sentry-aligned)\n for (const anrJson of anrList) {\n try {\n const anr = JSON.parse(anrJson) as ANREvent;\n\n this.api.pushError(new Error('ANR (Application Not Responding)'), {\n context: {\n duration: String(anr.duration),\n mechanism: ErrorMechanism.ANR,\n stacktrace: anr.stacktrace,\n timestamp: String(anr.timestamp),\n },\n type: 'ANR',\n });\n } catch {\n // If parsing fails, still log the raw ANR\n this.api.pushError(new Error('ANR (Application Not Responding)'), {\n context: {\n mechanism: ErrorMechanism.ANR,\n raw: anrJson,\n },\n type: 'ANR',\n });\n }\n }\n\n this.logDebug(`Recorded ${anrList.length} ANR event(s)`);\n }\n } catch (error) {\n this.logError('Failed to check ANR status', error);\n }\n }\n\n /**\n * Clean up resources when instrumentation is disabled.\n */\n unpatch(): void {\n // Stop polling\n if (this.pollingIntervalId !== null) {\n clearInterval(this.pollingIntervalId);\n this.pollingIntervalId = null;\n }\n\n // Stop native tracking\n if (Platform.OS === 'android') {\n const nativeModule = this.getNativeModule();\n if (nativeModule && typeof nativeModule.stopANRTracking === 'function') {\n try {\n nativeModule.stopANRTracking();\n } catch (error) {\n this.logError('Failed to stop native ANR tracking', error);\n }\n }\n }\n\n this.logDebug('ANR instrumentation stopped');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/anr/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeEventEmitter, NativeModules, Platform } from 'react-native';\n\nimport { BaseInstrumentation, VERSION } from '@grafana/faro-core';\n\nimport { normalizeJavaStackTraceForRetrace, parseAndroidCrashTrace } from '../crashReporting/android';\nimport { ErrorMechanism } from '../errors/const';\n\nimport { isInvalidAnrCaptureStack } from './anrStack';\nimport type { ANREvent, ANRInstrumentationOptions } from './types';\n\n/**\n * Default timeout for ANR detection (5 seconds, matching Android's threshold)\n */\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * Default polling interval for ANR status. Kept short so debug ANRs surface without\n * waiting a full minute; native also emits onANRDetected for immediate delivery.\n */\nconst DEFAULT_POLLING_INTERVAL = 10000;\n\n/** Aligns with native FaroAnrCache.MAX_TIMESTAMP_DELTA_MS */\nconst TIMESTAMP_DEDUP_WINDOW_MS = 10_000;\n\n/** ApplicationExitInfo ANR traces (Android 11 / API 30+). */\nconst APP_EXIT_INFO_MIN_SDK = 30;\n\n/**\n * ANR (Application Not Responding) Detection Instrumentation.\n *\n * On Android 11+, historical ANRs are read from ApplicationExitInfo (Sentry AnrV2\n * style) on the next launch. ANRTracker remains a pre-API-30 fallback and a\n * persistence backup when the OS trace stream is empty.\n *\n * ANRs stay out of CrashReportingInstrumentation so they are not replayed as\n * generic previous-session `crash` rows with empty messages.\n */\nexport class ANRInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-anr';\n readonly version = VERSION;\n\n private readonly options: Required<ANRInstrumentationOptions>;\n private pollingIntervalId: ReturnType<typeof setInterval> | null = null;\n private anrEventSubscription: ReturnType<NativeEventEmitter['addListener']> | null = null;\n private readonly reportedAnrTimestamps = new Set<number>();\n private readonly preferAppExitInfoAnr: boolean;\n\n constructor(options: ANRInstrumentationOptions = {}) {\n super();\n this.options = {\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n pollingInterval: options.pollingInterval ?? DEFAULT_POLLING_INTERVAL,\n };\n this.preferAppExitInfoAnr = Platform.OS === 'android' && Number(Platform.Version) >= APP_EXIT_INFO_MIN_SDK;\n }\n\n initialize(): void {\n if (Platform.OS !== 'android') {\n this.logDebug('ANR detection is only available on Android');\n return;\n }\n\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n this.logWarn('Native module not available for ANR detection');\n return;\n }\n\n this.logDebug('Initializing ANR detection instrumentation');\n\n void this.drainAllAnrSources(nativeModule);\n\n this.startNativeTracking(nativeModule);\n\n const emitter = new NativeEventEmitter(nativeModule);\n this.anrEventSubscription = emitter.addListener('onANRDetected', (anrJson: string) => {\n void this.handleLiveAnrEvent(nativeModule, anrJson);\n });\n\n void this.checkANRStatus(nativeModule);\n this.pollingIntervalId = setInterval(() => {\n void this.checkANRStatus(nativeModule);\n }, this.options.pollingInterval);\n }\n\n private getNativeModule(): typeof NativeModules.FaroReactNativeModule | null {\n const { FaroReactNativeModule } = NativeModules;\n\n if (!FaroReactNativeModule) {\n return null;\n }\n\n return FaroReactNativeModule;\n }\n\n private startNativeTracking(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n try {\n if (typeof nativeModule.startANRTracking === 'function') {\n nativeModule.startANRTracking({\n timeout: this.options.timeout,\n });\n this.logDebug('Started native ANR tracking');\n }\n } catch (error) {\n this.logError('Failed to start native ANR tracking', error);\n }\n }\n\n private async drainAllAnrSources(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n await this.drainHistoricalAnrs(nativeModule);\n await this.drainPendingANRs(nativeModule);\n }\n\n private async drainHistoricalAnrs(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n if (typeof nativeModule.getHistoricalAnrReports !== 'function') {\n return;\n }\n\n try {\n const anrList = (await nativeModule.getHistoricalAnrReports()) as string[] | null;\n if (!anrList?.length) {\n return;\n }\n\n const ackTimestamps: number[] = [];\n let reportedCount = 0;\n\n for (const anrJson of anrList) {\n try {\n const anr = JSON.parse(anrJson) as ANREvent;\n if (this.reportANRIfNew(anr)) {\n reportedCount += 1;\n }\n ackTimestamps.push(anr.timestamp);\n await this.acknowledgeNearbyPendingAnrs(nativeModule, anr.timestamp);\n } catch (error) {\n this.logError('Failed to parse historical ANR report', error);\n }\n }\n\n if (reportedCount > 0) {\n this.api.pushMeasurement(\n {\n type: 'anr',\n values: { anr_count: reportedCount },\n },\n { skipDedupe: true }\n );\n this.logDebug(`Reported ${reportedCount} historical ANR event(s) from ApplicationExitInfo`);\n }\n } catch (error) {\n this.logError('Failed to drain historical ANRs', error);\n }\n }\n\n private async drainPendingANRs(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n const anrList = await this.fetchPendingANRs(nativeModule);\n if (!anrList?.length) {\n return;\n }\n\n await this.reportAndAcknowledgePendingANRs(nativeModule, anrList);\n this.logDebug(`Processed ${anrList.length} persisted ANR event(s) from tracker cache`);\n }\n\n private async handleLiveAnrEvent(\n nativeModule: typeof NativeModules.FaroReactNativeModule,\n anrJson: string\n ): Promise<void> {\n try {\n const anr = JSON.parse(anrJson) as ANREvent;\n\n if (this.preferAppExitInfoAnr) {\n // Process death + ApplicationExitInfo on next launch delivers the main-thread dump.\n this.logDebug('Deferring live ANR to ApplicationExitInfo on next launch');\n return;\n }\n\n if (!this.shouldReportTrackerAnr(anr)) {\n await this.acknowledgeANRs(nativeModule, [anr.timestamp]);\n return;\n }\n\n if (this.reportANRIfNew(anr)) {\n this.api.pushMeasurement(\n {\n type: 'anr',\n values: { anr_count: 1 },\n },\n { skipDedupe: true }\n );\n }\n await this.acknowledgeANRs(nativeModule, [anr.timestamp]);\n } catch (error) {\n this.logError('Failed to parse native ANR event', error);\n }\n }\n\n private async fetchPendingANRs(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<string[] | null> {\n try {\n if (typeof nativeModule.getPendingANRs === 'function') {\n return (await nativeModule.getPendingANRs()) as string[] | null;\n }\n\n if (typeof nativeModule.getANRStatus === 'function') {\n return (await nativeModule.getANRStatus()) as string[] | null;\n }\n } catch (error) {\n this.logError('Failed to fetch pending ANRs', error);\n }\n\n return null;\n }\n\n private async acknowledgeANRs(\n nativeModule: typeof NativeModules.FaroReactNativeModule,\n timestamps: number[]\n ): Promise<void> {\n if (!timestamps.length || typeof nativeModule.acknowledgeANRs !== 'function') {\n return;\n }\n\n try {\n await nativeModule.acknowledgeANRs(timestamps);\n } catch (error) {\n this.logError('Failed to acknowledge ANRs', error);\n }\n }\n\n private async acknowledgeNearbyPendingAnrs(\n nativeModule: typeof NativeModules.FaroReactNativeModule,\n exitTimestamp: number\n ): Promise<void> {\n const pending = await this.fetchPendingANRs(nativeModule);\n if (!pending?.length) {\n return;\n }\n\n const ackTimestamps: number[] = [];\n for (const anrJson of pending) {\n try {\n const anr = JSON.parse(anrJson) as ANREvent;\n if (this.isNearbyTimestamp(anr.timestamp, exitTimestamp)) {\n ackTimestamps.push(anr.timestamp);\n }\n } catch {\n // ignore malformed cache entries\n }\n }\n\n if (ackTimestamps.length > 0) {\n await this.acknowledgeANRs(nativeModule, ackTimestamps);\n }\n }\n\n private async reportAndAcknowledgePendingANRs(\n nativeModule: typeof NativeModules.FaroReactNativeModule,\n anrList: string[]\n ): Promise<void> {\n const ackTimestamps: number[] = [];\n let reportedCount = 0;\n\n for (const anrJson of anrList) {\n try {\n const anr = JSON.parse(anrJson) as ANREvent;\n\n if (this.shouldReportTrackerAnr(anr) && this.reportANRIfNew(anr)) {\n reportedCount += 1;\n }\n\n ackTimestamps.push(anr.timestamp);\n } catch {\n const error = new Error('ANR (Application Not Responding)');\n error.stack = undefined;\n this.api.pushError(error, {\n context: {\n mechanism: ErrorMechanism.ANR,\n raw: anrJson,\n },\n fatal: true,\n type: 'ANR',\n });\n }\n }\n\n if (reportedCount > 0) {\n this.api.pushMeasurement(\n {\n type: 'anr',\n values: { anr_count: reportedCount },\n },\n { skipDedupe: true }\n );\n }\n\n if (ackTimestamps.length > 0) {\n await this.acknowledgeANRs(nativeModule, ackTimestamps);\n }\n }\n\n private shouldReportTrackerAnr(anr: ANREvent): boolean {\n if (this.isNearbyReportedTimestamp(anr.timestamp)) {\n return false;\n }\n\n if (isInvalidAnrCaptureStack(anr.stacktrace)) {\n return false;\n }\n\n // On API 30+, ApplicationExitInfo is preferred on launch; tracker pending entries\n // are reported only when historical did not already cover the same incident.\n return true;\n }\n\n private reportANRIfNew(anr: ANREvent): boolean {\n if (this.isNearbyReportedTimestamp(anr.timestamp)) {\n return false;\n }\n\n if (isInvalidAnrCaptureStack(anr.stacktrace)) {\n return false;\n }\n\n this.rememberReportedTimestamp(anr.timestamp);\n this.reportANR(anr);\n return true;\n }\n\n private rememberReportedTimestamp(timestamp: number): void {\n this.reportedAnrTimestamps.add(timestamp);\n }\n\n private isNearbyReportedTimestamp(timestamp: number): boolean {\n for (const reported of Array.from(this.reportedAnrTimestamps)) {\n if (this.isNearbyTimestamp(timestamp, reported)) {\n return true;\n }\n }\n return false;\n }\n\n private isNearbyTimestamp(a: number, b: number): boolean {\n if (a <= 0 || b <= 0) {\n return false;\n }\n return Math.abs(a - b) <= TIMESTAMP_DEDUP_WINDOW_MS;\n }\n\n private reportANR(anr: ANREvent): void {\n const rawStacktrace = anr.stacktrace?.trim() ?? '';\n const traceForRetrace = rawStacktrace ? normalizeJavaStackTraceForRetrace(rawStacktrace) : '';\n const parsed = traceForRetrace ? parseAndroidCrashTrace(traceForRetrace) : null;\n\n const message = anr.description?.trim() || 'ANR (Application Not Responding)';\n const error = new Error(message);\n error.stack = undefined;\n\n const context: Record<string, string> = {\n duration: String(anr.duration),\n mechanism: ErrorMechanism.ANR,\n timestamp: String(anr.timestamp),\n };\n if (anr.source) {\n context['source'] = anr.source;\n }\n if (traceForRetrace) {\n context['stacktrace'] = traceForRetrace;\n }\n\n const stackFrames = parsed?.frames ?? [];\n if (!stackFrames.length && rawStacktrace) {\n this.logWarn('ANR main-thread stack present but no Java/Kotlin frames parsed');\n }\n\n this.api.pushError(error, {\n type: 'ANR',\n context,\n fatal: true,\n ...(stackFrames.length ? { stackFrames } : {}),\n });\n }\n\n private async checkANRStatus(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n try {\n const anrList = await this.fetchPendingANRs(nativeModule);\n\n if (anrList && anrList.length > 0) {\n await this.reportAndAcknowledgePendingANRs(nativeModule, anrList);\n this.logDebug(`Polled ${anrList.length} ANR event(s) from pending cache`);\n }\n } catch (error) {\n this.logError('Failed to check ANR status', error);\n }\n }\n\n unpatch(): void {\n if (this.pollingIntervalId !== null) {\n clearInterval(this.pollingIntervalId);\n this.pollingIntervalId = null;\n }\n\n this.anrEventSubscription?.remove();\n this.anrEventSubscription = null;\n this.reportedAnrTimestamps.clear();\n\n if (Platform.OS === 'android') {\n const nativeModule = this.getNativeModule();\n if (nativeModule && typeof nativeModule.stopANRTracking === 'function') {\n try {\n nativeModule.stopANRTracking();\n } catch (error) {\n this.logError('Failed to stop native ANR tracking', error);\n }\n }\n }\n\n this.logDebug('ANR instrumentation stopped');\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/anr/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration options for ANR (Application Not Responding) detection.\n */\nexport interface ANRInstrumentationOptions {\n /**\n * Timeout in milliseconds for detecting ANR.\n * If the main thread doesn't respond within this time, an ANR is recorded.\n * Default: 5000 (5 seconds, matching Android's ANR threshold)\n */\n timeout?: number;\n\n /**\n * Interval in milliseconds for polling ANR status from native.\n * Default:
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/anr/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration options for ANR (Application Not Responding) detection.\n */\nexport interface ANRInstrumentationOptions {\n /**\n * Timeout in milliseconds for detecting ANR.\n * If the main thread doesn't respond within this time, an ANR is recorded.\n * Default: 5000 (5 seconds, matching Android's ANR threshold)\n */\n timeout?: number;\n\n /**\n * Interval in milliseconds for polling ANR status from native.\n * Default: 10000 (10 seconds). Native also emits onANRDetected immediately.\n */\n pollingInterval?: number;\n}\n\n/**\n * ANR event data from native module.\n */\nexport interface ANREvent {\n /**\n * Event type (always \"ANR\")\n */\n type: string;\n\n /**\n * Timestamp when ANR was detected\n */\n timestamp: number;\n\n /**\n * Stack trace of the main thread when ANR was detected\n */\n stacktrace: string;\n\n /**\n * Duration of the ANR in milliseconds\n */\n duration: number;\n\n /** Human-readable ANR description from ApplicationExitInfo when available. */\n description?: string;\n\n /** `AppExitInfo` (preferred) or `ANRTracker` (fallback). */\n source?: string;\n}\n"]}
|
|
@@ -54,12 +54,16 @@ export class ConsoleInstrumentation extends BaseInstrumentation {
|
|
|
54
54
|
const context = { mechanism: ErrorMechanism.CONSOLE };
|
|
55
55
|
if (value && !type && !stackFrames) {
|
|
56
56
|
// Simple error without stack frames
|
|
57
|
-
this.api.pushError(new Error(ConsoleInstrumentation.consoleErrorPrefix + value), {
|
|
57
|
+
this.api.pushError(new Error(ConsoleInstrumentation.consoleErrorPrefix + value), {
|
|
58
|
+
context,
|
|
59
|
+
fatal: false,
|
|
60
|
+
});
|
|
58
61
|
}
|
|
59
62
|
else {
|
|
60
63
|
// Error with type and/or stack frames (type from error.name, matches Web SDK)
|
|
61
64
|
this.api.pushError(new Error(ConsoleInstrumentation.consoleErrorPrefix + (value !== null && value !== void 0 ? value : '')), {
|
|
62
65
|
context,
|
|
66
|
+
fatal: false,
|
|
63
67
|
type,
|
|
64
68
|
stackFrames,
|
|
65
69
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/console/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGtH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,8BAA8B,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAEvF;;;;;;;;;GASG;AACH,MAAM,OAAO,sBAAuB,SAAQ,mBAAmB;IAA/D;;QACW,SAAI,GAAG,oDAAoD,CAAC;QAC5D,YAAO,GAAG,OAAO,CAAC;QAKnB,oBAAe,GAAqB,EAAE,CAAC;QACvC,oBAAe,GAAsB,4BAA4B,CAAC;QAClE,kBAAa,GAAe,EAAE,CAAC;QAC/B,iBAAY,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/console/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { allLogLevels, BaseInstrumentation, defaultErrorArgsSerializer, LogLevel, VERSION } from '@grafana/faro-core';\nimport type { LogArgsSerializer } from '@grafana/faro-core';\n\nimport { ErrorMechanism } from '../errors/const';\n\nimport { getDetailsFromConsoleErrorArgs, reactNativeLogArgsSerializer } from './utils';\n\n/**\n * Console instrumentation for React Native\n * Captures console logs and errors\n *\n * Features:\n * - Configurable log levels\n * - Advanced error serialization\n * - Option to treat console.error as log or error\n * - Unpatch support for cleanup\n */\nexport class ConsoleInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-console';\n readonly version = VERSION;\n\n static defaultDisabledLevels: LogLevel[] = [LogLevel.DEBUG, LogLevel.TRACE, LogLevel.LOG];\n static consoleErrorPrefix = 'console.error: ';\n\n private originalConsole: Partial<Console> = {};\n private errorSerializer: LogArgsSerializer = reactNativeLogArgsSerializer;\n private patchedLevels: LogLevel[] = [];\n private isProcessing = false;\n\n initialize(): void {\n const instrumentationOptions = this.config.consoleInstrumentation;\n\n // Configure error serialization\n const serializeErrors = instrumentationOptions?.serializeErrors || !!instrumentationOptions?.errorSerializer;\n this.errorSerializer = serializeErrors\n ? (instrumentationOptions?.errorSerializer ?? defaultErrorArgsSerializer)\n : reactNativeLogArgsSerializer;\n\n // Store original console methods - use unpatchedConsole from config if available\n // to avoid capturing React Native's patched console (LogBox, DevTools)\n const sourceConsole = this.config.unpatchedConsole ?? console;\n allLogLevels.forEach((level) => {\n this.originalConsole[level] = sourceConsole[level];\n });\n\n // Determine which levels to patch\n this.patchedLevels = allLogLevels.filter(\n (level) =>\n !(instrumentationOptions?.disabledLevels ?? ConsoleInstrumentation.defaultDisabledLevels).includes(level)\n );\n\n // Patch console methods\n this.patchedLevels.forEach((level) => {\n console[level] = (...args: unknown[]) => {\n // Prevent re-entry to avoid infinite loops\n if (this.isProcessing) {\n this.originalConsole[level]?.(...args);\n return;\n }\n\n this.isProcessing = true;\n try {\n if (level === LogLevel.ERROR && !instrumentationOptions?.consoleErrorAsLog) {\n // Handle console.error as an error with advanced serialization\n const { value, type, stackFrames } = getDetailsFromConsoleErrorArgs(args, this.errorSerializer);\n\n const context = { mechanism: ErrorMechanism.CONSOLE };\n\n if (value && !type && !stackFrames) {\n // Simple error without stack frames\n this.api.pushError(new Error(ConsoleInstrumentation.consoleErrorPrefix + value), {\n context,\n fatal: false,\n });\n } else {\n // Error with type and/or stack frames (type from error.name, matches Web SDK)\n this.api.pushError(new Error(ConsoleInstrumentation.consoleErrorPrefix + (value ?? '')), {\n context,\n fatal: false,\n type,\n stackFrames,\n });\n }\n } else if (level === LogLevel.ERROR && instrumentationOptions?.consoleErrorAsLog) {\n // Handle console.error as a log with error details in context\n const { value, type, stackFrames } = getDetailsFromConsoleErrorArgs(args, this.errorSerializer);\n\n this.api.pushLog(value ? [ConsoleInstrumentation.consoleErrorPrefix + value] : args, {\n level,\n context: {\n value: value ?? '',\n type: type ?? '',\n stackFrames: stackFrames?.length ? defaultErrorArgsSerializer(stackFrames) : '',\n },\n });\n } else {\n // Handle other log levels normally\n this.api.pushLog(args, { level });\n }\n } catch (_err) {\n // Silently ignore errors to prevent infinite loops during bootstrap\n } finally {\n // Always call original console method (still protected by isProcessing flag)\n this.originalConsole[level]?.(...args);\n this.isProcessing = false;\n }\n };\n });\n }\n\n /**\n * Restore original console methods\n * Call this to clean up and unpatch the console\n */\n unpatch(): void {\n this.patchedLevels.forEach((level) => {\n if (this.originalConsole[level]) {\n console[level] = this.originalConsole[level];\n }\n });\n\n this.patchedLevels = [];\n }\n}\n"]}
|