@grafana/faro-react-native 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +118 -36
- package/android/consumer-proguard-rules.pro +2 -0
- package/android/src/main/java/com/grafana/faro/reactnative/ANRTracker.kt +61 -9
- package/android/src/main/java/com/grafana/faro/reactnative/AnrThreadDumpParser.kt +70 -0
- package/android/src/main/java/com/grafana/faro/reactnative/ApplicationExitTraceReader.kt +142 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrCache.kt +163 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrReporter.kt +163 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +243 -50
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +75 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroNativeCrashTrace.kt +39 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +99 -8
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativePackage.kt +1 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroUncaughtExceptionHandler.kt +62 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneBacktraceFormatter.kt +117 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneModels.kt +35 -0
- package/android/src/main/java/com/grafana/faro/reactnative/TombstoneWireParser.kt +235 -0
- package/android/src/main/proto/tombstone.proto +62 -0
- package/android/src/test/java/com/grafana/faro/reactnative/AnrStackFiltersTest.kt +28 -0
- package/android/src/test/java/com/grafana/faro/reactnative/AnrThreadDumpParserTest.kt +38 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroAnrCacheTest.kt +81 -0
- package/android/src/test/java/com/grafana/faro/reactnative/TombstoneWireParserTest.kt +51 -0
- package/dist/cjs/config/getRNInstrumentations.js +3 -1
- package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
- package/dist/cjs/config/makeRNConfig.js +48 -9
- package/dist/cjs/config/makeRNConfig.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/initialize.js +14 -8
- package/dist/cjs/initialize.js.map +1 -1
- package/dist/cjs/instrumentations/anr/anrStack.js +19 -0
- package/dist/cjs/instrumentations/anr/anrStack.js.map +1 -0
- package/dist/cjs/instrumentations/anr/instrumentation.js +374 -72
- package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/anr/types.js.map +1 -1
- package/dist/cjs/instrumentations/console/index.js +5 -1
- package/dist/cjs/instrumentations/console/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +110 -103
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +41 -0
- package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +87 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js +123 -0
- package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/index.js +12 -0
- package/dist/cjs/instrumentations/crashReporting/android/index.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js +11 -0
- package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +181 -0
- package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/index.js +35 -2
- package/dist/cjs/instrumentations/crashReporting/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +78 -0
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js +103 -0
- package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/index.js +11 -0
- package/dist/cjs/instrumentations/crashReporting/ios/index.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js +48 -0
- package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/cjs/instrumentations/errors/index.js +2 -1
- package/dist/cjs/instrumentations/errors/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/installationId.js +91 -0
- package/dist/cjs/instrumentations/session/installationId.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionAttributes.js +152 -89
- package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/cjs/metas/appBuildIdentity.js +95 -0
- package/dist/cjs/metas/appBuildIdentity.js.map +1 -0
- package/dist/cjs/testUtils/mockDeviceInfo.js +5 -0
- package/dist/cjs/testUtils/mockDeviceInfo.js.map +1 -1
- package/dist/cjs/testUtils/setupTests.js +7 -0
- package/dist/cjs/testUtils/setupTests.js.map +1 -1
- package/dist/esm/config/getRNInstrumentations.js +3 -1
- package/dist/esm/config/getRNInstrumentations.js.map +1 -1
- package/dist/esm/config/makeRNConfig.js +49 -10
- package/dist/esm/config/makeRNConfig.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/initialize.js +11 -5
- package/dist/esm/initialize.js.map +1 -1
- package/dist/esm/instrumentations/anr/anrStack.js +16 -0
- package/dist/esm/instrumentations/anr/anrStack.js.map +1 -0
- package/dist/esm/instrumentations/anr/instrumentation.js +260 -67
- package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/anr/types.js.map +1 -1
- package/dist/esm/instrumentations/console/index.js +5 -1
- package/dist/esm/instrumentations/console/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +76 -94
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +18 -0
- package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +56 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js +116 -0
- package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/index.js +4 -0
- package/dist/esm/instrumentations/crashReporting/android/index.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js +8 -0
- package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +174 -0
- package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/index.js +35 -1
- package/dist/esm/instrumentations/crashReporting/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +56 -0
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js +98 -0
- package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/index.js +4 -0
- package/dist/esm/instrumentations/crashReporting/ios/index.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js +44 -0
- package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/esm/instrumentations/errors/index.js +2 -1
- package/dist/esm/instrumentations/errors/index.js.map +1 -1
- package/dist/esm/instrumentations/session/installationId.js +48 -0
- package/dist/esm/instrumentations/session/installationId.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionAttributes.js +83 -38
- package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/esm/metas/appBuildIdentity.js +56 -0
- package/dist/esm/metas/appBuildIdentity.js.map +1 -0
- package/dist/esm/testUtils/mockDeviceInfo.js +5 -0
- package/dist/esm/testUtils/mockDeviceInfo.js.map +1 -1
- package/dist/esm/testUtils/setupTests.js +7 -0
- package/dist/esm/testUtils/setupTests.js.map +1 -1
- package/dist/types/config/makeRNConfig.d.ts +4 -3
- package/dist/types/config/types.d.ts +2 -1
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/initialize.d.ts +4 -2
- package/dist/types/instrumentations/anr/anrStack.d.ts +7 -0
- package/dist/types/instrumentations/anr/instrumentation.d.ts +23 -28
- package/dist/types/instrumentations/anr/types.d.ts +5 -1
- package/dist/types/instrumentations/appState/index.d.ts +1 -1
- package/dist/types/instrumentations/console/index.d.ts +1 -1
- package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +56 -0
- package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +12 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +26 -0
- package/dist/types/instrumentations/crashReporting/android/crashErrorMessage.d.ts +23 -0
- package/dist/types/instrumentations/crashReporting/android/index.d.ts +4 -0
- package/dist/types/instrumentations/crashReporting/android/nativeTombstoneTrace.d.ts +2 -0
- package/dist/types/instrumentations/crashReporting/android/parseAndroidCrashTrace.d.ts +45 -0
- package/dist/types/instrumentations/crashReporting/index.d.ts +25 -2
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +28 -0
- package/dist/types/instrumentations/crashReporting/ios/crashErrorMessage.d.ts +14 -0
- package/dist/types/instrumentations/crashReporting/ios/index.d.ts +4 -0
- package/dist/types/instrumentations/crashReporting/ios/parseIosCrashTrace.d.ts +38 -0
- package/dist/types/instrumentations/crashReporting/types.d.ts +18 -51
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/http/index.d.ts +1 -1
- package/dist/types/instrumentations/performance/index.d.ts +1 -1
- package/dist/types/instrumentations/session/index.d.ts +1 -1
- package/dist/types/instrumentations/session/installationId.d.ts +1 -0
- package/dist/types/instrumentations/session/sessionAttributes.d.ts +13 -4
- package/dist/types/instrumentations/startup/index.d.ts +1 -1
- package/dist/types/instrumentations/userActions/index.d.ts +1 -1
- package/dist/types/instrumentations/view/index.d.ts +1 -1
- package/dist/types/instrumentations/xhr/index.d.ts +1 -1
- package/dist/types/metas/appBuildIdentity.d.ts +16 -0
- package/dist/types/testUtils/mockFaroReactNativeTracing.d.ts +1 -1
- package/dist/types/transports/console/transport.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +1 -1
- package/dist/types/transports/offline/transport.d.ts +1 -1
- package/package.json +4 -4
- package/src/config/getRNInstrumentations.ts +5 -1
- package/src/config/makeRNConfig.test.ts +59 -3
- package/src/config/makeRNConfig.ts +48 -9
- package/src/config/types.ts +2 -1
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/index.ts +2 -1
- package/src/initialize.test.ts +71 -15
- package/src/initialize.ts +11 -5
- package/src/instrumentations/anr/anrStack.test.ts +27 -0
- package/src/instrumentations/anr/anrStack.ts +19 -0
- package/src/instrumentations/anr/instrumentation.test.ts +45 -0
- package/src/instrumentations/anr/instrumentation.ts +301 -67
- package/src/instrumentations/anr/types.ts +7 -1
- package/src/instrumentations/console/index.ts +5 -1
- package/src/instrumentations/console/instrumentation.test.ts +5 -0
- package/src/instrumentations/crashReporting/{instrumentation.ts → BaseCrashReportingInstrumentation.ts} +119 -93
- package/src/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.ts +23 -0
- package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +62 -0
- package/src/instrumentations/crashReporting/android/crashErrorMessage.test.ts +182 -0
- package/src/instrumentations/crashReporting/android/crashErrorMessage.ts +135 -0
- package/src/instrumentations/crashReporting/android/index.ts +4 -0
- package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.test.ts +19 -0
- package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.ts +8 -0
- package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.test.ts +232 -0
- package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.ts +235 -0
- package/src/instrumentations/crashReporting/index.ts +43 -2
- package/src/instrumentations/crashReporting/instrumentation.test.ts +42 -0
- package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +62 -0
- package/src/instrumentations/crashReporting/ios/crashErrorMessage.test.ts +170 -0
- package/src/instrumentations/crashReporting/ios/crashErrorMessage.ts +111 -0
- package/src/instrumentations/crashReporting/ios/index.ts +4 -0
- package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.test.ts +86 -0
- package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.ts +83 -0
- package/src/instrumentations/crashReporting/types.ts +19 -58
- package/src/instrumentations/errors/index.ts +2 -1
- package/src/instrumentations/errors/instrumentation.test.ts +8 -2
- package/src/instrumentations/errors/stackTraceParser.test.ts +1 -1
- package/src/instrumentations/session/installationId.ts +43 -0
- package/src/instrumentations/session/sessionAttributes.test.ts +260 -30
- package/src/instrumentations/session/sessionAttributes.ts +112 -22
- package/src/metas/appBuildIdentity.test.ts +48 -0
- package/src/metas/appBuildIdentity.ts +50 -0
- package/src/testUtils/mockDeviceInfo.ts +5 -0
- package/src/testUtils/setupTests.ts +7 -0
- package/dist/cjs/instrumentations/crashReporting/instrumentation.js.map +0 -1
- package/dist/esm/instrumentations/crashReporting/instrumentation.js.map +0 -1
- package/dist/types/instrumentations/crashReporting/instrumentation.d.ts +0 -58
|
@@ -4,7 +4,7 @@ import type { ConsoleTransportOptions } from './types';
|
|
|
4
4
|
export declare class ConsoleTransport extends BaseTransport {
|
|
5
5
|
private options;
|
|
6
6
|
readonly name = "@grafana/faro-react-native:transport-console";
|
|
7
|
-
readonly version = "2.
|
|
7
|
+
readonly version = "2.8.2";
|
|
8
8
|
constructor(options?: ConsoleTransportOptions);
|
|
9
9
|
send(item: TransportItem): void;
|
|
10
10
|
}
|
|
@@ -4,7 +4,7 @@ import type { FetchTransportOptions } from './types';
|
|
|
4
4
|
export declare class FetchTransport extends BaseTransport {
|
|
5
5
|
private options;
|
|
6
6
|
readonly name = "@grafana/faro-react-native:transport-fetch";
|
|
7
|
-
readonly version = "2.
|
|
7
|
+
readonly version = "2.8.2";
|
|
8
8
|
promiseBuffer: PromiseBuffer<Response | void>;
|
|
9
9
|
private readonly rateLimitBackoffMs;
|
|
10
10
|
private readonly getNow;
|
|
@@ -31,7 +31,7 @@ import type { OfflineTransportOptions } from './types';
|
|
|
31
31
|
*/
|
|
32
32
|
export declare class OfflineTransport extends BaseTransport {
|
|
33
33
|
readonly name = "@grafana/faro-react-native:transport-offline";
|
|
34
|
-
readonly version = "2.
|
|
34
|
+
readonly version = "2.8.2";
|
|
35
35
|
private readonly cache;
|
|
36
36
|
private readonly connectivityService;
|
|
37
37
|
private readonly maxCacheDurationMs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/faro-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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.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
|
},
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"@types/react": "19.2.15",
|
|
80
80
|
"@types/react-native": "0.73.0",
|
|
81
81
|
"react-native-mmkv": "4.3.1",
|
|
82
|
-
"react-native-nitro-modules": "0.
|
|
82
|
+
"react-native-nitro-modules": "0.36.1"
|
|
83
83
|
},
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"access": "public"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "fed3084557bb9c34129fff2b190ad4758fad7daf"
|
|
88
88
|
}
|
|
@@ -144,7 +144,11 @@ export function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}):
|
|
|
144
144
|
|
|
145
145
|
// Crash reporting
|
|
146
146
|
if (enableCrashReporting) {
|
|
147
|
-
instrumentations.push(
|
|
147
|
+
instrumentations.push(
|
|
148
|
+
new CrashReportingInstrumentation({
|
|
149
|
+
releaseBundleFilename: config.releaseBundleFilename,
|
|
150
|
+
})
|
|
151
|
+
);
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
if (enableTracing) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { BaseInstrumentation, VERSION } from '@grafana/faro-core';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
minimalSessionDeviceAttributes,
|
|
5
|
+
type PreloadedMobileMeta,
|
|
6
|
+
} from '../instrumentations/session/sessionAttributes';
|
|
4
7
|
import { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';
|
|
5
8
|
import { ConsoleTransport } from '../transports/console';
|
|
6
9
|
import { FetchTransport } from '../transports/fetch';
|
|
@@ -116,16 +119,69 @@ describe('makeRNConfig', () => {
|
|
|
116
119
|
expect(minimal.instrumentations?.map((i) => i.name)).toEqual(direct.map((i) => i.name));
|
|
117
120
|
});
|
|
118
121
|
|
|
119
|
-
it('passes preloaded session device attributes to core config
|
|
122
|
+
it('passes preloaded mobile meta and flat session device attributes to core config', () => {
|
|
120
123
|
const preloaded = minimalSessionDeviceAttributes();
|
|
121
124
|
preloaded.device_id = 'test-preloaded-id';
|
|
122
|
-
const
|
|
125
|
+
const preloadedMobileMeta: PreloadedMobileMeta = {
|
|
126
|
+
sessionAttributes: preloaded,
|
|
127
|
+
meta: {
|
|
128
|
+
app: {
|
|
129
|
+
installationId: 'test-installation-id',
|
|
130
|
+
},
|
|
131
|
+
device: {
|
|
132
|
+
is_physical: true,
|
|
133
|
+
model_identifier: 'iPhone16,1',
|
|
134
|
+
},
|
|
135
|
+
os: {
|
|
136
|
+
name: 'iOS',
|
|
137
|
+
version: '17.0',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const cfg = makeRNConfig({ ...base }, preloadedMobileMeta);
|
|
143
|
+
|
|
144
|
+
expect(cfg.app.name).toBe('test-app');
|
|
145
|
+
expect(cfg.app.installationId).toBe('test-installation-id');
|
|
146
|
+
expect(cfg.preloadedMobileMeta).toEqual(preloadedMobileMeta.meta);
|
|
123
147
|
expect(
|
|
124
148
|
(cfg as { preloadedSessionDeviceAttributes?: { device_id: string } }).preloadedSessionDeviceAttributes
|
|
125
149
|
).toBeDefined();
|
|
126
150
|
expect(
|
|
127
151
|
(cfg as { preloadedSessionDeviceAttributes?: { device_id: string } }).preloadedSessionDeviceAttributes?.device_id
|
|
128
152
|
).toBe('test-preloaded-id');
|
|
153
|
+
expect(cfg.metas).toContainEqual({
|
|
154
|
+
device: {
|
|
155
|
+
is_physical: true,
|
|
156
|
+
model_identifier: 'iPhone16,1',
|
|
157
|
+
},
|
|
158
|
+
os: {
|
|
159
|
+
name: 'iOS',
|
|
160
|
+
version: '17.0',
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('keeps explicit config app installationId over preloaded installationId', () => {
|
|
166
|
+
const cfg = makeRNConfig(
|
|
167
|
+
{
|
|
168
|
+
...base,
|
|
169
|
+
app: {
|
|
170
|
+
...base.app,
|
|
171
|
+
installationId: 'explicit-installation-id',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
sessionAttributes: minimalSessionDeviceAttributes(),
|
|
176
|
+
meta: {
|
|
177
|
+
app: {
|
|
178
|
+
installationId: 'preloaded-installation-id',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(cfg.app.installationId).toBe('explicit-installation-id');
|
|
129
185
|
});
|
|
130
186
|
|
|
131
187
|
it('uses releaseBundleFilename in default parseStacktrace when set', () => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defaultGlobalObjectKey, defaultUnpatchedConsole } from '@grafana/faro-core';
|
|
2
|
-
import type { Config } from '@grafana/faro-core';
|
|
2
|
+
import type { Config, MetaApp } from '@grafana/faro-core';
|
|
3
3
|
|
|
4
4
|
import { getStackFramesFromError } from '../instrumentations/errors/stackTraceParser';
|
|
5
|
-
import type {
|
|
5
|
+
import type { PreloadedMobileMeta } from '../instrumentations/session/sessionAttributes';
|
|
6
6
|
import { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';
|
|
7
7
|
import { InternalLoggerLevel, LogLevel } from '../internalLogger';
|
|
8
|
+
import { getMetroInjectedBundleId } from '../metas/appBuildIdentity';
|
|
8
9
|
import { getPageMeta } from '../metas/page';
|
|
9
10
|
import { getScreenMeta } from '../metas/screen';
|
|
10
11
|
import { getSdkMeta } from '../metas/sdk';
|
|
@@ -17,6 +18,28 @@ import type { ReactNativeConfig, ReactNativeFullConfig } from './types';
|
|
|
17
18
|
|
|
18
19
|
const DEFAULT_OFFLINE_CACHE_MS = 3 * 24 * 60 * 60 * 1000; // 3 days
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Resolves `meta.app.bundleId` when it is not already supplied by faro-core from
|
|
23
|
+
* the Metro preamble (`registerInitialMetas` / `__faroBundleId_<appName>`).
|
|
24
|
+
*
|
|
25
|
+
* Priority: explicit `config.app.bundleId` → Metro preamble (core) → DeviceInfo
|
|
26
|
+
* fallback (`applicationId@versionCode@versionName`, same shape Gradle/Metro use).
|
|
27
|
+
*/
|
|
28
|
+
function resolveAppBundleId(
|
|
29
|
+
configApp: MetaApp | undefined,
|
|
30
|
+
appName: string | undefined,
|
|
31
|
+
appSymbolsBundleId?: string
|
|
32
|
+
): string | undefined {
|
|
33
|
+
if (configApp?.bundleId) {
|
|
34
|
+
return configApp.bundleId;
|
|
35
|
+
}
|
|
36
|
+
const metroId = getMetroInjectedBundleId(appName);
|
|
37
|
+
if (metroId) {
|
|
38
|
+
return metroId;
|
|
39
|
+
}
|
|
40
|
+
return appSymbolsBundleId;
|
|
41
|
+
}
|
|
42
|
+
|
|
20
43
|
/**
|
|
21
44
|
* Builds transports. FetchTransport is always added when url is provided.
|
|
22
45
|
* User can enable offline and console via enableTransports.
|
|
@@ -77,22 +100,38 @@ function createParseStacktrace(releaseBundleFilename: string | undefined): Confi
|
|
|
77
100
|
* Based on flags, builds instrumentations and transports automatically.
|
|
78
101
|
* Client just enables what they need; makeRNConfig does the rest.
|
|
79
102
|
*
|
|
80
|
-
* @param
|
|
103
|
+
* @param preloadedMobileMeta Device/session fields and structured mobile meta (passed from async `initializeFaro`).
|
|
104
|
+
* @param appSymbolsBundleId Encoded `meta.app.bundleId` for server-side symbol retrace.
|
|
81
105
|
*/
|
|
82
106
|
export function makeRNConfig(
|
|
83
107
|
config: ReactNativeConfig,
|
|
84
|
-
|
|
108
|
+
preloadedMobileMeta?: PreloadedMobileMeta,
|
|
109
|
+
appSymbolsBundleId?: string
|
|
85
110
|
): ReactNativeFullConfig {
|
|
86
|
-
const
|
|
111
|
+
const { app: preloadedAppMeta, ...structuredMobileMeta } = preloadedMobileMeta?.meta ?? {};
|
|
112
|
+
const mobileMetas = Object.keys(structuredMobileMeta).length > 0 ? [structuredMobileMeta] : [];
|
|
113
|
+
const defaultMetas = [getSdkMeta(), getPageMeta(), getScreenMeta(), ...mobileMetas];
|
|
87
114
|
const customMetas = config.metas ?? [];
|
|
88
115
|
const transports = buildTransports(config);
|
|
89
116
|
const instrumentations = buildInstrumentations(config);
|
|
117
|
+
const installationId = config.app.installationId ?? preloadedAppMeta?.installationId;
|
|
90
118
|
|
|
91
119
|
const releaseBundleFilename = config.releaseBundleFilename;
|
|
120
|
+
// Merge bundleId (from config, Metro, or DeviceInfo) with preloaded app meta
|
|
121
|
+
const bundleIdValue = resolveAppBundleId(config.app, config.app?.name, appSymbolsBundleId);
|
|
122
|
+
const appMetasIfPresent =
|
|
123
|
+
preloadedAppMeta || bundleIdValue
|
|
124
|
+
? [{ app: { ...preloadedAppMeta, ...(bundleIdValue && { bundleId: bundleIdValue }) } }]
|
|
125
|
+
: [];
|
|
126
|
+
|
|
92
127
|
return {
|
|
93
|
-
app:
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
app: {
|
|
129
|
+
...config.app,
|
|
130
|
+
...(installationId && { installationId }),
|
|
131
|
+
},
|
|
132
|
+
...(preloadedMobileMeta != null && {
|
|
133
|
+
preloadedMobileMeta: preloadedMobileMeta.meta,
|
|
134
|
+
preloadedSessionDeviceAttributes: preloadedMobileMeta.sessionAttributes,
|
|
96
135
|
}),
|
|
97
136
|
dedupe: config.dedupe ?? true,
|
|
98
137
|
globalObjectKey: config.globalObjectKey ?? defaultGlobalObjectKey,
|
|
@@ -111,7 +150,7 @@ export function makeRNConfig(
|
|
|
111
150
|
...defaultSessionTrackingConfig,
|
|
112
151
|
...config.sessionTracking,
|
|
113
152
|
},
|
|
114
|
-
metas: [...defaultMetas, ...customMetas],
|
|
153
|
+
metas: [...defaultMetas, ...customMetas, ...appMetasIfPresent],
|
|
115
154
|
instrumentations,
|
|
116
155
|
transports,
|
|
117
156
|
ignoreUrls: config.ignoreUrls ?? [],
|
package/src/config/types.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Config } from '@grafana/faro-core';
|
|
|
2
2
|
|
|
3
3
|
import type { ANRInstrumentationOptions } from '../instrumentations/anr';
|
|
4
4
|
import type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';
|
|
5
|
-
import type { SessionAttributes } from '../instrumentations/session/sessionAttributes';
|
|
5
|
+
import type { PreloadedMobileMeta, SessionAttributes } from '../instrumentations/session/sessionAttributes';
|
|
6
6
|
|
|
7
7
|
import type { Sampling } from './sampling';
|
|
8
8
|
|
|
@@ -92,5 +92,6 @@ export type ReactNativeConfig = Partial<
|
|
|
92
92
|
* Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.
|
|
93
93
|
*/
|
|
94
94
|
export type ReactNativeFullConfig = Config & {
|
|
95
|
+
preloadedMobileMeta?: PreloadedMobileMeta['meta'];
|
|
95
96
|
preloadedSessionDeviceAttributes?: SessionAttributes;
|
|
96
97
|
};
|
|
@@ -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.3.1' as const;
|
package/src/index.ts
CHANGED
|
@@ -21,10 +21,11 @@ export { ConsoleInstrumentation } from './instrumentations/console';
|
|
|
21
21
|
export { SessionInstrumentation } from './instrumentations/session';
|
|
22
22
|
export {
|
|
23
23
|
getSessionAttributes,
|
|
24
|
+
loadMobileMetaForInit,
|
|
24
25
|
loadSessionDeviceAttributesForInit,
|
|
25
26
|
minimalSessionDeviceAttributes,
|
|
26
27
|
} from './instrumentations/session/sessionAttributes';
|
|
27
|
-
export type { SessionAttributes } from './instrumentations/session/sessionAttributes';
|
|
28
|
+
export type { PreloadedMobileMeta, SessionAttributes } from './instrumentations/session/sessionAttributes';
|
|
28
29
|
export { ViewInstrumentation } from './instrumentations/view';
|
|
29
30
|
export { AppStateInstrumentation } from './instrumentations/appState';
|
|
30
31
|
export { UserActionInstrumentation } from './instrumentations/userActions';
|
package/src/initialize.test.ts
CHANGED
|
@@ -92,21 +92,41 @@ describe('initializeFaro', () => {
|
|
|
92
92
|
).rejects.toThrow('url is required');
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
it('should await
|
|
96
|
-
const spy = jest.spyOn(sessionAttributes, '
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
95
|
+
it('should await mobile meta then merge structured meta and preloaded session attributes', async () => {
|
|
96
|
+
const spy = jest.spyOn(sessionAttributes, 'loadMobileMetaForInit').mockResolvedValue({
|
|
97
|
+
sessionAttributes: {
|
|
98
|
+
react_native_version: '0.0.1',
|
|
99
|
+
device_os: 'iOS',
|
|
100
|
+
device_os_version: '17.0',
|
|
101
|
+
device_os_detail: 'iOS 17.0',
|
|
102
|
+
device_manufacturer: 'apple',
|
|
103
|
+
device_model: 'Test Phone',
|
|
104
|
+
device_model_name: "Test's iPhone",
|
|
105
|
+
device_brand: 'Apple',
|
|
106
|
+
device_is_physical: 'true',
|
|
107
|
+
device_id: 'preloaded-device-id',
|
|
108
|
+
device_type: 'mobile',
|
|
109
|
+
device_memory_total: '100',
|
|
110
|
+
device_memory_used: '50',
|
|
111
|
+
},
|
|
112
|
+
meta: {
|
|
113
|
+
app: {
|
|
114
|
+
installationId: 'preloaded-installation-id',
|
|
115
|
+
},
|
|
116
|
+
device: {
|
|
117
|
+
brand: 'iPhone',
|
|
118
|
+
is_physical: true,
|
|
119
|
+
manufacturer: 'apple',
|
|
120
|
+
model_identifier: 'test-model-identifier',
|
|
121
|
+
model_name: 'Test Phone',
|
|
122
|
+
type: 'mobile',
|
|
123
|
+
},
|
|
124
|
+
os: {
|
|
125
|
+
detail: 'iOS 17.0',
|
|
126
|
+
name: 'iOS',
|
|
127
|
+
version: '17.0',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
110
130
|
});
|
|
111
131
|
|
|
112
132
|
const faro = await initializeFaro(
|
|
@@ -125,6 +145,9 @@ describe('initializeFaro', () => {
|
|
|
125
145
|
expect(faro.metas.value.sdk?.name).toBe('faro-react-native');
|
|
126
146
|
expect(faro.metas.value.sdk?.version).toBe(VERSION);
|
|
127
147
|
expect(faro.metas.value.sdk?.integrations).toEqual([{ name: packageJson.name, version: packageJson.version }]);
|
|
148
|
+
expect(faro.metas.value.app?.installationId).toBe('preloaded-installation-id');
|
|
149
|
+
expect(faro.metas.value.device?.model_identifier).toBe('test-model-identifier');
|
|
150
|
+
expect(faro.metas.value.os?.name).toBe('iOS');
|
|
128
151
|
expect(faro.metas.value.session?.attributes?.['react_native_version']).toBe('0.0.1');
|
|
129
152
|
expect(faro.metas.value.session?.attributes?.['device_id']).toBe('preloaded-device-id');
|
|
130
153
|
|
|
@@ -148,4 +171,37 @@ describe('initializeFaro', () => {
|
|
|
148
171
|
|
|
149
172
|
expect(faro.metas.value.app?.bundleId).toBe('release-bundle-from-metro');
|
|
150
173
|
});
|
|
174
|
+
|
|
175
|
+
it('should keep config.app.bundleId when set explicitly (do not override with symbols id)', async () => {
|
|
176
|
+
const faro = await initializeFaro(
|
|
177
|
+
mockConfig({
|
|
178
|
+
url: 'http://localhost:12345/collect',
|
|
179
|
+
app: { bundleId: 'user-env-bundle-id' },
|
|
180
|
+
transports: [new MockTransport()],
|
|
181
|
+
instrumentations: [new SessionInstrumentation()],
|
|
182
|
+
sessionTracking: {
|
|
183
|
+
enabled: true,
|
|
184
|
+
persistent: false,
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(faro.metas.value.app?.bundleId).toBe('user-env-bundle-id');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should set meta.app.bundleId (encoded build identity) from DeviceInfo when no Metro id', async () => {
|
|
193
|
+
const faro = await initializeFaro(
|
|
194
|
+
mockConfig({
|
|
195
|
+
url: 'http://localhost:12345/collect',
|
|
196
|
+
transports: [new MockTransport()],
|
|
197
|
+
instrumentations: [new SessionInstrumentation()],
|
|
198
|
+
sessionTracking: {
|
|
199
|
+
enabled: true,
|
|
200
|
+
persistent: false,
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expect(faro.metas.value.app?.bundleId).toBe('com.example.myapp@42@1.0.0');
|
|
206
|
+
});
|
|
151
207
|
});
|
package/src/initialize.ts
CHANGED
|
@@ -2,14 +2,20 @@ import { type Faro, initializeFaro as initializeFaroCore } from '@grafana/faro-c
|
|
|
2
2
|
|
|
3
3
|
import { makeRNConfig } from './config/makeRNConfig';
|
|
4
4
|
import type { ReactNativeConfig } from './config/types';
|
|
5
|
-
import {
|
|
5
|
+
import { loadMobileMetaForInit } from './instrumentations/session/sessionAttributes';
|
|
6
|
+
import { loadAppSymbolsBundleIdForInit } from './metas/appBuildIdentity';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Awaits async device/session attribute collection
|
|
9
|
-
*
|
|
9
|
+
* Awaits async device/session attribute collection and the build identity used
|
|
10
|
+
* for server-side symbol retrace, then initializes Faro with core
|
|
11
|
+
* `initializeFaro`. On failure during collection, uses
|
|
12
|
+
* `minimalSessionDeviceAttributes` / an empty build identity before init.
|
|
10
13
|
*/
|
|
11
14
|
export async function initializeFaro(config: ReactNativeConfig): Promise<Faro> {
|
|
12
|
-
const
|
|
13
|
-
|
|
15
|
+
const [preloadedMobileMeta, appSymbolsBundleId] = await Promise.all([
|
|
16
|
+
loadMobileMetaForInit(),
|
|
17
|
+
loadAppSymbolsBundleIdForInit(),
|
|
18
|
+
]);
|
|
19
|
+
const fullConfig = makeRNConfig(config, preloadedMobileMeta, appSymbolsBundleId);
|
|
14
20
|
return initializeFaroCore(fullConfig);
|
|
15
21
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isInvalidAnrCaptureStack } from './anrStack';
|
|
2
|
+
|
|
3
|
+
describe('isInvalidAnrCaptureStack', () => {
|
|
4
|
+
it('rejects empty stacks', () => {
|
|
5
|
+
expect(isInvalidAnrCaptureStack(undefined)).toBe(true);
|
|
6
|
+
expect(isInvalidAnrCaptureStack('')).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('rejects crash-handler contamination', () => {
|
|
10
|
+
const stack = `
|
|
11
|
+
at com.android.internal.os.RuntimeInit$KillApplicationHandler.uncaughtException(RuntimeInit.java:174)
|
|
12
|
+
at com.grafana.faro.reactnative.FaroUncaughtExceptionHandler.uncaughtException(FaroUncaughtExceptionHandler.kt:42)
|
|
13
|
+
`.trim();
|
|
14
|
+
|
|
15
|
+
expect(isInvalidAnrCaptureStack(stack)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('accepts a typical blocked main-thread stack', () => {
|
|
19
|
+
const stack = `
|
|
20
|
+
at com.example.myapp.ui.HomeFragment.loadCatalog(HomeFragment.kt:84)
|
|
21
|
+
at com.example.myapp.ui.HomeFragment.onResume(HomeFragment.kt:52)
|
|
22
|
+
at android.app.Activity.performResume(Activity.java:8911)
|
|
23
|
+
`.trim();
|
|
24
|
+
|
|
25
|
+
expect(isInvalidAnrCaptureStack(stack)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when an ANR stack was sampled on the crash-killer path instead of
|
|
3
|
+
* the blocked main thread (common when ANRTracker fires during process teardown).
|
|
4
|
+
*
|
|
5
|
+
* Uses only Android framework and Faro SDK frame names — no app-specific identifiers.
|
|
6
|
+
*/
|
|
7
|
+
export function isInvalidAnrCaptureStack(stack: string | undefined): boolean {
|
|
8
|
+
if (!stack?.trim()) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const normalized = stack.toLowerCase();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
normalized.includes('handleapplicationcrash') ||
|
|
16
|
+
normalized.includes('killapplicationhandler') ||
|
|
17
|
+
normalized.includes('farouncaughtexceptionhandler')
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ExceptionEvent, initializeFaro, type TransportItem } from '@grafana/faro-core';
|
|
2
|
+
import { mockConfig, MockTransport } from '@grafana/faro-test-utils';
|
|
3
|
+
|
|
4
|
+
import { ANRInstrumentation } from './instrumentation';
|
|
5
|
+
|
|
6
|
+
describe('ANRInstrumentation', () => {
|
|
7
|
+
it('should report ANRs as fatal exceptions', async () => {
|
|
8
|
+
const transport = new MockTransport();
|
|
9
|
+
const instrumentation = new ANRInstrumentation();
|
|
10
|
+
const nativeModule = {
|
|
11
|
+
getANRStatus: jest.fn(() =>
|
|
12
|
+
Promise.resolve([
|
|
13
|
+
JSON.stringify({
|
|
14
|
+
duration: 5000,
|
|
15
|
+
stacktrace: 'main thread stack',
|
|
16
|
+
timestamp: 1710000000000,
|
|
17
|
+
type: 'ANR',
|
|
18
|
+
}),
|
|
19
|
+
])
|
|
20
|
+
),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
initializeFaro(
|
|
24
|
+
mockConfig({
|
|
25
|
+
transports: [transport],
|
|
26
|
+
instrumentations: [instrumentation],
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await (
|
|
31
|
+
instrumentation as unknown as {
|
|
32
|
+
checkANRStatus: (nativeModule: typeof nativeModule) => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
).checkANRStatus(nativeModule);
|
|
35
|
+
|
|
36
|
+
const item = transport.items.find((transportItem) => {
|
|
37
|
+
return (transportItem.payload as { type?: string }).type === 'ANR';
|
|
38
|
+
}) as TransportItem<ExceptionEvent>;
|
|
39
|
+
|
|
40
|
+
expect(item.payload.type).toBe('ANR');
|
|
41
|
+
expect(item.payload.fatal).toBe(true);
|
|
42
|
+
expect(item.payload.context?.mechanism).toBe('anr');
|
|
43
|
+
expect(item.payload.context?.stacktrace).toBe('main thread stack');
|
|
44
|
+
});
|
|
45
|
+
});
|