@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
|
@@ -55,11 +55,11 @@ exports.FrameMonitoringInstrumentation = void 0;
|
|
|
55
55
|
var react_native_1 = require("react-native");
|
|
56
56
|
var faro_core_1 = require("@grafana/faro-core");
|
|
57
57
|
/**
|
|
58
|
-
* Default configuration values
|
|
59
|
-
*
|
|
58
|
+
* Default configuration values. Frozen frame threshold aligns with Android Vitals
|
|
59
|
+
* and OpenTelemetry Android slow rendering instrumentation (700ms).
|
|
60
60
|
*/
|
|
61
61
|
var DEFAULT_TARGET_FPS = 60;
|
|
62
|
-
var DEFAULT_FROZEN_FRAME_THRESHOLD_MS =
|
|
62
|
+
var DEFAULT_FROZEN_FRAME_THRESHOLD_MS = 700;
|
|
63
63
|
var DEFAULT_REFRESH_RATE_POLLING_INTERVAL = 30000;
|
|
64
64
|
var DEFAULT_NORMALIZED_REFRESH_RATE = 60;
|
|
65
65
|
/**
|
|
@@ -76,7 +76,7 @@ var DEFAULT_NORMALIZED_REFRESH_RATE = 60;
|
|
|
76
76
|
* - Reports meaningful performance degradation that affects user experience
|
|
77
77
|
*
|
|
78
78
|
* ## Frozen Frame Detection
|
|
79
|
-
* Frozen frames are individual frames exceeding `frozenFrameThresholdMs` (default
|
|
79
|
+
* Frozen frames are individual frames exceeding `frozenFrameThresholdMs` (default 700ms).
|
|
80
80
|
* These represent severe jank where the app appears unresponsive.
|
|
81
81
|
*
|
|
82
82
|
* Sends measurements via Faro API:
|
|
@@ -93,7 +93,7 @@ var DEFAULT_NORMALIZED_REFRESH_RATE = 60;
|
|
|
93
93
|
* instrumentations: [
|
|
94
94
|
* new FrameMonitoringInstrumentation({
|
|
95
95
|
* targetFps: 60,
|
|
96
|
-
* frozenFrameThresholdMs:
|
|
96
|
+
* frozenFrameThresholdMs: 700,
|
|
97
97
|
* refreshRatePollingInterval: 30000,
|
|
98
98
|
* }),
|
|
99
99
|
* ],
|
|
@@ -142,7 +142,7 @@ var FrameMonitoringInstrumentation = /** @class */ (function (_super) {
|
|
|
142
142
|
}
|
|
143
143
|
// Start native frame monitoring with configuration
|
|
144
144
|
this.startNativeMonitoring(nativeModule);
|
|
145
|
-
// Set up
|
|
145
|
+
// Set up Android refresh rate events (throttled on native side); frozen/slow frames use polling
|
|
146
146
|
this.setupEventListeners(nativeModule);
|
|
147
147
|
// Set up polling for periodic metrics collection (iOS pattern)
|
|
148
148
|
this.startPolling();
|
|
@@ -171,30 +171,24 @@ var FrameMonitoringInstrumentation = /** @class */ (function (_super) {
|
|
|
171
171
|
};
|
|
172
172
|
FrameMonitoringInstrumentation.prototype.setupEventListeners = function (nativeModule) {
|
|
173
173
|
var _this = this;
|
|
174
|
-
// Android
|
|
175
|
-
|
|
176
|
-
if (react_native_1.Platform.OS === 'android') {
|
|
174
|
+
// Android: refresh rate only. Slow and frozen frames are polled (same as iOS) to avoid duplicate reports.
|
|
175
|
+
if (react_native_1.Platform.OS === 'android' && this.refreshRateVitalsEnabled) {
|
|
177
176
|
try {
|
|
178
177
|
this.eventEmitter = new react_native_1.NativeEventEmitter(nativeModule);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
_this.handleFrozenFrame(data, 0);
|
|
178
|
+
var refreshRateSubscription = this.eventEmitter.addListener('onRefreshRate', function () {
|
|
179
|
+
var args = [];
|
|
180
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
181
|
+
args[_i] = arguments[_i];
|
|
184
182
|
}
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
var refreshRate = args[0];
|
|
184
|
+
if (typeof refreshRate !== 'number') {
|
|
185
|
+
_this.logWarn('Ignoring onRefreshRate event without a numeric payload');
|
|
186
|
+
return;
|
|
187
187
|
}
|
|
188
|
+
_this.handleRefreshRate(refreshRate);
|
|
188
189
|
});
|
|
189
|
-
this.eventSubscriptions.push(
|
|
190
|
-
|
|
191
|
-
if (this.refreshRateVitalsEnabled) {
|
|
192
|
-
var refreshRateSubscription = this.eventEmitter.addListener('onRefreshRate', function (refreshRate) {
|
|
193
|
-
_this.handleRefreshRate(refreshRate);
|
|
194
|
-
});
|
|
195
|
-
this.eventSubscriptions.push(refreshRateSubscription);
|
|
196
|
-
}
|
|
197
|
-
this.logDebug('Set up Android frame event listeners');
|
|
190
|
+
this.eventSubscriptions.push(refreshRateSubscription);
|
|
191
|
+
this.logDebug('Set up Android refresh rate event listener');
|
|
198
192
|
}
|
|
199
193
|
catch (error) {
|
|
200
194
|
this.logError('Failed to set up frame event listeners', error);
|
|
@@ -203,8 +197,7 @@ var FrameMonitoringInstrumentation = /** @class */ (function (_super) {
|
|
|
203
197
|
};
|
|
204
198
|
FrameMonitoringInstrumentation.prototype.startPolling = function () {
|
|
205
199
|
var _this = this;
|
|
206
|
-
// Both iOS and Android use polling
|
|
207
|
-
// This provides consistent behavior and avoids flooding with events
|
|
200
|
+
// Both iOS and Android use polling for slow and frozen frames (avoids duplicate reports on Android).
|
|
208
201
|
this.pollingIntervalId = setInterval(function () {
|
|
209
202
|
_this.pollFrameMetrics();
|
|
210
203
|
}, this.options.refreshRatePollingInterval);
|
|
@@ -273,11 +266,7 @@ var FrameMonitoringInstrumentation = /** @class */ (function (_super) {
|
|
|
273
266
|
}, { skipDedupe: true });
|
|
274
267
|
};
|
|
275
268
|
FrameMonitoringInstrumentation.prototype.handleFrozenFrame = function (count, durationMs) {
|
|
276
|
-
// Only send frozen frame events if duration is greater than 0
|
|
277
|
-
// This filters out any erroneous 0ms frozen frames
|
|
278
269
|
if (durationMs > 0) {
|
|
279
|
-
// 🔍 TEMP DEBUG LOG - Remove after analysis
|
|
280
|
-
console.log("[Faro DEBUG ".concat(react_native_1.Platform.OS.toUpperCase(), "] \uD83E\uDDCA SENDING frozen frame: count=").concat(count, ", duration=").concat(durationMs, "ms"));
|
|
281
270
|
this.api.pushMeasurement({
|
|
282
271
|
type: 'app_frozen_frame',
|
|
283
272
|
values: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/frameMonitoring/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 type { ReactNativeConfig } from '../../config/types';\n\nimport type { FrameMetrics, FrameMonitoringOptions } from './types';\n\n/**\n * Default configuration values matching Flutter SDK's hardcoded values.\n * These are React Native-specific advanced options for customization.\n */\nconst DEFAULT_TARGET_FPS = 60;\nconst DEFAULT_FROZEN_FRAME_THRESHOLD_MS = 100;\nconst DEFAULT_REFRESH_RATE_POLLING_INTERVAL = 30000;\nconst DEFAULT_NORMALIZED_REFRESH_RATE = 60;\n\n/**\n * Frame Monitoring Instrumentation for React Native.\n *\n * Monitors frame rendering performance and detects slow/frozen frames.\n * Uses native CADisplayLink (iOS) and Choreographer (Android) for accurate metrics.\n *\n * ## Slow Frame Detection\n * Slow frames are detected using **event-based grouping** to report user-perceptible jank:\n * - Tracks consecutive frames below `targetFps` as a single \"slow frame event\"\n * - Only counts events lasting ≥50ms (~3 frames at 60fps) to filter noise\n * - This prevents reporting normal microsecond variations as slow frames\n * - Reports meaningful performance degradation that affects user experience\n *\n * ## Frozen Frame Detection\n * Frozen frames are individual frames exceeding `frozenFrameThresholdMs` (default 100ms).\n * These represent severe jank where the app appears unresponsive.\n *\n * Sends measurements via Faro API:\n * - `app_refresh_rate`: Current refresh rate in FPS\n * - `app_frames_rate`: Number of slow frame events (not individual frames)\n * - `app_frozen_frame`: Number of frozen frames (exceeding frozenFrameThresholdMs)\n *\n * @example\n * ```typescript\n * import { initializeFaro, FrameMonitoringInstrumentation } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://collector.example.com',\n * instrumentations: [\n * new FrameMonitoringInstrumentation({\n * targetFps: 60,\n * frozenFrameThresholdMs: 100,\n * refreshRatePollingInterval: 30000,\n * }),\n * ],\n * });\n * ```\n */\nexport class FrameMonitoringInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-frame-monitoring';\n readonly version = VERSION;\n\n private readonly options: Required<FrameMonitoringOptions>;\n private pollingIntervalId: ReturnType<typeof setInterval> | null = null;\n private eventEmitter: NativeEventEmitter | null = null;\n private eventSubscriptions: Array<{ remove: () => void }> = [];\n\n constructor(options: FrameMonitoringOptions = {}) {\n super();\n this.options = {\n targetFps: options.targetFps ?? DEFAULT_TARGET_FPS,\n frozenFrameThresholdMs: options.frozenFrameThresholdMs ?? DEFAULT_FROZEN_FRAME_THRESHOLD_MS,\n refreshRatePollingInterval: options.refreshRatePollingInterval ?? DEFAULT_REFRESH_RATE_POLLING_INTERVAL,\n normalizedRefreshRate: options.normalizedRefreshRate ?? DEFAULT_NORMALIZED_REFRESH_RATE,\n };\n }\n\n /**\n * Check if refresh rate vitals are enabled in Faro config.\n * Reads from faro.config.refreshRateVitals (single source of truth).\n */\n private get refreshRateVitalsEnabled(): boolean {\n // Access config via this.config which is set by BaseInstrumentation after initialization\n const config = this.config as ReactNativeConfig | undefined;\n return config?.refreshRateVitals ?? false;\n }\n\n initialize(): void {\n this.logDebug('Initializing frame monitoring instrumentation');\n\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n this.logWarn('Native module not available for frame monitoring');\n return;\n }\n\n // Start native frame monitoring with configuration\n this.startNativeMonitoring(nativeModule);\n\n // Set up event listeners for real-time frame events (Android pattern)\n this.setupEventListeners(nativeModule);\n\n // Set up polling for periodic metrics collection (iOS pattern)\n this.startPolling();\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 startNativeMonitoring(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n try {\n if (typeof nativeModule.startFrameMonitoring === 'function') {\n nativeModule.startFrameMonitoring({\n targetFps: this.options.targetFps,\n frozenFrameThresholdMs: this.options.frozenFrameThresholdMs,\n normalizedRefreshRate: this.options.normalizedRefreshRate,\n });\n this.logDebug('Started native frame monitoring');\n }\n } catch (error) {\n this.logError('Failed to start native frame monitoring', error);\n }\n }\n\n private setupEventListeners(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n // Android uses event-based approach for frozen frames only\n // Slow frames are retrieved via polling (like iOS) to avoid flooding\n if (Platform.OS === 'android') {\n try {\n this.eventEmitter = new NativeEventEmitter(nativeModule);\n\n // Listen for frozen frame events\n const frozenFrameSubscription = this.eventEmitter.addListener(\n 'onFrozenFrame',\n (data: number | { count: number; durationMs: number }) => {\n // Support both formats: number (legacy) or object (with duration)\n if (typeof data === 'number') {\n this.handleFrozenFrame(data, 0);\n } else {\n this.handleFrozenFrame(data.count, data.durationMs || 0);\n }\n }\n );\n this.eventSubscriptions.push(frozenFrameSubscription);\n\n // Listen for refresh rate events (only when refreshRateVitals is enabled in config)\n if (this.refreshRateVitalsEnabled) {\n const refreshRateSubscription = this.eventEmitter.addListener('onRefreshRate', (refreshRate: number) => {\n this.handleRefreshRate(refreshRate);\n });\n this.eventSubscriptions.push(refreshRateSubscription);\n }\n\n this.logDebug('Set up Android frame event listeners');\n } catch (error) {\n this.logError('Failed to set up frame event listeners', error);\n }\n }\n }\n\n private startPolling(): void {\n // Both iOS and Android use polling approach for slow frames\n // This provides consistent behavior and avoids flooding with events\n this.pollingIntervalId = setInterval(() => {\n this.pollFrameMetrics();\n }, this.options.refreshRatePollingInterval);\n }\n\n private async pollFrameMetrics(): Promise<void> {\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n return;\n }\n\n try {\n // Get refresh rate (only send when refreshRateVitals is enabled in config)\n if (this.refreshRateVitalsEnabled) {\n if (typeof nativeModule.getRefreshRate === 'function') {\n const refreshRate = await nativeModule.getRefreshRate();\n if (refreshRate !== null && refreshRate > 0) {\n this.handleRefreshRate(refreshRate);\n }\n }\n }\n\n // Get full frame metrics if available\n if (typeof nativeModule.getFrameMetrics === 'function') {\n const metrics = (await nativeModule.getFrameMetrics()) as FrameMetrics | null;\n if (metrics) {\n if (this.refreshRateVitalsEnabled && metrics.refreshRate > 0) {\n this.handleRefreshRate(metrics.refreshRate);\n }\n if (metrics.slowFrames > 0) {\n this.handleSlowFrames(metrics.slowFrames);\n }\n if (metrics.frozenFrames > 0) {\n this.handleFrozenFrame(metrics.frozenFrames, metrics.frozenDurationMs);\n }\n }\n }\n } catch (error) {\n this.logError('Failed to poll frame metrics', error);\n }\n }\n\n private handleRefreshRate(refreshRate: number): void {\n this.api.pushMeasurement(\n {\n type: 'app_refresh_rate',\n values: { refresh_rate: refreshRate },\n },\n { skipDedupe: true }\n );\n }\n\n private handleSlowFrames(count: number): void {\n // Note: Despite the name \"slow_frames\", this count represents slow frame EVENTS, not individual frames.\n // Each event is a period of consecutive slow frames lasting ≥50ms (~3 frames at 60fps).\n this.api.pushMeasurement(\n {\n type: 'app_frames_rate',\n values: { slow_frames: count },\n },\n { skipDedupe: true }\n );\n }\n\n private handleFrozenFrame(count: number, durationMs: number): void {\n // Only send frozen frame events if duration is greater than 0\n // This filters out any erroneous 0ms frozen frames\n if (durationMs > 0) {\n // 🔍 TEMP DEBUG LOG - Remove after analysis\n console.log(\n `[Faro DEBUG ${Platform.OS.toUpperCase()}] 🧊 SENDING frozen frame: count=${count}, duration=${durationMs}ms`\n );\n\n this.api.pushMeasurement(\n {\n type: 'app_frozen_frame',\n values: {\n frozen_frames: count,\n frozen_duration: durationMs,\n },\n },\n { skipDedupe: true }\n );\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 // Remove event subscriptions\n for (const subscription of this.eventSubscriptions) {\n subscription.remove();\n }\n this.eventSubscriptions = [];\n this.eventEmitter = null;\n\n // Stop native monitoring\n const nativeModule = this.getNativeModule();\n if (nativeModule && typeof nativeModule.stopFrameMonitoring === 'function') {\n try {\n nativeModule.stopFrameMonitoring();\n } catch (error) {\n this.logError('Failed to stop native frame monitoring', error);\n }\n }\n\n this.logDebug('Frame monitoring instrumentation stopped');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/frameMonitoring/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 type { ReactNativeConfig } from '../../config/types';\n\nimport type { FrameMetrics, FrameMonitoringOptions } from './types';\n\n/**\n * Default configuration values. Frozen frame threshold aligns with Android Vitals\n * and OpenTelemetry Android slow rendering instrumentation (700ms).\n */\nconst DEFAULT_TARGET_FPS = 60;\nconst DEFAULT_FROZEN_FRAME_THRESHOLD_MS = 700;\nconst DEFAULT_REFRESH_RATE_POLLING_INTERVAL = 30000;\nconst DEFAULT_NORMALIZED_REFRESH_RATE = 60;\n\n/**\n * Frame Monitoring Instrumentation for React Native.\n *\n * Monitors frame rendering performance and detects slow/frozen frames.\n * Uses native CADisplayLink (iOS) and Choreographer (Android) for accurate metrics.\n *\n * ## Slow Frame Detection\n * Slow frames are detected using **event-based grouping** to report user-perceptible jank:\n * - Tracks consecutive frames below `targetFps` as a single \"slow frame event\"\n * - Only counts events lasting ≥50ms (~3 frames at 60fps) to filter noise\n * - This prevents reporting normal microsecond variations as slow frames\n * - Reports meaningful performance degradation that affects user experience\n *\n * ## Frozen Frame Detection\n * Frozen frames are individual frames exceeding `frozenFrameThresholdMs` (default 700ms).\n * These represent severe jank where the app appears unresponsive.\n *\n * Sends measurements via Faro API:\n * - `app_refresh_rate`: Current refresh rate in FPS\n * - `app_frames_rate`: Number of slow frame events (not individual frames)\n * - `app_frozen_frame`: Number of frozen frames (exceeding frozenFrameThresholdMs)\n *\n * @example\n * ```typescript\n * import { initializeFaro, FrameMonitoringInstrumentation } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://collector.example.com',\n * instrumentations: [\n * new FrameMonitoringInstrumentation({\n * targetFps: 60,\n * frozenFrameThresholdMs: 700,\n * refreshRatePollingInterval: 30000,\n * }),\n * ],\n * });\n * ```\n */\nexport class FrameMonitoringInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-frame-monitoring';\n readonly version = VERSION;\n\n private readonly options: Required<FrameMonitoringOptions>;\n private pollingIntervalId: ReturnType<typeof setInterval> | null = null;\n private eventEmitter: NativeEventEmitter | null = null;\n private eventSubscriptions: Array<{ remove: () => void }> = [];\n\n constructor(options: FrameMonitoringOptions = {}) {\n super();\n this.options = {\n targetFps: options.targetFps ?? DEFAULT_TARGET_FPS,\n frozenFrameThresholdMs: options.frozenFrameThresholdMs ?? DEFAULT_FROZEN_FRAME_THRESHOLD_MS,\n refreshRatePollingInterval: options.refreshRatePollingInterval ?? DEFAULT_REFRESH_RATE_POLLING_INTERVAL,\n normalizedRefreshRate: options.normalizedRefreshRate ?? DEFAULT_NORMALIZED_REFRESH_RATE,\n };\n }\n\n /**\n * Check if refresh rate vitals are enabled in Faro config.\n * Reads from faro.config.refreshRateVitals (single source of truth).\n */\n private get refreshRateVitalsEnabled(): boolean {\n // Access config via this.config which is set by BaseInstrumentation after initialization\n const config = this.config as ReactNativeConfig | undefined;\n return config?.refreshRateVitals ?? false;\n }\n\n initialize(): void {\n this.logDebug('Initializing frame monitoring instrumentation');\n\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n this.logWarn('Native module not available for frame monitoring');\n return;\n }\n\n // Start native frame monitoring with configuration\n this.startNativeMonitoring(nativeModule);\n\n // Set up Android refresh rate events (throttled on native side); frozen/slow frames use polling\n this.setupEventListeners(nativeModule);\n\n // Set up polling for periodic metrics collection (iOS pattern)\n this.startPolling();\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 startNativeMonitoring(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n try {\n if (typeof nativeModule.startFrameMonitoring === 'function') {\n nativeModule.startFrameMonitoring({\n targetFps: this.options.targetFps,\n frozenFrameThresholdMs: this.options.frozenFrameThresholdMs,\n normalizedRefreshRate: this.options.normalizedRefreshRate,\n });\n this.logDebug('Started native frame monitoring');\n }\n } catch (error) {\n this.logError('Failed to start native frame monitoring', error);\n }\n }\n\n private setupEventListeners(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n // Android: refresh rate only. Slow and frozen frames are polled (same as iOS) to avoid duplicate reports.\n if (Platform.OS === 'android' && this.refreshRateVitalsEnabled) {\n try {\n this.eventEmitter = new NativeEventEmitter(nativeModule);\n\n const refreshRateSubscription = this.eventEmitter.addListener('onRefreshRate', (...args: readonly Object[]) => {\n const [refreshRate] = args;\n\n if (typeof refreshRate !== 'number') {\n this.logWarn('Ignoring onRefreshRate event without a numeric payload');\n return;\n }\n\n this.handleRefreshRate(refreshRate);\n });\n this.eventSubscriptions.push(refreshRateSubscription);\n\n this.logDebug('Set up Android refresh rate event listener');\n } catch (error) {\n this.logError('Failed to set up frame event listeners', error);\n }\n }\n }\n\n private startPolling(): void {\n // Both iOS and Android use polling for slow and frozen frames (avoids duplicate reports on Android).\n this.pollingIntervalId = setInterval(() => {\n this.pollFrameMetrics();\n }, this.options.refreshRatePollingInterval);\n }\n\n private async pollFrameMetrics(): Promise<void> {\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n return;\n }\n\n try {\n // Get refresh rate (only send when refreshRateVitals is enabled in config)\n if (this.refreshRateVitalsEnabled) {\n if (typeof nativeModule.getRefreshRate === 'function') {\n const refreshRate = await nativeModule.getRefreshRate();\n if (refreshRate !== null && refreshRate > 0) {\n this.handleRefreshRate(refreshRate);\n }\n }\n }\n\n // Get full frame metrics if available\n if (typeof nativeModule.getFrameMetrics === 'function') {\n const metrics = (await nativeModule.getFrameMetrics()) as FrameMetrics | null;\n if (metrics) {\n if (this.refreshRateVitalsEnabled && metrics.refreshRate > 0) {\n this.handleRefreshRate(metrics.refreshRate);\n }\n if (metrics.slowFrames > 0) {\n this.handleSlowFrames(metrics.slowFrames);\n }\n if (metrics.frozenFrames > 0) {\n this.handleFrozenFrame(metrics.frozenFrames, metrics.frozenDurationMs);\n }\n }\n }\n } catch (error) {\n this.logError('Failed to poll frame metrics', error);\n }\n }\n\n private handleRefreshRate(refreshRate: number): void {\n this.api.pushMeasurement(\n {\n type: 'app_refresh_rate',\n values: { refresh_rate: refreshRate },\n },\n { skipDedupe: true }\n );\n }\n\n private handleSlowFrames(count: number): void {\n // Note: Despite the name \"slow_frames\", this count represents slow frame EVENTS, not individual frames.\n // Each event is a period of consecutive slow frames lasting ≥50ms (~3 frames at 60fps).\n this.api.pushMeasurement(\n {\n type: 'app_frames_rate',\n values: { slow_frames: count },\n },\n { skipDedupe: true }\n );\n }\n\n private handleFrozenFrame(count: number, durationMs: number): void {\n if (durationMs > 0) {\n this.api.pushMeasurement(\n {\n type: 'app_frozen_frame',\n values: {\n frozen_frames: count,\n frozen_duration: durationMs,\n },\n },\n { skipDedupe: true }\n );\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 // Remove event subscriptions\n for (const subscription of this.eventSubscriptions) {\n subscription.remove();\n }\n this.eventSubscriptions = [];\n this.eventEmitter = null;\n\n // Stop native monitoring\n const nativeModule = this.getNativeModule();\n if (nativeModule && typeof nativeModule.stopFrameMonitoring === 'function') {\n try {\n nativeModule.stopFrameMonitoring();\n } catch (error) {\n this.logError('Failed to stop native frame monitoring', error);\n }\n }\n\n this.logDebug('Frame monitoring instrumentation stopped');\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/frameMonitoring/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration options for Frame Monitoring instrumentation.\n *\n *
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/frameMonitoring/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration options for Frame Monitoring instrumentation.\n *\n * Frozen frame threshold (700ms).\n *\n * Note: To enable frame monitoring, set `refreshRateVitals: true` in ReactNativeConfig.\n */\nexport interface FrameMonitoringOptions {\n /**\n * Target frames per second for slow frame detection.\n * Frames rendering below this threshold are considered \"slow\".\n * Default: 60\n */\n targetFps?: number;\n\n /**\n * Threshold in milliseconds for detecting frozen frames.\n * Frames taking longer than this are considered \"frozen\".\n * Default: 700ms (Android Vitals and OpenTelemetry Android slow rendering instrumentation)\n */\n frozenFrameThresholdMs?: number;\n\n /**\n * Interval in milliseconds for collecting and sending frame metrics.\n * Default: 30000 (30 seconds, aligned with fetchVitalsInterval)\n */\n refreshRatePollingInterval?: number;\n\n /**\n * Normalized refresh rate for ProMotion displays (120Hz, etc.).\n * Used to normalize frame rates for high-refresh-rate displays to a standard baseline.\n * Default: 60\n */\n normalizedRefreshRate?: number;\n}\n\n/**\n * Frame metrics collected by the instrumentation.\n */\nexport interface FrameMetrics {\n /**\n * Current refresh rate in FPS.\n */\n refreshRate: number;\n\n /**\n * Number of slow frames since last measurement.\n */\n slowFrames: number;\n\n /**\n * Number of frozen frames since last measurement.\n */\n frozenFrames: number;\n\n /**\n * Total duration of frozen frames in milliseconds.\n */\n frozenDurationMs: number;\n}\n\n/**\n * Native module methods for frame monitoring.\n */\nexport interface FrameMonitoringNativeModule {\n /**\n * Start frame monitoring.\n */\n startFrameMonitoring(config: {\n targetFps: number;\n frozenFrameThresholdMs: number;\n normalizedRefreshRate: number;\n }): void;\n\n /**\n * Stop frame monitoring.\n */\n stopFrameMonitoring(): void;\n\n /**\n * Get current frame metrics.\n */\n getFrameMetrics(): Promise<FrameMetrics | null>;\n\n /**\n * Get current refresh rate.\n */\n getRefreshRate(): Promise<number | null>;\n}\n"]}
|
|
@@ -26,8 +26,6 @@ var FaroReactNativeModule = react_native_1.NativeModules.FaroReactNativeModule;
|
|
|
26
26
|
* - iOS: task_info() for memory, host_statistics() for CPU
|
|
27
27
|
* - Android: /proc/[pid]/status for memory, /proc/[pid]/stat for CPU
|
|
28
28
|
*
|
|
29
|
-
* Implementation ported from Faro Flutter SDK with feature parity.
|
|
30
|
-
*
|
|
31
29
|
* **Key Features**:
|
|
32
30
|
* - ✅ NO manual setup required - OS tracks metrics automatically!
|
|
33
31
|
* - ✅ Periodic collection (default: every 30 seconds)
|
|
@@ -141,15 +139,13 @@ var PerformanceInstrumentation = /** @class */ (function (_super) {
|
|
|
141
139
|
}
|
|
142
140
|
var cpuUsage = FaroReactNativeModule.getCpuUsage();
|
|
143
141
|
this.logDebug("CPU: raw value = ".concat(cpuUsage));
|
|
144
|
-
// Validate CPU usage
|
|
145
|
-
// Skip null, negative, or exactly 0 (baseline reading)
|
|
142
|
+
// Validate CPU usage. Skip null, negative, or exactly 0 (baseline reading).
|
|
146
143
|
if (cpuUsage == null || cpuUsage <= 0) {
|
|
147
144
|
this.logDebug('CPU: skipping - value is null or <= 0');
|
|
148
145
|
return;
|
|
149
146
|
}
|
|
150
147
|
this.logDebug("CPU: pushing measurement with cpu_usage = ".concat(cpuUsage));
|
|
151
|
-
//
|
|
152
|
-
// in multi-core scenarios where one core is maxed out
|
|
148
|
+
// Values >= 100 can be valid in multi-core scenarios where one core is maxed out.
|
|
153
149
|
this.api.pushMeasurement({
|
|
154
150
|
type: 'app_cpu_usage',
|
|
155
151
|
values: { cpu_usage: cpuUsage },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/performance/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,6CAA6C;AAE7C,gDAAkE;AAI1D,IAAA,qBAAqB,GAAK,4BAAa,sBAAlB,CAAmB;AAEhD
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/performance/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModules } from 'react-native';\n\nimport { BaseInstrumentation, VERSION } from '@grafana/faro-core';\n\nimport type { PerformanceInstrumentationOptions } from './types';\n\nconst { FaroReactNativeModule } = NativeModules;\n\n/**\n * Measures React Native app performance metrics (CPU and Memory usage)\n *\n * Collects periodic performance metrics using native OS APIs:\n * - iOS: task_info() for memory, host_statistics() for CPU\n * - Android: /proc/[pid]/status for memory, /proc/[pid]/stat for CPU\n *\n * **Key Features**:\n * - ✅ NO manual setup required - OS tracks metrics automatically!\n * - ✅ Periodic collection (default: every 30 seconds)\n * - ✅ Configurable per-metric enable/disable\n * - ✅ Differential CPU calculation (accurate usage percentages)\n * - ✅ Memory usage in KB (Resident Set Size)\n *\n * **Metrics Captured**:\n * - `mem_usage`: Current memory usage in KB (RSS - physical memory)\n * - `cpu_usage`: Current CPU usage percentage (0-100+)\n *\n * **Requirements**:\n * - iOS 13.4+ (any iOS that supports React Native)\n * - Android API 21+ for CPU (Android 5.0 Lollipop, ~99% of devices)\n * - Android any version for Memory\n *\n * @example\n * ```tsx\n * import { initializeFaro, getRNInstrumentations } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://your-collector.com',\n * instrumentations: [\n * ...getRNInstrumentations({\n * memoryUsageVitals: true, // default: true\n * cpuUsageVitals: true, // default: true\n * fetchVitalsInterval: 30000, // default: 30s\n * }),\n * ],\n * });\n * ```\n */\nexport class PerformanceInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-performance';\n readonly version = VERSION;\n\n private options: Required<PerformanceInstrumentationOptions>;\n private intervalId: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: PerformanceInstrumentationOptions = {}) {\n super();\n this.options = {\n memoryUsageVitals: options.memoryUsageVitals ?? true,\n cpuUsageVitals: options.cpuUsageVitals ?? true,\n fetchVitalsInterval: options.fetchVitalsInterval ?? 30000,\n };\n }\n\n initialize(): void {\n // Only start if at least one metric is enabled\n if (!this.options.memoryUsageVitals && !this.options.cpuUsageVitals) {\n this.logInfo('Performance monitoring disabled - no metrics enabled');\n return;\n }\n\n // Check native module availability\n if (!FaroReactNativeModule) {\n this.logWarn(\n 'Native module not available. Performance instrumentation requires native module. ' +\n 'Run `cd ios && pod install` and rebuild the app.'\n );\n return;\n }\n\n // Start periodic collection\n this.startPeriodicCollection();\n }\n\n private startPeriodicCollection(): void {\n // Collect immediately on initialization\n this.collectMetrics();\n\n // Then collect periodically\n this.intervalId = setInterval(() => {\n this.collectMetrics();\n }, this.options.fetchVitalsInterval);\n\n this.logInfo(\n `Performance monitoring started - collecting every ${this.options.fetchVitalsInterval}ms ` +\n `(memory: ${this.options.memoryUsageVitals}, cpu: ${this.options.cpuUsageVitals})`\n );\n }\n\n private collectMetrics(): void {\n // Collect memory if enabled\n if (this.options.memoryUsageVitals) {\n this.collectMemoryUsage();\n }\n\n // Collect CPU if enabled\n if (this.options.cpuUsageVitals) {\n this.collectCpuUsage();\n }\n }\n\n private collectMemoryUsage(): void {\n try {\n if (!FaroReactNativeModule?.getMemoryUsage) {\n this.logDebug('Memory: getMemoryUsage not available on native module');\n return;\n }\n\n const memoryUsage = FaroReactNativeModule.getMemoryUsage();\n if (memoryUsage == null || memoryUsage <= 0) {\n this.logDebug('Memory: skipping - value is null or <= 0');\n return;\n }\n\n this.logDebug(`Memory: pushing measurement with mem_usage = ${memoryUsage}`);\n this.api.pushMeasurement({\n type: 'app_memory',\n values: { mem_usage: memoryUsage },\n });\n } catch (error) {\n this.logError('Failed to collect memory usage', error);\n }\n }\n\n private collectCpuUsage(): void {\n try {\n if (!FaroReactNativeModule?.getCpuUsage) {\n this.logDebug('CPU: getCpuUsage not available on native module');\n return;\n }\n\n const cpuUsage = FaroReactNativeModule.getCpuUsage();\n this.logDebug(`CPU: raw value = ${cpuUsage}`);\n\n // Validate CPU usage. Skip null, negative, or exactly 0 (baseline reading).\n if (cpuUsage == null || cpuUsage <= 0) {\n this.logDebug('CPU: skipping - value is null or <= 0');\n return;\n }\n\n this.logDebug(`CPU: pushing measurement with cpu_usage = ${cpuUsage}`);\n // Values >= 100 can be valid in multi-core scenarios where one core is maxed out.\n this.api.pushMeasurement({\n type: 'app_cpu_usage',\n values: { cpu_usage: cpuUsage },\n });\n } catch (error) {\n this.logError('Failed to collect CPU usage', error);\n }\n }\n\n unpatch(): void {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n this.logInfo('Performance monitoring stopped');\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
14
|
+
var t = {};
|
|
15
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
16
|
+
t[p] = s[p];
|
|
17
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
18
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
19
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
20
|
+
t[p[i]] = s[p[i]];
|
|
21
|
+
}
|
|
22
|
+
return t;
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.FaroSessionActivityBoundary = FaroSessionActivityBoundary;
|
|
26
|
+
var jsx_runtime_1 = require("react/jsx-runtime");
|
|
27
|
+
var react_1 = require("react");
|
|
28
|
+
var react_native_1 = require("react-native");
|
|
29
|
+
var directSessionActivity_1 = require("./directSessionActivity");
|
|
30
|
+
/**
|
|
31
|
+
* Records touch activity for the React Native subtree without emitting a user
|
|
32
|
+
* action or taking ownership of the responder.
|
|
33
|
+
*/
|
|
34
|
+
function FaroSessionActivityBoundary(_a) {
|
|
35
|
+
var children = _a.children, onStartShouldSetResponderCapture = _a.onStartShouldSetResponderCapture, style = _a.style, viewProps = __rest(_a, ["children", "onStartShouldSetResponderCapture", "style"]);
|
|
36
|
+
var handleStartShouldSetResponderCapture = (0, react_1.useCallback)(function (event) {
|
|
37
|
+
var _a;
|
|
38
|
+
(0, directSessionActivity_1.notifySessionActivity)();
|
|
39
|
+
return (_a = onStartShouldSetResponderCapture === null || onStartShouldSetResponderCapture === void 0 ? void 0 : onStartShouldSetResponderCapture(event)) !== null && _a !== void 0 ? _a : false;
|
|
40
|
+
}, [onStartShouldSetResponderCapture]);
|
|
41
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, __assign({}, viewProps, { onStartShouldSetResponderCapture: handleStartShouldSetResponderCapture, style: [styles.container, style], children: children })));
|
|
42
|
+
}
|
|
43
|
+
var styles = react_native_1.StyleSheet.create({
|
|
44
|
+
container: {
|
|
45
|
+
flex: 1,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=FaroSessionActivityBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FaroSessionActivityBoundary.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/FaroSessionActivityBoundary.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAWA,kEAuBC;;AAlCD,+BAA4D;AAC5D,6CAA4F;AAE5F,iEAAgE;AAIhE;;;GAGG;AACH,SAAgB,2BAA2B,CAAC,EAKT;IAJjC,IAAA,QAAQ,cAAA,EACR,gCAAgC,sCAAA,EAChC,KAAK,WAAA,EACF,SAAS,cAJ8B,yDAK3C,CADa;IAEZ,IAAM,oCAAoC,GAAG,IAAA,mBAAW,EACtD,UAAC,KAA4B;;QAC3B,IAAA,6CAAqB,GAAE,CAAC;QACxB,OAAO,MAAA,gCAAgC,aAAhC,gCAAgC,uBAAhC,gCAAgC,CAAG,KAAK,CAAC,mCAAI,KAAK,CAAC;IAC5D,CAAC,EACD,CAAC,gCAAgC,CAAC,CACnC,CAAC;IAEF,OAAO,CACL,uBAAC,mBAAI,eACC,SAAS,IACb,gCAAgC,EAAE,oCAAoC,EACtE,KAAK,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,YAE/B,QAAQ,IACJ,CACR,CAAC;AACJ,CAAC;AAED,IAAM,MAAM,GAAG,yBAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,IAAI,EAAE,CAAC;KACR;CACF,CAAC,CAAC","sourcesContent":["import { type PropsWithChildren, useCallback } from 'react';\nimport { type GestureResponderEvent, StyleSheet, View, type ViewProps } from 'react-native';\n\nimport { notifySessionActivity } from './directSessionActivity';\n\nexport type FaroSessionActivityBoundaryProps = PropsWithChildren<ViewProps>;\n\n/**\n * Records touch activity for the React Native subtree without emitting a user\n * action or taking ownership of the responder.\n */\nexport function FaroSessionActivityBoundary({\n children,\n onStartShouldSetResponderCapture,\n style,\n ...viewProps\n}: FaroSessionActivityBoundaryProps) {\n const handleStartShouldSetResponderCapture = useCallback(\n (event: GestureResponderEvent): boolean => {\n notifySessionActivity();\n return onStartShouldSetResponderCapture?.(event) ?? false;\n },\n [onStartShouldSetResponderCapture]\n );\n\n return (\n <View\n {...viewProps}\n onStartShouldSetResponderCapture={handleStartShouldSetResponderCapture}\n style={[styles.container, style]}\n >\n {children}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n});\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clearDirectSessionActivityHandler = clearDirectSessionActivityHandler;
|
|
4
|
+
exports.registerDirectSessionActivityHandler = registerDirectSessionActivityHandler;
|
|
5
|
+
exports.notifySessionActivity = notifySessionActivity;
|
|
6
|
+
var faro_core_1 = require("@grafana/faro-core");
|
|
7
|
+
var directSessionActivityHandler;
|
|
8
|
+
/** Clears a handler left by a previous SDK initialization. */
|
|
9
|
+
function clearDirectSessionActivityHandler() {
|
|
10
|
+
directSessionActivityHandler = undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Registers the active session manager as the direct-activity consumer. */
|
|
13
|
+
function registerDirectSessionActivityHandler(handler) {
|
|
14
|
+
directSessionActivityHandler = handler;
|
|
15
|
+
return function () {
|
|
16
|
+
if (directSessionActivityHandler === handler) {
|
|
17
|
+
directSessionActivityHandler = undefined;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Refreshes session inactivity without emitting telemetry.
|
|
23
|
+
*
|
|
24
|
+
* This is a no-op until session tracking has initialized. Use it for direct
|
|
25
|
+
* interactions that do not pass through `FaroSessionActivityBoundary`.
|
|
26
|
+
*/
|
|
27
|
+
function notifySessionActivity() {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
try {
|
|
30
|
+
directSessionActivityHandler === null || directSessionActivityHandler === void 0 ? void 0 : directSessionActivityHandler();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// Session tracking must never interrupt the interaction being observed.
|
|
34
|
+
(_b = (_a = faro_core_1.faro.unpatchedConsole) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, 'Failed to record session activity:', error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=directSessionActivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directSessionActivity.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/directSessionActivity.ts"],"names":[],"mappings":";;AAOA,8EAEC;AAGD,oFAQC;AAQD,sDAOC;AAnCD,gDAA0C;AAI1C,IAAI,4BAAsE,CAAC;AAE3E,8DAA8D;AAC9D,SAAgB,iCAAiC;IAC/C,4BAA4B,GAAG,SAAS,CAAC;AAC3C,CAAC;AAED,4EAA4E;AAC5E,SAAgB,oCAAoC,CAAC,OAAqC;IACxF,4BAA4B,GAAG,OAAO,CAAC;IAEvC,OAAO;QACL,IAAI,4BAA4B,KAAK,OAAO,EAAE,CAAC;YAC7C,4BAA4B,GAAG,SAAS,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,qBAAqB;;IACnC,IAAI,CAAC;QACH,4BAA4B,aAA5B,4BAA4B,uBAA5B,4BAA4B,EAAI,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,wEAAwE;QACxE,MAAA,MAAA,gBAAI,CAAC,gBAAgB,0CAAE,IAAI,mDAAG,oCAAoC,EAAE,KAAK,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC","sourcesContent":["import { faro } from '@grafana/faro-core';\n\ntype DirectSessionActivityHandler = () => void;\n\nlet directSessionActivityHandler: DirectSessionActivityHandler | undefined;\n\n/** Clears a handler left by a previous SDK initialization. */\nexport function clearDirectSessionActivityHandler(): void {\n directSessionActivityHandler = undefined;\n}\n\n/** Registers the active session manager as the direct-activity consumer. */\nexport function registerDirectSessionActivityHandler(handler: DirectSessionActivityHandler): () => void {\n directSessionActivityHandler = handler;\n\n return () => {\n if (directSessionActivityHandler === handler) {\n directSessionActivityHandler = undefined;\n }\n };\n}\n\n/**\n * Refreshes session inactivity without emitting telemetry.\n *\n * This is a no-op until session tracking has initialized. Use it for direct\n * interactions that do not pass through `FaroSessionActivityBoundary`.\n */\nexport function notifySessionActivity(): void {\n try {\n directSessionActivityHandler?.();\n } catch (error) {\n // Session tracking must never interrupt the interaction being observed.\n faro.unpatchedConsole?.warn?.('Failed to record session activity:', error);\n }\n}\n"]}
|