@grafana/faro-react-native 1.3.0 → 1.4.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/README.md +176 -30
- package/android/build.gradle +20 -15
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashIds.kt +44 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +134 -38
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashSessionStore.kt +433 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +11 -2
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +128 -17
- package/android/src/main/java/com/grafana/faro/reactnative/FaroSessionProcess.kt +88 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FrameMonitor.kt +34 -42
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterBelowApi30Test.kt +21 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterTest.kt +59 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashSessionStoreTest.kt +268 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashTraceCacheTest.kt +52 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroSessionProcessTest.kt +127 -0
- package/dist/cjs/config/getRNInstrumentations.js +6 -8
- package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
- package/dist/cjs/config/sampling.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/cjs/dataCollection/DataCollectionPolicy.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 +8 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js +10 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/appState/index.js +8 -2
- package/dist/cjs/instrumentations/appState/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +19 -6
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +331 -0
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/cjs/instrumentations/errors/index.js +8 -3
- package/dist/cjs/instrumentations/errors/index.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js +20 -31
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/cjs/instrumentations/performance/index.js +2 -6
- package/dist/cjs/instrumentations/performance/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js +48 -0
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js +37 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/index.js +214 -45
- package/dist/cjs/instrumentations/session/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionActivity.js +43 -0
- package/dist/cjs/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionAttributes.js +8 -8
- package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +199 -24
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -13
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js +72 -0
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js +124 -96
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionProcess.js +96 -0
- package/dist/cjs/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/cjs/instrumentations/startup/index.js +2 -6
- package/dist/cjs/instrumentations/startup/index.js.map +1 -1
- package/dist/cjs/navigation/utils.js +3 -1
- package/dist/cjs/navigation/utils.js.map +1 -1
- package/dist/cjs/transports/fetch/index.js.map +1 -1
- package/dist/cjs/transports/fetch/transport.js +56 -25
- package/dist/cjs/transports/fetch/transport.js.map +1 -1
- package/dist/cjs/transports/fetch/types.js.map +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/cjs/transports/offline/OfflineCache.js +2 -2
- package/dist/cjs/transports/offline/OfflineCache.js.map +1 -1
- package/dist/cjs/transports/offline/transport.js +3 -4
- package/dist/cjs/transports/offline/transport.js.map +1 -1
- package/dist/cjs/userPersistence/UserPersistence.js +1 -2
- package/dist/cjs/userPersistence/UserPersistence.js.map +1 -1
- package/dist/esm/config/getRNInstrumentations.js +6 -8
- package/dist/esm/config/getRNInstrumentations.js.map +1 -1
- package/dist/esm/config/sampling.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/esm/dataCollection/DataCollectionPolicy.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 +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/instrumentations/anr/instrumentation.js +6 -1
- package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/appState/index.js +8 -2
- package/dist/esm/instrumentations/appState/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +15 -4
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +230 -0
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/esm/instrumentations/errors/index.js +8 -3
- package/dist/esm/instrumentations/errors/index.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js +17 -32
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/esm/instrumentations/performance/index.js +2 -6
- package/dist/esm/instrumentations/performance/index.js.map +1 -1
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js +34 -0
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js +32 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/index.js +207 -46
- package/dist/esm/instrumentations/session/index.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionActivity.js +38 -0
- package/dist/esm/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionAttributes.js +7 -7
- package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +190 -27
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -14
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js +56 -0
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js +120 -44
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionProcess.js +89 -0
- package/dist/esm/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/esm/instrumentations/startup/index.js +2 -6
- package/dist/esm/instrumentations/startup/index.js.map +1 -1
- package/dist/esm/navigation/utils.js +2 -1
- package/dist/esm/navigation/utils.js.map +1 -1
- package/dist/esm/transports/fetch/index.js.map +1 -1
- package/dist/esm/transports/fetch/transport.js +38 -14
- package/dist/esm/transports/fetch/transport.js.map +1 -1
- package/dist/esm/transports/fetch/types.js.map +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/esm/transports/offline/OfflineCache.js +2 -2
- package/dist/esm/transports/offline/OfflineCache.js.map +1 -1
- package/dist/esm/transports/offline/transport.js +3 -4
- package/dist/esm/transports/offline/transport.js.map +1 -1
- package/dist/esm/userPersistence/UserPersistence.js +1 -2
- package/dist/esm/userPersistence/UserPersistence.js.map +1 -1
- package/dist/types/config/getRNInstrumentations.d.ts +1 -2
- package/dist/types/config/sampling.d.ts +0 -3
- package/dist/types/config/types.d.ts +5 -3
- package/dist/types/dataCollection/DataCollectionPolicy.d.ts +1 -2
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/instrumentations/anr/instrumentation.d.ts +1 -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 +14 -4
- package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +1 -1
- package/dist/types/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.d.ts +27 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +3 -3
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +3 -3
- package/dist/types/instrumentations/crashReporting/types.d.ts +6 -0
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +3 -3
- package/dist/types/instrumentations/frameMonitoring/types.d.ts +4 -6
- package/dist/types/instrumentations/http/index.d.ts +1 -1
- package/dist/types/instrumentations/performance/index.d.ts +1 -3
- package/dist/types/instrumentations/session/FaroSessionActivityBoundary.d.ts +8 -0
- package/dist/types/instrumentations/session/directSessionActivity.d.ts +13 -0
- package/dist/types/instrumentations/session/index.d.ts +21 -2
- package/dist/types/instrumentations/session/sessionActivity.d.ts +9 -0
- package/dist/types/instrumentations/session/sessionAttributes.d.ts +6 -3
- package/dist/types/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.d.ts +16 -3
- package/dist/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +9 -3
- package/dist/types/instrumentations/session/sessionManager/persistentSessionRecord.d.ts +4 -0
- package/dist/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +19 -7
- package/dist/types/instrumentations/session/sessionManager/types.d.ts +6 -0
- package/dist/types/instrumentations/session/sessionProcess.d.ts +17 -0
- package/dist/types/instrumentations/startup/index.d.ts +3 -7
- 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/navigation/utils.d.ts +1 -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/index.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +9 -2
- package/dist/types/transports/fetch/types.d.ts +5 -0
- package/dist/types/transports/offline/ConnectivityService.d.ts +1 -1
- package/dist/types/transports/offline/OfflineCache.d.ts +2 -2
- package/dist/types/transports/offline/transport.d.ts +4 -5
- package/dist/types/userPersistence/UserPersistence.d.ts +1 -2
- package/ios/FaroCrashReporter.swift +214 -21
- package/ios/FaroReactNative.swift +24 -0
- package/ios/FaroReactNativeModule.h +2 -1
- package/ios/FaroReactNativeModule.mm +121 -6
- package/ios/RefreshRateVitals.swift +34 -38
- package/package.json +8 -6
- package/src/config/getRNInstrumentations.ts +6 -8
- package/src/config/makeRNConfig.test.ts +6 -0
- package/src/config/sampling.ts +0 -3
- package/src/config/types.ts +5 -3
- package/src/dataCollection/DataCollectionPolicy.ts +1 -2
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/index.ts +8 -2
- package/src/instrumentations/anr/instrumentation.ts +8 -1
- package/src/instrumentations/appState/index.ts +10 -2
- package/src/instrumentations/crashReporting/BaseCrashReportingInstrumentation.ts +28 -8
- package/src/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.ts +277 -0
- package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/instrumentation.test.ts +433 -5
- package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/types.ts +7 -0
- package/src/instrumentations/errors/index.ts +12 -4
- package/src/instrumentations/errors/instrumentation.test.ts +35 -0
- package/src/instrumentations/frameMonitoring/instrumentation.test.ts +155 -0
- package/src/instrumentations/frameMonitoring/instrumentation.ts +19 -38
- package/src/instrumentations/frameMonitoring/types.ts +4 -6
- package/src/instrumentations/performance/index.ts +2 -6
- package/src/instrumentations/session/FaroSessionActivityBoundary.test.ts +60 -0
- package/src/instrumentations/session/FaroSessionActivityBoundary.tsx +41 -0
- package/src/instrumentations/session/beforeSendHook.test.ts +246 -0
- package/src/instrumentations/session/directSessionActivity.test.ts +158 -0
- package/src/instrumentations/session/directSessionActivity.ts +36 -0
- package/src/instrumentations/session/index.ts +269 -46
- package/src/instrumentations/session/persistentColdStart.test.ts +196 -0
- package/src/instrumentations/session/sessionActivity.test.ts +68 -0
- package/src/instrumentations/session/sessionActivity.ts +58 -0
- package/src/instrumentations/session/sessionAttributes.test.ts +37 -2
- package/src/instrumentations/session/sessionAttributes.ts +12 -6
- package/src/instrumentations/session/sessionCleanup.test.ts +410 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.test.ts +552 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts +249 -33
- package/src/instrumentations/session/sessionManager/VolatileSessionManager.ts +38 -18
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.test.ts +103 -0
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.ts +78 -0
- package/src/instrumentations/session/sessionManager/sampling.test.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionConstants.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.test.ts +256 -18
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.ts +180 -39
- package/src/instrumentations/session/sessionManager/types.ts +8 -0
- package/src/instrumentations/session/sessionManagerInstance.test.ts +65 -0
- package/src/instrumentations/session/sessionProcess.test.ts +136 -0
- package/src/instrumentations/session/sessionProcess.ts +114 -0
- package/src/instrumentations/session/startNewSession.test.ts +337 -0
- package/src/instrumentations/startup/index.ts +2 -6
- package/src/navigation/utils.ts +3 -1
- package/src/transports/fetch/index.ts +6 -1
- package/src/transports/fetch/transport.test.ts +86 -2
- package/src/transports/fetch/transport.ts +38 -16
- package/src/transports/fetch/types.ts +7 -0
- package/src/transports/offline/ConnectivityService.ts +1 -1
- package/src/transports/offline/OfflineCache.ts +2 -2
- package/src/transports/offline/transport.ts +3 -4
- package/src/userPersistence/UserPersistence.ts +1 -2
- package/dist/cjs/utils/throttle.js +0 -42
- package/dist/cjs/utils/throttle.js.map +0 -1
- package/dist/esm/utils/throttle.js +0 -34
- package/dist/esm/utils/throttle.js.map +0 -1
- package/dist/types/utils/throttle.d.ts +0 -9
- package/src/utils/throttle.test.ts +0 -141
- package/src/utils/throttle.ts +0 -34
|
@@ -2,10 +2,81 @@
|
|
|
2
2
|
#import <React/RCTBridgeModule.h>
|
|
3
3
|
#import "FaroReactNative-Swift.h"
|
|
4
4
|
|
|
5
|
+
static NSLock *FaroSessionPersistenceLock(void)
|
|
6
|
+
{
|
|
7
|
+
static NSLock *lock;
|
|
8
|
+
static dispatch_once_t onceToken;
|
|
9
|
+
dispatch_once(&onceToken, ^{
|
|
10
|
+
lock = [[NSLock alloc] init];
|
|
11
|
+
});
|
|
12
|
+
return lock;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Weak ownership also clears the claim if teardown skips `invalidate`.
|
|
16
|
+
static __weak FaroReactNativeModule *FaroSessionPersistenceOwner;
|
|
17
|
+
|
|
18
|
+
@interface FaroReactNativeModule ()
|
|
19
|
+
- (BOOL)claimSessionPersistenceOwnership;
|
|
20
|
+
- (BOOL)releaseSessionPersistenceOwnership;
|
|
21
|
+
@end
|
|
22
|
+
|
|
5
23
|
@implementation FaroReactNativeModule
|
|
6
24
|
|
|
7
25
|
RCT_EXPORT_MODULE(FaroReactNativeModule)
|
|
8
26
|
|
|
27
|
+
/// Stable process identity used to isolate persisted session chains.
|
|
28
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getSessionProcessIdentifier)
|
|
29
|
+
{
|
|
30
|
+
return [FaroReactNative getSessionProcessIdentifier];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Whether this runtime belongs to the host application's main process.
|
|
34
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isMainSessionProcess)
|
|
35
|
+
{
|
|
36
|
+
return @([FaroReactNative isMainSessionProcess]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Allows only one React Native runtime in a process to persist sessions.
|
|
40
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(claimSessionPersistence)
|
|
41
|
+
{
|
|
42
|
+
return @([self claimSessionPersistenceOwnership]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Releases persistence only when this native module instance owns it.
|
|
46
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(releaseSessionPersistence)
|
|
47
|
+
{
|
|
48
|
+
return @([self releaseSessionPersistenceOwnership]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (BOOL)claimSessionPersistenceOwnership
|
|
52
|
+
{
|
|
53
|
+
NSLock *lock = FaroSessionPersistenceLock();
|
|
54
|
+
[lock lock];
|
|
55
|
+
BOOL claimed = FaroSessionPersistenceOwner == nil || FaroSessionPersistenceOwner == self;
|
|
56
|
+
if (claimed) {
|
|
57
|
+
FaroSessionPersistenceOwner = self;
|
|
58
|
+
}
|
|
59
|
+
[lock unlock];
|
|
60
|
+
return claimed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (BOOL)releaseSessionPersistenceOwnership
|
|
64
|
+
{
|
|
65
|
+
NSLock *lock = FaroSessionPersistenceLock();
|
|
66
|
+
[lock lock];
|
|
67
|
+
BOOL released = FaroSessionPersistenceOwner == self;
|
|
68
|
+
if (released) {
|
|
69
|
+
FaroSessionPersistenceOwner = nil;
|
|
70
|
+
}
|
|
71
|
+
[lock unlock];
|
|
72
|
+
return released;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (void)invalidate
|
|
76
|
+
{
|
|
77
|
+
[self releaseSessionPersistenceOwnership];
|
|
78
|
+
}
|
|
79
|
+
|
|
9
80
|
/// Synchronous method for immediate access from JavaScript
|
|
10
81
|
/// Returns app startup duration in milliseconds from process start to current time
|
|
11
82
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getAppStartDuration)
|
|
@@ -34,9 +105,9 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCpuUsage)
|
|
|
34
105
|
/// @param config Dictionary with targetFps, frozenFrameThresholdMs, normalizedRefreshRate
|
|
35
106
|
RCT_EXPORT_METHOD(startFrameMonitoring:(NSDictionary *)config)
|
|
36
107
|
{
|
|
37
|
-
double targetFps = [[config objectForKey:@"targetFps"] doubleValue] ?:
|
|
38
|
-
double frozenFrameThresholdMs = [[config objectForKey:@"frozenFrameThresholdMs"] doubleValue] ?:
|
|
39
|
-
double normalizedRefreshRate = [[config objectForKey:@"normalizedRefreshRate"] doubleValue] ?:
|
|
108
|
+
double targetFps = [[config objectForKey:@"targetFps"] doubleValue] ?: RefreshRateVitals.defaultTargetFps;
|
|
109
|
+
double frozenFrameThresholdMs = [[config objectForKey:@"frozenFrameThresholdMs"] doubleValue] ?: RefreshRateVitals.defaultFrozenFrameThresholdMs;
|
|
110
|
+
double normalizedRefreshRate = [[config objectForKey:@"normalizedRefreshRate"] doubleValue] ?: RefreshRateVitals.defaultNormalizedRefreshRate;
|
|
40
111
|
|
|
41
112
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
42
113
|
[[RefreshRateVitals shared] configureWithTargetFps:targetFps
|
|
@@ -79,12 +150,13 @@ RCT_EXPORT_METHOD(getFrameMetrics:(RCTPromiseResolveBlock)resolve
|
|
|
79
150
|
{
|
|
80
151
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
81
152
|
RefreshRateVitals *vitals = [RefreshRateVitals shared];
|
|
82
|
-
|
|
153
|
+
NSDictionary *frozenMetrics = [vitals getAndResetFrozenMetrics];
|
|
154
|
+
|
|
83
155
|
NSDictionary *metrics = @{
|
|
84
156
|
@"refreshRate": @([vitals getRefreshRate]),
|
|
85
157
|
@"slowFrames": @([vitals getAndResetSlowFrames]),
|
|
86
|
-
@"frozenFrames": @
|
|
87
|
-
@"frozenDurationMs": @
|
|
158
|
+
@"frozenFrames": frozenMetrics[@"count"],
|
|
159
|
+
@"frozenDurationMs": frozenMetrics[@"durationMs"]
|
|
88
160
|
};
|
|
89
161
|
|
|
90
162
|
resolve(metrics);
|
|
@@ -121,4 +193,47 @@ RCT_EXPORT_METHOD(getCrashReport:(RCTPromiseResolveBlock)resolve
|
|
|
121
193
|
}
|
|
122
194
|
}
|
|
123
195
|
|
|
196
|
+
/// Store the active session synchronously so it is available if the process crashes.
|
|
197
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(recordCrashSessionContext:(NSDictionary *)sessionContext)
|
|
198
|
+
{
|
|
199
|
+
return @([FaroReactNative recordCrashSessionContext:sessionContext]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Get pending crash reports without deleting them.
|
|
203
|
+
RCT_EXPORT_METHOD(getPendingCrashReports:(RCTPromiseResolveBlock)resolve
|
|
204
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
205
|
+
{
|
|
206
|
+
NSArray<NSString *> *crashReports = [FaroReactNative getPendingCrashReports];
|
|
207
|
+
if (crashReports != nil && crashReports.count > 0) {
|
|
208
|
+
resolve(crashReports);
|
|
209
|
+
} else {
|
|
210
|
+
resolve([NSNull null]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Delete only crash reports that JavaScript has finished handling.
|
|
215
|
+
RCT_EXPORT_METHOD(acknowledgeCrashReports:(NSArray *)reportIds
|
|
216
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
217
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
218
|
+
{
|
|
219
|
+
NSMutableArray<NSString *> *sanitizedReportIds = [NSMutableArray array];
|
|
220
|
+
for (id reportId in reportIds) {
|
|
221
|
+
if (![reportId isKindOfClass:[NSString class]]) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
NSString *trimmedReportId = [(NSString *)reportId stringByTrimmingCharactersInSet:
|
|
226
|
+
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
227
|
+
if (trimmedReportId.length > 0) {
|
|
228
|
+
[sanitizedReportIds addObject:trimmedReportId];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if ([FaroReactNative acknowledgeCrashReports:sanitizedReportIds]) {
|
|
233
|
+
resolve([NSNull null]);
|
|
234
|
+
} else {
|
|
235
|
+
reject(@"E_CRASH_ACK_FAILED", @"Failed to acknowledge recovered crash report", nil);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
124
239
|
@end
|
|
@@ -9,18 +9,22 @@ import UIKit
|
|
|
9
9
|
/// Uses CADisplayLink to measure actual frame durations and calculate FPS.
|
|
10
10
|
/// Handles ProMotion displays (120Hz) by normalizing to 60 FPS baseline.
|
|
11
11
|
@objc public class RefreshRateVitals: NSObject {
|
|
12
|
+
@objc public static let defaultTargetFps: Double = 60.0
|
|
13
|
+
@objc public static let defaultFrozenFrameThresholdMs: Double = 700.0
|
|
14
|
+
@objc public static let defaultNormalizedRefreshRate: Double = 60.0
|
|
15
|
+
|
|
12
16
|
private var displayLink: CADisplayLink?
|
|
13
17
|
private var lastFrameTimestamp: CFTimeInterval?
|
|
14
18
|
private var nextFrameDuration: CFTimeInterval?
|
|
15
19
|
|
|
16
20
|
/// Normalized refresh rate for backend compatibility (default 60 FPS)
|
|
17
|
-
private var normalizedRefreshRate: Double =
|
|
21
|
+
private var normalizedRefreshRate: Double = RefreshRateVitals.defaultNormalizedRefreshRate
|
|
18
22
|
|
|
19
23
|
/// Target FPS for slow frame detection
|
|
20
|
-
private var targetFps: Double =
|
|
24
|
+
private var targetFps: Double = RefreshRateVitals.defaultTargetFps
|
|
21
25
|
|
|
22
26
|
/// Threshold in seconds for frozen frame detection
|
|
23
|
-
private var frozenFrameThreshold: Double = 0
|
|
27
|
+
private var frozenFrameThreshold: Double = RefreshRateVitals.defaultFrozenFrameThresholdMs / 1000.0
|
|
24
28
|
|
|
25
29
|
/// Last calculated refresh rate
|
|
26
30
|
@objc public private(set) var lastRefreshRate: Double = 0.0
|
|
@@ -28,11 +32,11 @@ import UIKit
|
|
|
28
32
|
/// Count of slow frame events since last reset (not individual frames)
|
|
29
33
|
@objc public private(set) var slowFrameEventCount: Int = 0
|
|
30
34
|
|
|
31
|
-
///
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
/// Lock for frozen frame count and duration (writer and poll snapshot).
|
|
36
|
+
private let frozenMetricsLock = NSLock()
|
|
37
|
+
|
|
38
|
+
private var frozenFrameCount: Int = 0
|
|
39
|
+
private var frozenFrameDurationMs: Double = 0
|
|
36
40
|
|
|
37
41
|
/// Whether monitoring is active
|
|
38
42
|
@objc public private(set) var isMonitoring: Bool = false
|
|
@@ -60,7 +64,7 @@ import UIKit
|
|
|
60
64
|
/// Configure monitoring parameters
|
|
61
65
|
/// - Parameters:
|
|
62
66
|
/// - targetFps: Target FPS for slow frame detection (default 60)
|
|
63
|
-
/// - frozenFrameThresholdMs: Threshold in ms for frozen frames (default
|
|
67
|
+
/// - frozenFrameThresholdMs: Threshold in ms for frozen frames (default 700)
|
|
64
68
|
/// - normalizedRefreshRate: Normalized rate for ProMotion displays (default 60)
|
|
65
69
|
@objc public func configure(
|
|
66
70
|
targetFps: Double,
|
|
@@ -82,8 +86,10 @@ import UIKit
|
|
|
82
86
|
|
|
83
87
|
// Reset counters
|
|
84
88
|
slowFrameEventCount = 0
|
|
89
|
+
frozenMetricsLock.lock()
|
|
85
90
|
frozenFrameCount = 0
|
|
86
91
|
frozenFrameDurationMs = 0
|
|
92
|
+
frozenMetricsLock.unlock()
|
|
87
93
|
lastFrameTimestamp = nil
|
|
88
94
|
nextFrameDuration = nil
|
|
89
95
|
inSlowFrameEvent = false
|
|
@@ -111,18 +117,25 @@ import UIKit
|
|
|
111
117
|
return count
|
|
112
118
|
}
|
|
113
119
|
|
|
114
|
-
/// Get and reset frozen frame count
|
|
115
|
-
@objc public func
|
|
120
|
+
/// Get and reset frozen frame count and duration atomically.
|
|
121
|
+
@objc public func getAndResetFrozenMetrics() -> [String: NSNumber] {
|
|
122
|
+
frozenMetricsLock.lock()
|
|
116
123
|
let count = frozenFrameCount
|
|
117
|
-
frozenFrameCount = 0
|
|
118
|
-
return count
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/// Get and reset frozen frame duration in milliseconds
|
|
122
|
-
@objc public func getAndResetFrozenDuration() -> Double {
|
|
123
124
|
let duration = frozenFrameDurationMs
|
|
125
|
+
frozenFrameCount = 0
|
|
124
126
|
frozenFrameDurationMs = 0
|
|
125
|
-
|
|
127
|
+
frozenMetricsLock.unlock()
|
|
128
|
+
return [
|
|
129
|
+
"count": NSNumber(value: count),
|
|
130
|
+
"durationMs": NSNumber(value: duration),
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private func recordFrozenFrame(durationMs: Double) {
|
|
135
|
+
frozenMetricsLock.lock()
|
|
136
|
+
frozenFrameCount += 1
|
|
137
|
+
frozenFrameDurationMs += durationMs
|
|
138
|
+
frozenMetricsLock.unlock()
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
// MARK: - Private Methods
|
|
@@ -132,13 +145,7 @@ import UIKit
|
|
|
132
145
|
if let lastTimestamp = lastFrameTimestamp {
|
|
133
146
|
let frameDuration = link.timestamp - lastTimestamp
|
|
134
147
|
if frameDuration > frozenFrameThreshold {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Track duration in milliseconds
|
|
138
|
-
let durationMs = frameDuration * 1000
|
|
139
|
-
frozenFrameDurationMs += durationMs
|
|
140
|
-
|
|
141
|
-
NSLog("[Faro] Frozen frame detected! Duration: %.0fms (threshold: %.0fms)", durationMs, frozenFrameThreshold * 1000)
|
|
148
|
+
recordFrozenFrame(durationMs: frameDuration * 1000)
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
|
|
@@ -150,12 +157,7 @@ import UIKit
|
|
|
150
157
|
// A slow frame "event" is a period of consecutive frames below target FPS
|
|
151
158
|
// This groups consecutive slow frames to report meaningful jank, not microsecond variations
|
|
152
159
|
let isSlow = fps < targetFps
|
|
153
|
-
|
|
154
|
-
// 🔍 TEMP DEBUG LOG - Remove after analysis
|
|
155
|
-
if isSlow {
|
|
156
|
-
NSLog("[Faro DEBUG IOS] 🐌 Slow frame detected: %.1f FPS (target: %.1f)", fps, targetFps)
|
|
157
|
-
}
|
|
158
|
-
|
|
160
|
+
|
|
159
161
|
if isSlow {
|
|
160
162
|
if !inSlowFrameEvent {
|
|
161
163
|
// Start new slow frame event
|
|
@@ -166,17 +168,11 @@ import UIKit
|
|
|
166
168
|
// Frame is good - check if we should end the current slow frame event
|
|
167
169
|
if inSlowFrameEvent {
|
|
168
170
|
let eventDuration = link.timestamp - slowFrameEventStartTime
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
// Only count as a slow frame event if it lasted long enough to be user-perceptible
|
|
172
173
|
// This filters out single-frame dips that don't affect user experience
|
|
173
174
|
if eventDuration >= slowFrameEventMinDuration {
|
|
174
175
|
slowFrameEventCount += 1
|
|
175
|
-
// 🔍 TEMP DEBUG LOG - Remove after analysis
|
|
176
|
-
NSLog("[Faro DEBUG IOS] ✅ COUNTED as event (%.0fms)! Total events now: %d", eventDurationMs, slowFrameEventCount)
|
|
177
|
-
} else {
|
|
178
|
-
// 🔍 TEMP DEBUG LOG - Remove after analysis
|
|
179
|
-
NSLog("[Faro DEBUG IOS] ❌ NOT counted (%.0fms is too short). Total events still: %d", eventDurationMs, slowFrameEventCount)
|
|
180
176
|
}
|
|
181
177
|
|
|
182
178
|
inSlowFrameEvent = false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/faro-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Faro instrumentations, metas, transports for React Native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"observability",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"version": "node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@grafana/faro-core": "^2.
|
|
59
|
+
"@grafana/faro-core": "^2.8.2",
|
|
60
60
|
"@react-native-async-storage/async-storage": "^1.21.0",
|
|
61
61
|
"react-native-device-info": "^11.1.0"
|
|
62
62
|
},
|
|
@@ -76,13 +76,15 @@
|
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@types/node": "24.12.4",
|
|
79
|
-
"@types/react": "19.2.
|
|
79
|
+
"@types/react": "19.2.18",
|
|
80
80
|
"@types/react-native": "0.73.0",
|
|
81
|
-
"react-
|
|
82
|
-
"react-native-
|
|
81
|
+
"@types/react-test-renderer": "19.1.0",
|
|
82
|
+
"react-native-mmkv": "4.3.2",
|
|
83
|
+
"react-native-nitro-modules": "0.36.1",
|
|
84
|
+
"react-test-renderer": "19.2.3"
|
|
83
85
|
},
|
|
84
86
|
"publishConfig": {
|
|
85
87
|
"access": "public"
|
|
86
88
|
},
|
|
87
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "38d87a3474585e1177c38d2e9acd04860a4236e6"
|
|
88
90
|
}
|
|
@@ -26,8 +26,7 @@ function toRegExpArray(patterns: Patterns): RegExp[] {
|
|
|
26
26
|
/**
|
|
27
27
|
* Returns the default set of instrumentations for React Native.
|
|
28
28
|
*
|
|
29
|
-
* Reads all options from the Faro config (single source of truth)
|
|
30
|
-
* Property names are aligned with Flutter SDK FaroConfig:
|
|
29
|
+
* Reads all options from the Faro config (single source of truth):
|
|
31
30
|
* - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals
|
|
32
31
|
* - enableCrashReporting, enableErrorReporting
|
|
33
32
|
* - fetchVitalsInterval, ignoreUrls
|
|
@@ -47,7 +46,6 @@ function toRegExpArray(patterns: Patterns): RegExp[] {
|
|
|
47
46
|
* ```
|
|
48
47
|
*/
|
|
49
48
|
export function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {
|
|
50
|
-
// Aligned with Flutter SDK FaroConfig defaults
|
|
51
49
|
const {
|
|
52
50
|
// Error & crash tracking
|
|
53
51
|
enableErrorReporting = true,
|
|
@@ -77,7 +75,7 @@ export function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}):
|
|
|
77
75
|
|
|
78
76
|
const instrumentations: Instrumentation[] = [];
|
|
79
77
|
|
|
80
|
-
// Error reporting
|
|
78
|
+
// Error reporting
|
|
81
79
|
if (enableErrorReporting) {
|
|
82
80
|
instrumentations.push(
|
|
83
81
|
new ErrorsInstrumentation({
|
|
@@ -86,18 +84,18 @@ export function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}):
|
|
|
86
84
|
);
|
|
87
85
|
}
|
|
88
86
|
|
|
89
|
-
// Console capture
|
|
87
|
+
// Console capture
|
|
90
88
|
if (enableConsoleCapture) {
|
|
91
89
|
instrumentations.push(new ConsoleInstrumentation());
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
// Sessions
|
|
92
|
+
// Sessions
|
|
95
93
|
instrumentations.push(new SessionInstrumentation());
|
|
96
94
|
|
|
97
|
-
// Views
|
|
95
|
+
// Views
|
|
98
96
|
instrumentations.push(new ViewInstrumentation());
|
|
99
97
|
|
|
100
|
-
// App state
|
|
98
|
+
// App state
|
|
101
99
|
instrumentations.push(new AppStateInstrumentation());
|
|
102
100
|
|
|
103
101
|
// User actions - enabled by default, opt-out via enableUserActions
|
|
@@ -108,6 +108,12 @@ describe('makeRNConfig', () => {
|
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
it('enables minimal session persistence by default', () => {
|
|
112
|
+
const cfg = makeRNConfig({ ...base });
|
|
113
|
+
|
|
114
|
+
expect(cfg.sessionTracking?.persistent).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
111
117
|
it('uses getRNInstrumentations for the instrumentation set from flags', () => {
|
|
112
118
|
const minimal = makeRNConfig({
|
|
113
119
|
...base,
|
package/src/config/sampling.ts
CHANGED
|
@@ -7,9 +7,6 @@ import type { Meta } from '@grafana/faro-core';
|
|
|
7
7
|
* allowing sampling decisions based on session attributes, user info, app
|
|
8
8
|
* environment, and other metadata.
|
|
9
9
|
*
|
|
10
|
-
* This aligns with the Faro Flutter SDK's `SamplingContext` which provides
|
|
11
|
-
* `meta` to the sampler.
|
|
12
|
-
*
|
|
13
10
|
* @example
|
|
14
11
|
* ```ts
|
|
15
12
|
* sampling: SamplingFunction((context) => {
|
package/src/config/types.ts
CHANGED
|
@@ -8,11 +8,13 @@ import type { Sampling } from './sampling';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* React Native session tracking config.
|
|
11
|
-
* Extends Config['sessionTracking'] with sampling
|
|
11
|
+
* Extends Config['sessionTracking'] with sampling and RN-specific props.
|
|
12
12
|
* Excludes samplingRate and sampler in favor of sampling.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* (
|
|
14
|
+
* Persistence defaults to true and stores a minimal, versioned record with
|
|
15
|
+
* [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) (synchronous).
|
|
16
|
+
* Add `react-native-mmkv` to your app and rebuild native projects, or set
|
|
17
|
+
* `persistent` to false to use in-memory sessions.
|
|
16
18
|
*/
|
|
17
19
|
export type ReactNativeSessionTrackingConfig = Omit<
|
|
18
20
|
NonNullable<Config['sessionTracking']>,
|
|
@@ -13,8 +13,7 @@ const DEFAULT_STORAGE_KEY = 'faro_enable_data_collection';
|
|
|
13
13
|
* Controls whether telemetry data is collected and sent.
|
|
14
14
|
* The setting is persisted to AsyncStorage so it survives app restarts.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* - Uses AsyncStorage for persistence (matching Flutter's SharedPreferences)
|
|
16
|
+
* Uses AsyncStorage for persisting data collection policy state.
|
|
18
17
|
* - Provides enable/disable methods
|
|
19
18
|
* - Initializes from persisted value or default
|
|
20
19
|
*
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.
|
|
3
3
|
*/
|
|
4
4
|
export const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;
|
|
5
|
-
export const FARO_REACT_NATIVE_NPM_VERSION = '1.
|
|
5
|
+
export const FARO_REACT_NATIVE_NPM_VERSION = '1.4.0' as const;
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { faro } from '@grafana/faro-core';
|
|
|
8
8
|
// Export types
|
|
9
9
|
export type { EnableTransportsConfig, ReactNativeConfig, ReactNativeSessionTrackingConfig } from './config/types';
|
|
10
10
|
|
|
11
|
-
// Export sampling
|
|
11
|
+
// Export sampling
|
|
12
12
|
export { SamplingFunction, SamplingRate, type Sampling, type SamplingContext } from './config/sampling';
|
|
13
13
|
|
|
14
14
|
// Export instrumentation helpers
|
|
@@ -18,7 +18,12 @@ export { getRNInstrumentations } from './config/getRNInstrumentations';
|
|
|
18
18
|
export { ErrorMechanism, ErrorsInstrumentation } from './instrumentations/errors';
|
|
19
19
|
export type { ErrorMechanismType, ErrorsInstrumentationOptions } from './instrumentations/errors';
|
|
20
20
|
export { ConsoleInstrumentation } from './instrumentations/console';
|
|
21
|
-
export { SessionInstrumentation } from './instrumentations/session';
|
|
21
|
+
export { startNewSession, SessionInstrumentation } from './instrumentations/session';
|
|
22
|
+
export {
|
|
23
|
+
FaroSessionActivityBoundary,
|
|
24
|
+
type FaroSessionActivityBoundaryProps,
|
|
25
|
+
} from './instrumentations/session/FaroSessionActivityBoundary';
|
|
26
|
+
export { notifySessionActivity } from './instrumentations/session/directSessionActivity';
|
|
22
27
|
export {
|
|
23
28
|
getSessionAttributes,
|
|
24
29
|
loadMobileMetaForInit,
|
|
@@ -70,6 +75,7 @@ export { getSdkMeta } from './metas/sdk';
|
|
|
70
75
|
|
|
71
76
|
// Export transports
|
|
72
77
|
export { FetchTransport } from './transports/fetch';
|
|
78
|
+
export type { FetchTransportSendOutcome, FetchTransportSendResult } from './transports/fetch';
|
|
73
79
|
export { ConsoleTransport } from './transports/console';
|
|
74
80
|
export type { ConsoleTransportOptions } from './transports/console';
|
|
75
81
|
export { OfflineTransport } from './transports/offline';
|
|
@@ -73,7 +73,14 @@ export class ANRInstrumentation extends BaseInstrumentation {
|
|
|
73
73
|
this.startNativeTracking(nativeModule);
|
|
74
74
|
|
|
75
75
|
const emitter = new NativeEventEmitter(nativeModule);
|
|
76
|
-
this.anrEventSubscription = emitter.addListener('onANRDetected', (
|
|
76
|
+
this.anrEventSubscription = emitter.addListener('onANRDetected', (...args: readonly Object[]) => {
|
|
77
|
+
const [anrJson] = args;
|
|
78
|
+
|
|
79
|
+
if (typeof anrJson !== 'string') {
|
|
80
|
+
this.logWarn('Ignoring onANRDetected event without a string payload');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
void this.handleLiveAnrEvent(nativeModule, anrJson);
|
|
78
85
|
});
|
|
79
86
|
|
|
@@ -2,9 +2,16 @@ import { AppState, type AppStateStatus } from 'react-native';
|
|
|
2
2
|
|
|
3
3
|
import { BaseInstrumentation, dateNow, VERSION } from '@grafana/faro-core';
|
|
4
4
|
|
|
5
|
-
// Event name aligned with Faro Flutter SDK (app_lifecycle_changed)
|
|
6
5
|
export const EVENT_APP_STATE_CHANGED = 'app_lifecycle_changed';
|
|
7
6
|
|
|
7
|
+
const APP_STATE_STATUSES = ['inactive', 'background', 'active', 'extension', 'unknown'] as const;
|
|
8
|
+
|
|
9
|
+
// RN types AppState.currentState as `null | undefined | string`: it stays null
|
|
10
|
+
// until the initial value is set, so narrow it instead of trusting the shape.
|
|
11
|
+
function isAppStateStatus(value: unknown): value is AppStateStatus {
|
|
12
|
+
return typeof value === 'string' && (APP_STATE_STATUSES as readonly string[]).includes(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* AppState instrumentation for React Native
|
|
10
17
|
* Tracks app foreground/background/inactive state changes
|
|
@@ -22,7 +29,8 @@ export class AppStateInstrumentation extends BaseInstrumentation {
|
|
|
22
29
|
|
|
23
30
|
initialize(): void {
|
|
24
31
|
// Get initial app state
|
|
25
|
-
|
|
32
|
+
const initialState = AppState.currentState;
|
|
33
|
+
this.currentState = isAppStateStatus(initialState) ? initialState : undefined;
|
|
26
34
|
this.stateStartTime = dateNow();
|
|
27
35
|
|
|
28
36
|
this.logInfo('AppState instrumentation initialized', {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { NativeModules } from 'react-native';
|
|
2
2
|
|
|
3
3
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
4
|
+
import type { ExceptionStackFrame } from '@grafana/faro-core';
|
|
4
5
|
|
|
5
6
|
import { looksLikeNativeTombstoneTrace } from './android/nativeTombstoneTrace';
|
|
6
7
|
import type { CrashReport, CrashReportingOptions } from './types';
|
|
7
8
|
|
|
9
|
+
interface CrashReportDetails {
|
|
10
|
+
context: Record<string, string>;
|
|
11
|
+
errorMessage: string;
|
|
12
|
+
stackFrames: ExceptionStackFrame[];
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Abstract base class for platform-specific crash reporting implementations.
|
|
10
17
|
*
|
|
@@ -39,6 +46,7 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
this.logDebug('Initializing crash reporting instrumentation');
|
|
49
|
+
await this.prepareCrashReporting(nativeModule);
|
|
42
50
|
|
|
43
51
|
// Wait for session attributes before sending crash reports
|
|
44
52
|
await this.waitForSessionAttributes();
|
|
@@ -82,7 +90,11 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
82
90
|
*/
|
|
83
91
|
protected abstract getTraceUnavailableWarning(): string;
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
protected prepareCrashReporting(_nativeModule: typeof NativeModules.FaroReactNativeModule): void | Promise<void> {
|
|
94
|
+
// Optional platform-specific setup.
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected async waitForSessionAttributes(maxWaitMs = 10000): Promise<void> {
|
|
86
98
|
const startTime = Date.now();
|
|
87
99
|
const pollInterval = 200;
|
|
88
100
|
let checkCount = 0;
|
|
@@ -134,7 +146,7 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
134
146
|
);
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
|
|
149
|
+
protected getNativeModule(): typeof NativeModules.FaroReactNativeModule | null {
|
|
138
150
|
const { FaroReactNativeModule } = NativeModules;
|
|
139
151
|
|
|
140
152
|
if (!FaroReactNativeModule) {
|
|
@@ -144,7 +156,7 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
144
156
|
return FaroReactNativeModule;
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
|
|
159
|
+
protected async processCrashReports(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {
|
|
148
160
|
try {
|
|
149
161
|
if (typeof nativeModule.getCrashReport !== 'function') {
|
|
150
162
|
this.logDebug('getCrashReport method not available');
|
|
@@ -201,15 +213,11 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
201
213
|
}
|
|
202
214
|
}
|
|
203
215
|
|
|
204
|
-
|
|
216
|
+
protected buildCrashReportDetails(crash: CrashReport): CrashReportDetails {
|
|
205
217
|
// Platform-specific parsing
|
|
206
218
|
const parsedTrace = crash.trace ? this.parseCrashTrace(crash.trace) : null;
|
|
207
219
|
const errorMessage = this.resolveCrashErrorMessage(crash, parsedTrace).trim() || 'Application crash';
|
|
208
220
|
|
|
209
|
-
// Use a message-only Error so pushError does not capture the JS reporter stack
|
|
210
|
-
const error = new Error(errorMessage);
|
|
211
|
-
error.stack = undefined;
|
|
212
|
-
|
|
213
221
|
// Build context from crash data
|
|
214
222
|
const context: Record<string, string> = {
|
|
215
223
|
mechanism: 'crash',
|
|
@@ -258,6 +266,18 @@ export abstract class BaseCrashReportingInstrumentation extends BaseInstrumentat
|
|
|
258
266
|
);
|
|
259
267
|
}
|
|
260
268
|
|
|
269
|
+
return {
|
|
270
|
+
context,
|
|
271
|
+
errorMessage,
|
|
272
|
+
stackFrames,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
protected sendCrashReport(crash: CrashReport): void {
|
|
277
|
+
const { context, errorMessage, stackFrames } = this.buildCrashReportDetails(crash);
|
|
278
|
+
const error = new Error(errorMessage);
|
|
279
|
+
error.stack = undefined;
|
|
280
|
+
|
|
261
281
|
this.api.pushError(error, {
|
|
262
282
|
stackFrames,
|
|
263
283
|
context,
|