@grafana/faro-react-native 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/android/build.gradle +118 -36
  2. package/android/consumer-proguard-rules.pro +2 -0
  3. package/android/src/main/java/com/grafana/faro/reactnative/ANRTracker.kt +61 -9
  4. package/android/src/main/java/com/grafana/faro/reactnative/AnrThreadDumpParser.kt +70 -0
  5. package/android/src/main/java/com/grafana/faro/reactnative/ApplicationExitTraceReader.kt +142 -0
  6. package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrCache.kt +163 -0
  7. package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrReporter.kt +163 -0
  8. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +243 -50
  9. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +75 -0
  10. package/android/src/main/java/com/grafana/faro/reactnative/FaroNativeCrashTrace.kt +39 -0
  11. package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +99 -8
  12. package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativePackage.kt +1 -0
  13. package/android/src/main/java/com/grafana/faro/reactnative/FaroUncaughtExceptionHandler.kt +62 -0
  14. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneBacktraceFormatter.kt +117 -0
  15. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneModels.kt +35 -0
  16. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneWireParser.kt +235 -0
  17. package/android/src/main/proto/tombstone.proto +62 -0
  18. package/android/src/test/java/com/grafana/faro/reactnative/AnrStackFiltersTest.kt +28 -0
  19. package/android/src/test/java/com/grafana/faro/reactnative/AnrThreadDumpParserTest.kt +38 -0
  20. package/android/src/test/java/com/grafana/faro/reactnative/FaroAnrCacheTest.kt +81 -0
  21. package/android/src/test/java/com/grafana/faro/reactnative/TombstoneWireParserTest.kt +51 -0
  22. package/dist/cjs/config/getRNInstrumentations.js +3 -1
  23. package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
  24. package/dist/cjs/config/makeRNConfig.js +48 -9
  25. package/dist/cjs/config/makeRNConfig.js.map +1 -1
  26. package/dist/cjs/config/types.js.map +1 -1
  27. package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
  28. package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
  29. package/dist/cjs/index.js +3 -2
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/initialize.js +14 -8
  32. package/dist/cjs/initialize.js.map +1 -1
  33. package/dist/cjs/instrumentations/anr/anrStack.js +19 -0
  34. package/dist/cjs/instrumentations/anr/anrStack.js.map +1 -0
  35. package/dist/cjs/instrumentations/anr/instrumentation.js +374 -72
  36. package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
  37. package/dist/cjs/instrumentations/anr/types.js.map +1 -1
  38. package/dist/cjs/instrumentations/console/index.js +5 -1
  39. package/dist/cjs/instrumentations/console/index.js.map +1 -1
  40. package/dist/cjs/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +110 -103
  41. package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
  42. package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +41 -0
  43. package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
  44. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +87 -0
  45. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
  46. package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js +123 -0
  47. package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
  48. package/dist/cjs/instrumentations/crashReporting/android/index.js +12 -0
  49. package/dist/cjs/instrumentations/crashReporting/android/index.js.map +1 -0
  50. package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js +11 -0
  51. package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
  52. package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +181 -0
  53. package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
  54. package/dist/cjs/instrumentations/crashReporting/index.js +35 -2
  55. package/dist/cjs/instrumentations/crashReporting/index.js.map +1 -1
  56. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +78 -0
  57. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
  58. package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js +103 -0
  59. package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
  60. package/dist/cjs/instrumentations/crashReporting/ios/index.js +11 -0
  61. package/dist/cjs/instrumentations/crashReporting/ios/index.js.map +1 -0
  62. package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js +48 -0
  63. package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
  64. package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
  65. package/dist/cjs/instrumentations/errors/index.js +2 -1
  66. package/dist/cjs/instrumentations/errors/index.js.map +1 -1
  67. package/dist/cjs/instrumentations/session/installationId.js +91 -0
  68. package/dist/cjs/instrumentations/session/installationId.js.map +1 -0
  69. package/dist/cjs/instrumentations/session/sessionAttributes.js +152 -89
  70. package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
  71. package/dist/cjs/metas/appBuildIdentity.js +95 -0
  72. package/dist/cjs/metas/appBuildIdentity.js.map +1 -0
  73. package/dist/cjs/testUtils/mockDeviceInfo.js +5 -0
  74. package/dist/cjs/testUtils/mockDeviceInfo.js.map +1 -1
  75. package/dist/cjs/testUtils/setupTests.js +7 -0
  76. package/dist/cjs/testUtils/setupTests.js.map +1 -1
  77. package/dist/esm/config/getRNInstrumentations.js +3 -1
  78. package/dist/esm/config/getRNInstrumentations.js.map +1 -1
  79. package/dist/esm/config/makeRNConfig.js +49 -10
  80. package/dist/esm/config/makeRNConfig.js.map +1 -1
  81. package/dist/esm/config/types.js.map +1 -1
  82. package/dist/esm/generated/faroRNPackageMeta.js +1 -1
  83. package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
  84. package/dist/esm/index.js +1 -1
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/initialize.js +11 -5
  87. package/dist/esm/initialize.js.map +1 -1
  88. package/dist/esm/instrumentations/anr/anrStack.js +16 -0
  89. package/dist/esm/instrumentations/anr/anrStack.js.map +1 -0
  90. package/dist/esm/instrumentations/anr/instrumentation.js +260 -67
  91. package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
  92. package/dist/esm/instrumentations/anr/types.js.map +1 -1
  93. package/dist/esm/instrumentations/console/index.js +5 -1
  94. package/dist/esm/instrumentations/console/index.js.map +1 -1
  95. package/dist/esm/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +76 -94
  96. package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
  97. package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +18 -0
  98. package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
  99. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +56 -0
  100. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
  101. package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js +116 -0
  102. package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
  103. package/dist/esm/instrumentations/crashReporting/android/index.js +4 -0
  104. package/dist/esm/instrumentations/crashReporting/android/index.js.map +1 -0
  105. package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js +8 -0
  106. package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
  107. package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +174 -0
  108. package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
  109. package/dist/esm/instrumentations/crashReporting/index.js +35 -1
  110. package/dist/esm/instrumentations/crashReporting/index.js.map +1 -1
  111. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +56 -0
  112. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
  113. package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js +98 -0
  114. package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
  115. package/dist/esm/instrumentations/crashReporting/ios/index.js +4 -0
  116. package/dist/esm/instrumentations/crashReporting/ios/index.js.map +1 -0
  117. package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js +44 -0
  118. package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
  119. package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
  120. package/dist/esm/instrumentations/errors/index.js +2 -1
  121. package/dist/esm/instrumentations/errors/index.js.map +1 -1
  122. package/dist/esm/instrumentations/session/installationId.js +48 -0
  123. package/dist/esm/instrumentations/session/installationId.js.map +1 -0
  124. package/dist/esm/instrumentations/session/sessionAttributes.js +83 -38
  125. package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
  126. package/dist/esm/metas/appBuildIdentity.js +56 -0
  127. package/dist/esm/metas/appBuildIdentity.js.map +1 -0
  128. package/dist/esm/testUtils/mockDeviceInfo.js +5 -0
  129. package/dist/esm/testUtils/mockDeviceInfo.js.map +1 -1
  130. package/dist/esm/testUtils/setupTests.js +7 -0
  131. package/dist/esm/testUtils/setupTests.js.map +1 -1
  132. package/dist/types/config/makeRNConfig.d.ts +4 -3
  133. package/dist/types/config/types.d.ts +2 -1
  134. package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
  135. package/dist/types/index.d.ts +2 -2
  136. package/dist/types/initialize.d.ts +4 -2
  137. package/dist/types/instrumentations/anr/anrStack.d.ts +7 -0
  138. package/dist/types/instrumentations/anr/instrumentation.d.ts +23 -28
  139. package/dist/types/instrumentations/anr/types.d.ts +5 -1
  140. package/dist/types/instrumentations/appState/index.d.ts +1 -1
  141. package/dist/types/instrumentations/console/index.d.ts +1 -1
  142. package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +56 -0
  143. package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +12 -0
  144. package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +26 -0
  145. package/dist/types/instrumentations/crashReporting/android/crashErrorMessage.d.ts +23 -0
  146. package/dist/types/instrumentations/crashReporting/android/index.d.ts +4 -0
  147. package/dist/types/instrumentations/crashReporting/android/nativeTombstoneTrace.d.ts +2 -0
  148. package/dist/types/instrumentations/crashReporting/android/parseAndroidCrashTrace.d.ts +45 -0
  149. package/dist/types/instrumentations/crashReporting/index.d.ts +25 -2
  150. package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +28 -0
  151. package/dist/types/instrumentations/crashReporting/ios/crashErrorMessage.d.ts +14 -0
  152. package/dist/types/instrumentations/crashReporting/ios/index.d.ts +4 -0
  153. package/dist/types/instrumentations/crashReporting/ios/parseIosCrashTrace.d.ts +38 -0
  154. package/dist/types/instrumentations/crashReporting/types.d.ts +18 -51
  155. package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +1 -1
  156. package/dist/types/instrumentations/http/index.d.ts +1 -1
  157. package/dist/types/instrumentations/performance/index.d.ts +1 -1
  158. package/dist/types/instrumentations/session/index.d.ts +1 -1
  159. package/dist/types/instrumentations/session/installationId.d.ts +1 -0
  160. package/dist/types/instrumentations/session/sessionAttributes.d.ts +13 -4
  161. package/dist/types/instrumentations/startup/index.d.ts +1 -1
  162. package/dist/types/instrumentations/userActions/index.d.ts +1 -1
  163. package/dist/types/instrumentations/view/index.d.ts +1 -1
  164. package/dist/types/instrumentations/xhr/index.d.ts +1 -1
  165. package/dist/types/metas/appBuildIdentity.d.ts +16 -0
  166. package/dist/types/testUtils/mockFaroReactNativeTracing.d.ts +1 -1
  167. package/dist/types/transports/console/transport.d.ts +1 -1
  168. package/dist/types/transports/fetch/transport.d.ts +1 -1
  169. package/dist/types/transports/offline/transport.d.ts +1 -1
  170. package/ios/faro-upload-composed-source-map.sh +8 -2
  171. package/package.json +5 -5
  172. package/src/config/getRNInstrumentations.ts +5 -1
  173. package/src/config/makeRNConfig.test.ts +59 -3
  174. package/src/config/makeRNConfig.ts +48 -9
  175. package/src/config/types.ts +2 -1
  176. package/src/generated/faroRNPackageMeta.ts +1 -1
  177. package/src/index.ts +2 -1
  178. package/src/initialize.test.ts +71 -15
  179. package/src/initialize.ts +11 -5
  180. package/src/instrumentations/anr/anrStack.test.ts +27 -0
  181. package/src/instrumentations/anr/anrStack.ts +19 -0
  182. package/src/instrumentations/anr/instrumentation.test.ts +45 -0
  183. package/src/instrumentations/anr/instrumentation.ts +301 -67
  184. package/src/instrumentations/anr/types.ts +7 -1
  185. package/src/instrumentations/console/index.ts +5 -1
  186. package/src/instrumentations/console/instrumentation.test.ts +5 -0
  187. package/src/instrumentations/crashReporting/{instrumentation.ts → BaseCrashReportingInstrumentation.ts} +119 -93
  188. package/src/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.ts +23 -0
  189. package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +62 -0
  190. package/src/instrumentations/crashReporting/android/crashErrorMessage.test.ts +182 -0
  191. package/src/instrumentations/crashReporting/android/crashErrorMessage.ts +135 -0
  192. package/src/instrumentations/crashReporting/android/index.ts +4 -0
  193. package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.test.ts +19 -0
  194. package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.ts +8 -0
  195. package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.test.ts +232 -0
  196. package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.ts +235 -0
  197. package/src/instrumentations/crashReporting/index.ts +43 -2
  198. package/src/instrumentations/crashReporting/instrumentation.test.ts +42 -0
  199. package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +62 -0
  200. package/src/instrumentations/crashReporting/ios/crashErrorMessage.test.ts +170 -0
  201. package/src/instrumentations/crashReporting/ios/crashErrorMessage.ts +111 -0
  202. package/src/instrumentations/crashReporting/ios/index.ts +4 -0
  203. package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.test.ts +86 -0
  204. package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.ts +83 -0
  205. package/src/instrumentations/crashReporting/types.ts +19 -58
  206. package/src/instrumentations/errors/index.ts +2 -1
  207. package/src/instrumentations/errors/instrumentation.test.ts +8 -2
  208. package/src/instrumentations/errors/stackTraceParser.test.ts +1 -1
  209. package/src/instrumentations/session/installationId.ts +43 -0
  210. package/src/instrumentations/session/sessionAttributes.test.ts +260 -30
  211. package/src/instrumentations/session/sessionAttributes.ts +112 -22
  212. package/src/metas/appBuildIdentity.test.ts +48 -0
  213. package/src/metas/appBuildIdentity.ts +50 -0
  214. package/src/testUtils/mockDeviceInfo.ts +5 -0
  215. package/src/testUtils/setupTests.ts +7 -0
  216. package/dist/cjs/instrumentations/crashReporting/instrumentation.js.map +0 -1
  217. package/dist/esm/instrumentations/crashReporting/instrumentation.js.map +0 -1
  218. package/dist/types/instrumentations/crashReporting/instrumentation.d.ts +0 -58
@@ -8,42 +8,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { NativeModules } from 'react-native';
11
- import { BaseInstrumentation, VERSION } from '@grafana/faro-core';
12
- import { ErrorMechanism } from '../errors/const';
11
+ import { BaseInstrumentation } from '@grafana/faro-core';
12
+ import { looksLikeNativeTombstoneTrace } from './android/nativeTombstoneTrace';
13
13
  /**
14
- * Crash Reporting Instrumentation (Experimental).
14
+ * Abstract base class for platform-specific crash reporting implementations.
15
15
  *
16
- * Retrieves crash reports from previous app sessions and sends them via Faro API.
17
- *
18
- * **Platform Support**:
19
- * - **Android**: Uses ApplicationExitInfo API (Android 11+ / API 30+)
20
- * Captures: CRASH, CRASH_NATIVE, ANR, LOW_MEMORY, EXCESSIVE_RESOURCE_USAGE
21
- * - **iOS**: Uses PLCrashReporter (requires adding dependency to podspec)
22
- * Captures: Signal crashes (SIGSEGV, SIGABRT, etc.) and Mach exceptions
23
- *
24
- * **Note**: This instrumentation only retrieves crash data from previous sessions.
25
- * The crash itself terminates the app, so the report is sent on next app launch.
26
- *
27
- * @example
28
- * ```typescript
29
- * import { initializeFaro, CrashReportingInstrumentation } from '@grafana/faro-react-native';
30
- *
31
- * initializeFaro({
32
- * url: 'https://collector.example.com',
33
- * instrumentations: [
34
- * new CrashReportingInstrumentation(),
35
- * ],
36
- * });
37
- * ```
16
+ * Defines the common contract that both Android and iOS implementations must follow.
17
+ * Handles the shared logic for retrieving crash reports from native modules and
18
+ * processing them, while delegating platform-specific parsing and validation to subclasses.
38
19
  */
39
- export class CrashReportingInstrumentation extends BaseInstrumentation {
20
+ export class BaseCrashReportingInstrumentation extends BaseInstrumentation {
40
21
  constructor(options = {}) {
41
22
  var _a;
42
23
  super();
43
- this.name = '@grafana/faro-react-native:instrumentation-crash';
44
- this.version = VERSION;
45
24
  this.options = {
46
25
  enabled: (_a = options.enabled) !== null && _a !== void 0 ? _a : true,
26
+ releaseBundleFilename: options.releaseBundleFilename,
47
27
  };
48
28
  }
49
29
  initialize() {
@@ -58,44 +38,28 @@ export class CrashReportingInstrumentation extends BaseInstrumentation {
58
38
  return;
59
39
  }
60
40
  this.logDebug('Initializing crash reporting instrumentation');
61
- // Wait for session attributes to be populated before sending crash reports
62
- // This ensures crash reports include full session metadata
41
+ // Wait for session attributes before sending crash reports
63
42
  yield this.waitForSessionAttributes();
64
- // Get crash reports from previous session
43
+ // Get and process crash reports from previous session
65
44
  yield this.processCrashReports(nativeModule);
66
45
  });
67
46
  }
68
- /**
69
- * Wait for session attributes to be populated.
70
- *
71
- * Crash reports are sent in a new session, so we need to wait for
72
- * SessionInstrumentation to finish collecting device info, OS version, etc.
73
- * before sending the crash report. Otherwise, the crash report will be missing
74
- * session metadata.
75
- *
76
- * This checks for multiple device-specific attributes that are collected
77
- * asynchronously (device_id, device_os_detail, device_model_name) to ensure
78
- * the async getSessionAttributes() call has completed.
79
- *
80
- * @param maxWaitMs Maximum time to wait in milliseconds (default 10000ms)
81
- */
82
47
  waitForSessionAttributes() {
83
48
  return __awaiter(this, arguments, void 0, function* (maxWaitMs = 10000) {
84
49
  var _a, _b, _c, _d, _e, _f;
85
50
  const startTime = Date.now();
86
- const pollInterval = 200; // Check every 200ms
51
+ const pollInterval = 200;
87
52
  let checkCount = 0;
88
53
  while (Date.now() - startTime < maxWaitMs) {
89
54
  checkCount++;
90
55
  try {
91
- // Check if session attributes are populated
92
56
  const sessionAttrs = (_c = (_b = (_a = this.metas) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.session) === null || _c === void 0 ? void 0 : _c.attributes;
93
57
  if (sessionAttrs) {
94
58
  const attrCount = Object.keys(sessionAttrs).length;
95
59
  const attrKeys = JSON.stringify(Object.keys(sessionAttrs));
96
60
  // Look for multiple device-specific attributes that indicate full async collection is done.
97
61
  // getSessionAttributes() collects these asynchronously:
98
- // - device_id (async getDeviceId)
62
+ // - device_id (SDK installation id, kept as a flat attr during migration)
99
63
  // - device_os_detail (async getDeviceOsDetail)
100
64
  // - device_model_name (DeviceInfo.getDeviceNameSync)
101
65
  // All three should be present when collection is complete.
@@ -116,13 +80,10 @@ export class CrashReportingInstrumentation extends BaseInstrumentation {
116
80
  }
117
81
  }
118
82
  catch (error) {
119
- // Continue waiting if we can't access metas yet
120
83
  this.logDebug(`Check #${checkCount}: Error accessing metas: ${error}`);
121
84
  }
122
- // Wait before next check
123
85
  yield new Promise((resolve) => setTimeout(resolve, pollInterval));
124
86
  }
125
- // Timeout reached - log warning but continue
126
87
  const finalAttrs = (_f = (_e = (_d = this.metas) === null || _d === void 0 ? void 0 : _d.value) === null || _e === void 0 ? void 0 : _e.session) === null || _f === void 0 ? void 0 : _f.attributes;
127
88
  const finalAttrCount = finalAttrs ? Object.keys(finalAttrs).length : 0;
128
89
  const finalAttrKeys = finalAttrs ? JSON.stringify(Object.keys(finalAttrs)) : 'none';
@@ -150,21 +111,33 @@ export class CrashReportingInstrumentation extends BaseInstrumentation {
150
111
  return;
151
112
  }
152
113
  this.logDebug(`Processing ${crashReports.length} crash report(s) from previous session`);
114
+ const reportedTimestamps = new Set();
153
115
  for (const crashJson of crashReports) {
154
116
  try {
155
117
  this.logDebug(`Parsing crash report JSON: ${crashJson.substring(0, 200)}...`);
156
118
  const crash = JSON.parse(crashJson);
119
+ if (crash.timestamp && reportedTimestamps.has(crash.timestamp)) {
120
+ this.logDebug(`Skipping duplicate crash report at ${crash.timestamp}`);
121
+ continue;
122
+ }
123
+ if (this.shouldSkipCrashReport(crash)) {
124
+ this.logDebug(`Skipping crash report at ${crash.timestamp} (platform-specific skip logic)`);
125
+ continue;
126
+ }
127
+ if (crash.timestamp) {
128
+ reportedTimestamps.add(crash.timestamp);
129
+ }
157
130
  this.sendCrashReport(crash);
158
131
  }
159
132
  catch (parseError) {
160
- // If parsing fails, still try to report something
161
133
  this.logError('Failed to parse crash report JSON', parseError);
162
134
  this.api.pushError(new Error('Application crash (parse error)'), {
163
135
  context: {
164
- mechanism: ErrorMechanism.CRASH,
136
+ mechanism: 'crash',
165
137
  parseError: String(parseError),
166
- raw: crashJson.substring(0, 500), // Limit size
138
+ raw: crashJson.substring(0, 500),
167
139
  },
140
+ fatal: true,
168
141
  type: 'crash',
169
142
  });
170
143
  }
@@ -176,11 +149,15 @@ export class CrashReportingInstrumentation extends BaseInstrumentation {
176
149
  });
177
150
  }
178
151
  sendCrashReport(crash) {
179
- const errorMessage = this.getErrorMessage(crash);
152
+ // Platform-specific parsing
153
+ const parsedTrace = crash.trace ? this.parseCrashTrace(crash.trace) : null;
154
+ const errorMessage = this.resolveCrashErrorMessage(crash, parsedTrace).trim() || 'Application crash';
155
+ // Use a message-only Error so pushError does not capture the JS reporter stack
180
156
  const error = new Error(errorMessage);
181
- // Build context from crash data (matching Flutter pattern)
157
+ error.stack = undefined;
158
+ // Build context from crash data
182
159
  const context = {
183
- mechanism: ErrorMechanism.CRASH,
160
+ mechanism: 'crash',
184
161
  };
185
162
  if (crash.trace) {
186
163
  context['trace'] = crash.trace;
@@ -203,46 +180,51 @@ export class CrashReportingInstrumentation extends BaseInstrumentation {
203
180
  if (crash.importance !== undefined) {
204
181
  context['importance'] = String(crash.importance);
205
182
  }
206
- // Push as error via Faro API (matching Flutter pattern)
183
+ // Platform-specific context
184
+ this.buildPlatformContext(parsedTrace, context);
185
+ // Platform-specific stack frames
186
+ const stackFrames = this.getStackFrames(parsedTrace);
187
+ // Warnings for missing/unparseable traces
188
+ if (!stackFrames.length && crash.trace) {
189
+ this.logWarn(this.getParseFailureWarning());
190
+ }
191
+ else if (!crash.trace) {
192
+ this.logWarn(this.getTraceUnavailableWarning());
193
+ }
194
+ else if (looksLikeNativeTombstoneTrace(crash.trace)) {
195
+ const framePreview = crash.trace
196
+ .split('\n')
197
+ .map((line) => line.trim())
198
+ .find((line) => /#\d+\s+pc\s+/i.test(line));
199
+ this.logInfo(`[Faro crash native] Sending tombstone trace (${crash.trace.split('\n').length} lines) preview=${framePreview !== null && framePreview !== void 0 ? framePreview : 'n/a'}`);
200
+ }
207
201
  this.api.pushError(error, {
208
- type: 'crash',
202
+ stackFrames,
209
203
  context,
204
+ type: 'crash',
205
+ fatal: true,
210
206
  });
211
- this.logDebug(`Reported crash: ${crash.reason} at ${crash.timestamp}`);
207
+ this.logDebug(`Sent crash report: ${errorMessage}`);
212
208
  }
213
- /**
214
- * Build error message matching Flutter SDK format:
215
- * "{reason}: {description}, status: {status}"
216
- */
217
- getErrorMessage(crash) {
218
- var _a;
219
- const reason = crash.reason || 'UNKNOWN';
220
- const status = (_a = crash.status) !== null && _a !== void 0 ? _a : 0;
221
- let description;
222
- switch (crash.reason) {
223
- case 'ANR':
224
- description = 'Application Not Responding';
225
- break;
226
- case 'CRASH':
227
- description = 'Application crash (Java/Kotlin)';
228
- break;
229
- case 'CRASH_NATIVE':
230
- description = 'Application crash (Native)';
231
- break;
232
- case 'LOW_MEMORY':
233
- description = 'Application terminated due to low memory';
234
- break;
235
- case 'EXCESSIVE_RESOURCE_USAGE':
236
- description = 'Application terminated due to excessive resource usage';
237
- break;
238
- case 'INITIALIZATION_FAILURE':
239
- description = 'Application failed to initialize';
240
- break;
241
- default:
242
- description = crash.description || 'Application crash';
243
- break;
244
- }
245
- return `${reason}: ${description}, status: ${status}`;
209
+ // Logging helpers
210
+ logDebug(message, ...args) {
211
+ var _a, _b;
212
+ (_b = (_a = this.internalLogger) === null || _a === void 0 ? void 0 : _a.debug) === null || _b === void 0 ? void 0 : _b.call(_a, message, ...args);
213
+ }
214
+ logInfo(message, ...args) {
215
+ var _a, _b;
216
+ (_b = (_a = this.internalLogger) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.call(_a, message, ...args);
217
+ }
218
+ logWarn(message, ...args) {
219
+ var _a, _b;
220
+ (_b = (_a = this.internalLogger) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, message, ...args);
221
+ }
222
+ logError(message, ...args) {
223
+ var _a, _b;
224
+ (_b = (_a = this.internalLogger) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, message, ...args);
225
+ }
226
+ unpatch() {
227
+ // No patching to undo
246
228
  }
247
229
  }
248
- //# sourceMappingURL=instrumentation.js.map
230
+ //# sourceMappingURL=BaseCrashReportingInstrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/crashReporting/BaseCrashReportingInstrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModules } from 'react-native';\n\nimport { BaseInstrumentation } from '@grafana/faro-core';\n\nimport { looksLikeNativeTombstoneTrace } from './android/nativeTombstoneTrace';\nimport type { CrashReport, CrashReportingOptions } from './types';\n\n/**\n * Abstract base class for platform-specific crash reporting implementations.\n *\n * Defines the common contract that both Android and iOS implementations must follow.\n * Handles the shared logic for retrieving crash reports from native modules and\n * processing them, while delegating platform-specific parsing and validation to subclasses.\n */\nexport abstract class BaseCrashReportingInstrumentation extends BaseInstrumentation {\n abstract override readonly name: string;\n abstract override readonly version: string;\n\n protected readonly options: { enabled: boolean; releaseBundleFilename?: string };\n\n constructor(options: CrashReportingOptions = {}) {\n super();\n this.options = {\n enabled: options.enabled ?? true,\n releaseBundleFilename: options.releaseBundleFilename,\n };\n }\n\n async initialize(): Promise<void> {\n if (!this.options.enabled) {\n this.logDebug('Crash reporting is disabled');\n return;\n }\n\n const nativeModule = this.getNativeModule();\n if (!nativeModule) {\n this.logWarn('Native module not available for crash reporting');\n return;\n }\n\n this.logDebug('Initializing crash reporting instrumentation');\n\n // Wait for session attributes before sending crash reports\n await this.waitForSessionAttributes();\n\n // Get and process crash reports from previous session\n await this.processCrashReports(nativeModule);\n }\n\n /**\n * Platform-specific: Parse a crash trace string into structured stack frames.\n */\n protected abstract parseCrashTrace(trace: string): any | null;\n\n /**\n * Platform-specific: Resolve human-readable error message from crash data.\n */\n protected abstract resolveCrashErrorMessage(crash: CrashReport, parsedTrace: any | null): string;\n\n /**\n * Platform-specific: Determine if a crash report should be skipped.\n */\n protected abstract shouldSkipCrashReport(crash: CrashReport): boolean;\n\n /**\n * Platform-specific: Extract stack frames from parsed trace.\n */\n protected abstract getStackFrames(parsedTrace: any | null): any[];\n\n /**\n * Platform-specific: Build platform-specific context from parsed trace.\n */\n protected abstract buildPlatformContext(parsedTrace: any | null, context: Record<string, string>): void;\n\n /**\n * Platform-specific: Get warning message when trace parsing fails.\n */\n protected abstract getParseFailureWarning(): string;\n\n /**\n * Platform-specific: Get warning message when trace is unavailable.\n */\n protected abstract getTraceUnavailableWarning(): string;\n\n private async waitForSessionAttributes(maxWaitMs = 10000): Promise<void> {\n const startTime = Date.now();\n const pollInterval = 200;\n let checkCount = 0;\n\n while (Date.now() - startTime < maxWaitMs) {\n checkCount++;\n try {\n const sessionAttrs = this.metas?.value?.session?.attributes;\n\n if (sessionAttrs) {\n const attrCount = Object.keys(sessionAttrs).length;\n const attrKeys = JSON.stringify(Object.keys(sessionAttrs));\n\n // Look for multiple device-specific attributes that indicate full async collection is done.\n // getSessionAttributes() collects these asynchronously:\n // - device_id (SDK installation id, kept as a flat attr during migration)\n // - device_os_detail (async getDeviceOsDetail)\n // - device_model_name (DeviceInfo.getDeviceNameSync)\n // All three should be present when collection is complete.\n const hasDeviceId = 'device_id' in sessionAttrs && sessionAttrs['device_id'] !== 'unknown';\n const hasDeviceOsDetail =\n 'device_os_detail' in sessionAttrs && sessionAttrs['device_os_detail'] !== 'unknown';\n const hasDeviceModelName = 'device_model_name' in sessionAttrs;\n\n if (hasDeviceId && hasDeviceOsDetail && hasDeviceModelName) {\n const elapsed = Date.now() - startTime;\n this.logDebug(`Session attributes ready after ${elapsed}ms (${checkCount} checks, ${attrCount} attrs)`);\n return;\n } else {\n this.logDebug(\n `Check #${checkCount}: Found ${attrCount} session attributes: ${attrKeys} but still missing required attrs - device_id:${hasDeviceId}, device_os_detail:${hasDeviceOsDetail}, device_model_name:${hasDeviceModelName}`\n );\n }\n } else {\n this.logDebug(`Check #${checkCount}: No session attributes available yet`);\n }\n } catch (error) {\n this.logDebug(`Check #${checkCount}: Error accessing metas: ${error}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n const finalAttrs = this.metas?.value?.session?.attributes;\n const finalAttrCount = finalAttrs ? Object.keys(finalAttrs).length : 0;\n const finalAttrKeys = finalAttrs ? JSON.stringify(Object.keys(finalAttrs)) : 'none';\n this.logWarn(\n `Session attributes not ready after ${maxWaitMs}ms timeout (${checkCount} checks, ${finalAttrCount} attrs: ${finalAttrKeys}). Sending crash report anyway.`\n );\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 async processCrashReports(nativeModule: typeof NativeModules.FaroReactNativeModule): Promise<void> {\n try {\n if (typeof nativeModule.getCrashReport !== 'function') {\n this.logDebug('getCrashReport method not available');\n return;\n }\n\n this.logDebug('Attempting to retrieve crash reports from native module');\n const crashReports = (await nativeModule.getCrashReport()) as string[] | null;\n\n if (!crashReports || crashReports.length === 0) {\n this.logDebug('No crash reports from previous session');\n return;\n }\n\n this.logDebug(`Processing ${crashReports.length} crash report(s) from previous session`);\n\n const reportedTimestamps = new Set<number>();\n\n for (const crashJson of crashReports) {\n try {\n this.logDebug(`Parsing crash report JSON: ${crashJson.substring(0, 200)}...`);\n const crash = JSON.parse(crashJson) as CrashReport;\n\n if (crash.timestamp && reportedTimestamps.has(crash.timestamp)) {\n this.logDebug(`Skipping duplicate crash report at ${crash.timestamp}`);\n continue;\n }\n\n if (this.shouldSkipCrashReport(crash)) {\n this.logDebug(`Skipping crash report at ${crash.timestamp} (platform-specific skip logic)`);\n continue;\n }\n\n if (crash.timestamp) {\n reportedTimestamps.add(crash.timestamp);\n }\n\n this.sendCrashReport(crash);\n } catch (parseError) {\n this.logError('Failed to parse crash report JSON', parseError);\n this.api.pushError(new Error('Application crash (parse error)'), {\n context: {\n mechanism: 'crash',\n parseError: String(parseError),\n raw: crashJson.substring(0, 500),\n },\n fatal: true,\n type: 'crash',\n });\n }\n }\n } catch (error) {\n this.logError('Failed to process crash reports', error);\n }\n }\n\n private sendCrashReport(crash: CrashReport): void {\n // Platform-specific parsing\n const parsedTrace = crash.trace ? this.parseCrashTrace(crash.trace) : null;\n const errorMessage = this.resolveCrashErrorMessage(crash, parsedTrace).trim() || 'Application crash';\n\n // Use a message-only Error so pushError does not capture the JS reporter stack\n const error = new Error(errorMessage);\n error.stack = undefined;\n\n // Build context from crash data\n const context: Record<string, string> = {\n mechanism: 'crash',\n };\n\n if (crash.trace) {\n context['trace'] = crash.trace;\n }\n if (crash.signal) {\n context['signal'] = crash.signal;\n }\n if (crash.timestamp) {\n context['timestamp'] = String(crash.timestamp);\n }\n if (crash.description) {\n context['description'] = crash.description;\n }\n if (crash.processName) {\n context['processName'] = crash.processName;\n }\n if (crash.pid) {\n context['pid'] = String(crash.pid);\n }\n if (crash.importance !== undefined) {\n context['importance'] = String(crash.importance);\n }\n\n // Platform-specific context\n this.buildPlatformContext(parsedTrace, context);\n\n // Platform-specific stack frames\n const stackFrames = this.getStackFrames(parsedTrace);\n\n // Warnings for missing/unparseable traces\n if (!stackFrames.length && crash.trace) {\n this.logWarn(this.getParseFailureWarning());\n } else if (!crash.trace) {\n this.logWarn(this.getTraceUnavailableWarning());\n } else if (looksLikeNativeTombstoneTrace(crash.trace)) {\n const framePreview = crash.trace\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => /#\\d+\\s+pc\\s+/i.test(line));\n this.logInfo(\n `[Faro crash native] Sending tombstone trace (${crash.trace.split('\\n').length} lines) preview=${framePreview ?? 'n/a'}`\n );\n }\n\n this.api.pushError(error, {\n stackFrames,\n context,\n type: 'crash',\n fatal: true,\n });\n\n this.logDebug(`Sent crash report: ${errorMessage}`);\n }\n\n // Logging helpers\n override logDebug(message: string, ...args: any[]): void {\n this.internalLogger?.debug?.(message, ...args);\n }\n\n override logInfo(message: string, ...args: any[]): void {\n this.internalLogger?.info?.(message, ...args);\n }\n\n override logWarn(message: string, ...args: any[]): void {\n this.internalLogger?.warn?.(message, ...args);\n }\n\n override logError(message: string, ...args: any[]): void {\n this.internalLogger?.error?.(message, ...args);\n }\n\n unpatch(): void {\n // No patching to undo\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { BaseInstrumentation, VERSION } from '@grafana/faro-core';
2
+ /**
3
+ * Crash reporting stub for platforms other than Android and iOS (e.g. web, macOS, Windows).
4
+ */
5
+ export class NoOpCrashReportingInstrumentation extends BaseInstrumentation {
6
+ constructor(_options = {}) {
7
+ super();
8
+ this.name = '@grafana/faro-react-native:instrumentation-crash-noop';
9
+ this.version = VERSION;
10
+ }
11
+ initialize() {
12
+ this.logDebug('Crash reporting is not supported on this platform');
13
+ }
14
+ unpatch() {
15
+ // No patching to undo
16
+ }
17
+ }
18
+ //# sourceMappingURL=NoOpCrashReportingInstrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoOpCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAIlE;;GAEG;AACH,MAAM,OAAO,iCAAkC,SAAQ,mBAAmB;IAIxE,YAAY,WAAkC,EAAE;QAC9C,KAAK,EAAE,CAAC;QAJD,SAAI,GAAG,uDAAuD,CAAC;QAC/D,YAAO,GAAG,OAAO,CAAC;IAI3B,CAAC;IAED,UAAU;QACR,IAAI,CAAC,QAAQ,CAAC,mDAAmD,CAAC,CAAC;IACrE,CAAC;IAED,OAAO;QACL,sBAAsB;IACxB,CAAC;CACF","sourcesContent":["import { BaseInstrumentation, VERSION } from '@grafana/faro-core';\n\nimport type { CrashReportingOptions } from './types';\n\n/**\n * Crash reporting stub for platforms other than Android and iOS (e.g. web, macOS, Windows).\n */\nexport class NoOpCrashReportingInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-crash-noop';\n readonly version = VERSION;\n\n constructor(_options: CrashReportingOptions = {}) {\n super();\n }\n\n initialize(): void {\n this.logDebug('Crash reporting is not supported on this platform');\n }\n\n unpatch(): void {\n // No patching to undo\n }\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import { VERSION } from '@grafana/faro-core';
2
+ import { BaseCrashReportingInstrumentation } from '../BaseCrashReportingInstrumentation';
3
+ import { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';
4
+ import { parseAndroidCrashTrace } from './parseAndroidCrashTrace';
5
+ /**
6
+ * Android Crash Reporting Instrumentation.
7
+ *
8
+ * Uses ApplicationExitInfo API (Android 11+ / API 30+) to retrieve crash reports
9
+ * from previous sessions. Captures:
10
+ * - CRASH: Java/Kotlin exception crashes
11
+ * - CRASH_NATIVE: Native (NDK) crashes
12
+ * - LOW_MEMORY: App killed due to low memory
13
+ * - EXCESSIVE_RESOURCE_USAGE: App killed due to excessive resource usage
14
+ *
15
+ * ANRs are reported separately by ANRInstrumentation, not via ApplicationExitInfo replay.
16
+ */
17
+ export class AndroidCrashReportingInstrumentation extends BaseCrashReportingInstrumentation {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.name = '@grafana/faro-react-native:instrumentation-crash-android';
21
+ this.version = VERSION;
22
+ }
23
+ parseCrashTrace(trace) {
24
+ return parseAndroidCrashTrace(trace, {
25
+ releaseBundleFilename: this.options.releaseBundleFilename,
26
+ });
27
+ }
28
+ resolveCrashErrorMessage(crash, parsedTrace) {
29
+ return resolveCrashErrorMessage(crash, parsedTrace);
30
+ }
31
+ shouldSkipCrashReport(crash) {
32
+ return shouldSkipCrashReport(crash);
33
+ }
34
+ getStackFrames(parsedTrace) {
35
+ var _a, _b, _c;
36
+ if (!parsedTrace) {
37
+ return [];
38
+ }
39
+ // Android has JS frames, Java/Kotlin frames (R8 retrace), and NDK tombstone rows (.so retrace).
40
+ return [...((_a = parsedTrace.jsFrames) !== null && _a !== void 0 ? _a : []), ...((_b = parsedTrace.frames) !== null && _b !== void 0 ? _b : []), ...((_c = parsedTrace.nativeFrames) !== null && _c !== void 0 ? _c : [])];
41
+ }
42
+ buildPlatformContext(parsedTrace, context) {
43
+ // Store the native Java/Kotlin exception class separately for drill-down
44
+ // Plugin overview metrics key off exception_type = 'crash', so keep that stable
45
+ if (parsedTrace === null || parsedTrace === void 0 ? void 0 : parsedTrace.exceptionType) {
46
+ context['nativeExceptionType'] = parsedTrace.exceptionType;
47
+ }
48
+ }
49
+ getParseFailureWarning() {
50
+ return 'Native crash trace present but no frames parsed; check Android trace format or UncaughtExceptionHandler cache';
51
+ }
52
+ getTraceUnavailableWarning() {
53
+ return 'Native crash trace unavailable (ApplicationExitInfo traceInputStream was null at crash time)';
54
+ }
55
+ }
56
+ //# sourceMappingURL=AndroidCrashReportingInstrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AndroidCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,iCAAiC,EAAE,MAAM,sCAAsC,CAAC;AAGzF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAgC,MAAM,0BAA0B,CAAC;AAEhG;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,oCAAqC,SAAQ,iCAAiC;IAA3F;;QACW,SAAI,GAAG,0DAA0D,CAAC;QAClE,YAAO,GAAG,OAAO,CAAC;IAuC7B,CAAC;IArCW,eAAe,CAAC,KAAa;QACrC,OAAO,sBAAsB,CAAC,KAAK,EAAE;YACnC,qBAAqB,EAAE,IAAI,CAAC,OAAO,CAAC,qBAAqB;SAC1D,CAAC,CAAC;IACL,CAAC;IAES,wBAAwB,CAAC,KAAkB,EAAE,WAA2C;QAChG,OAAO,wBAAwB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAES,qBAAqB,CAAC,KAAkB;QAChD,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAES,cAAc,CAAC,WAA2C;;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,gGAAgG;QAChG,OAAO,CAAC,GAAG,CAAC,MAAA,WAAW,CAAC,QAAQ,mCAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAA,WAAW,CAAC,MAAM,mCAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAA,WAAW,CAAC,YAAY,mCAAI,EAAE,CAAC,CAAC,CAAC;IAC/G,CAAC;IAES,oBAAoB,CAAC,WAA2C,EAAE,OAA+B;QACzG,yEAAyE;QACzE,gFAAgF;QAChF,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,aAAa,EAAE,CAAC;YAC/B,OAAO,CAAC,qBAAqB,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC;QAC7D,CAAC;IACH,CAAC;IAES,sBAAsB;QAC9B,OAAO,+GAA+G,CAAC;IACzH,CAAC;IAES,0BAA0B;QAClC,OAAO,8FAA8F,CAAC;IACxG,CAAC;CACF","sourcesContent":["import { VERSION } from '@grafana/faro-core';\n\nimport { BaseCrashReportingInstrumentation } from '../BaseCrashReportingInstrumentation';\nimport type { CrashReport } from '../types';\n\nimport { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';\nimport { parseAndroidCrashTrace, type ParsedAndroidCrashTrace } from './parseAndroidCrashTrace';\n\n/**\n * Android Crash Reporting Instrumentation.\n *\n * Uses ApplicationExitInfo API (Android 11+ / API 30+) to retrieve crash reports\n * from previous sessions. Captures:\n * - CRASH: Java/Kotlin exception crashes\n * - CRASH_NATIVE: Native (NDK) crashes\n * - LOW_MEMORY: App killed due to low memory\n * - EXCESSIVE_RESOURCE_USAGE: App killed due to excessive resource usage\n *\n * ANRs are reported separately by ANRInstrumentation, not via ApplicationExitInfo replay.\n */\nexport class AndroidCrashReportingInstrumentation extends BaseCrashReportingInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-crash-android';\n readonly version = VERSION;\n\n protected parseCrashTrace(trace: string): ParsedAndroidCrashTrace | null {\n return parseAndroidCrashTrace(trace, {\n releaseBundleFilename: this.options.releaseBundleFilename,\n });\n }\n\n protected resolveCrashErrorMessage(crash: CrashReport, parsedTrace: ParsedAndroidCrashTrace | null): string {\n return resolveCrashErrorMessage(crash, parsedTrace);\n }\n\n protected shouldSkipCrashReport(crash: CrashReport): boolean {\n return shouldSkipCrashReport(crash);\n }\n\n protected getStackFrames(parsedTrace: ParsedAndroidCrashTrace | null): any[] {\n if (!parsedTrace) {\n return [];\n }\n // Android has JS frames, Java/Kotlin frames (R8 retrace), and NDK tombstone rows (.so retrace).\n return [...(parsedTrace.jsFrames ?? []), ...(parsedTrace.frames ?? []), ...(parsedTrace.nativeFrames ?? [])];\n }\n\n protected buildPlatformContext(parsedTrace: ParsedAndroidCrashTrace | null, context: Record<string, string>): void {\n // Store the native Java/Kotlin exception class separately for drill-down\n // Plugin overview metrics key off exception_type = 'crash', so keep that stable\n if (parsedTrace?.exceptionType) {\n context['nativeExceptionType'] = parsedTrace.exceptionType;\n }\n }\n\n protected getParseFailureWarning(): string {\n return 'Native crash trace present but no frames parsed; check Android trace format or UncaughtExceptionHandler cache';\n }\n\n protected getTraceUnavailableWarning(): string {\n return 'Native crash trace unavailable (ApplicationExitInfo traceInputStream was null at crash time)';\n }\n}\n"]}
@@ -0,0 +1,116 @@
1
+ import { isPlausibleJavaExceptionIdentifier } from './parseAndroidCrashTrace';
2
+ /**
3
+ * Human-readable exception value for ApplicationExitInfo crash reports.
4
+ * Priority: parsed trace message → exception class → description → generic fallback.
5
+ */
6
+ export function resolveCrashErrorMessage(crash, parsedTrace) {
7
+ var _a, _b, _c;
8
+ const fromTrace = (_a = parsedTrace === null || parsedTrace === void 0 ? void 0 : parsedTrace.exceptionMessage) === null || _a === void 0 ? void 0 : _a.trim();
9
+ if (fromTrace) {
10
+ return fromTrace;
11
+ }
12
+ const fromType = (_b = parsedTrace === null || parsedTrace === void 0 ? void 0 : parsedTrace.exceptionType) === null || _b === void 0 ? void 0 : _b.trim();
13
+ if (fromType && isPlausibleJavaExceptionIdentifier(fromType)) {
14
+ return fromType;
15
+ }
16
+ const fromDescriptionType = exceptionTypeFromDescription(crash.description);
17
+ if (fromDescriptionType) {
18
+ return fromDescriptionType;
19
+ }
20
+ const fromDescription = (_c = crash.description) === null || _c === void 0 ? void 0 : _c.trim();
21
+ if (fromDescription && !isGenericCrashDescription(fromDescription)) {
22
+ return fromDescription;
23
+ }
24
+ const fallback = buildFallbackCrashMessage(crash);
25
+ return fallback.trim() || crash.reason || 'Application crash';
26
+ }
27
+ const ANR_TIMEOUT_DESCRIPTION_PATTERN = /input dispatching timed out|not responding|application not responding/i;
28
+ /**
29
+ * Android ANR watchdog descriptions from ApplicationExitInfo (any app).
30
+ */
31
+ export function isAnrTimeoutDescription(description) {
32
+ const trimmed = description === null || description === void 0 ? void 0 : description.trim();
33
+ if (!trimmed) {
34
+ return false;
35
+ }
36
+ return ANR_TIMEOUT_DESCRIPTION_PATTERN.test(trimmed);
37
+ }
38
+ /**
39
+ * Skip duplicate ApplicationExitInfo rows that have no stack and no useful title,
40
+ * and ANR incidents mis-tagged as previous-session crashes.
41
+ */
42
+ export function shouldSkipCrashReport(crash) {
43
+ if (isAnrTimeoutDescription(crash.description)) {
44
+ return true;
45
+ }
46
+ return shouldSkipLowSignalCrashReport(crash);
47
+ }
48
+ /**
49
+ * Skip duplicate ApplicationExitInfo rows that have no stack and no useful title.
50
+ * These surface in the plugin as a second generic `crash` row for a single button press.
51
+ */
52
+ export function shouldSkipLowSignalCrashReport(crash) {
53
+ var _a, _b;
54
+ if ((_a = crash.trace) === null || _a === void 0 ? void 0 : _a.trim()) {
55
+ return false;
56
+ }
57
+ const description = (_b = crash.description) === null || _b === void 0 ? void 0 : _b.trim();
58
+ if (description && !isGenericCrashDescription(description)) {
59
+ return false;
60
+ }
61
+ return true;
62
+ }
63
+ /** Extract `java.lang.NullPointerException` from ApplicationExitInfo description text. */
64
+ function exceptionTypeFromDescription(description) {
65
+ var _a;
66
+ const trimmed = description === null || description === void 0 ? void 0 : description.trim();
67
+ if (!trimmed || isGenericCrashDescription(trimmed)) {
68
+ return undefined;
69
+ }
70
+ const match = trimmed.match(/^([\w.$]+)(?::(.*))?$/);
71
+ const candidate = (_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.trim();
72
+ if (!candidate || !isPlausibleJavaExceptionIdentifier(candidate)) {
73
+ return undefined;
74
+ }
75
+ return candidate;
76
+ }
77
+ function isGenericCrashDescription(description) {
78
+ const normalized = description.trim().toLowerCase();
79
+ return (normalized === 'crash' ||
80
+ normalized === 'native crash' ||
81
+ normalized === 'application crash' ||
82
+ normalized === 'application crash (java/kotlin)' ||
83
+ normalized === 'application crash (native)');
84
+ }
85
+ /** Generic fallback when the trace carries no parseable message or type. */
86
+ export function buildFallbackCrashMessage(crash) {
87
+ var _a;
88
+ const reason = crash.reason || 'UNKNOWN';
89
+ const status = (_a = crash.status) !== null && _a !== void 0 ? _a : 0;
90
+ let description;
91
+ switch (crash.reason) {
92
+ case 'ANR':
93
+ description = 'Application Not Responding';
94
+ break;
95
+ case 'CRASH':
96
+ description = 'Application crash (Java/Kotlin)';
97
+ break;
98
+ case 'CRASH_NATIVE':
99
+ description = 'Application crash (Native)';
100
+ break;
101
+ case 'LOW_MEMORY':
102
+ description = 'Application terminated due to low memory';
103
+ break;
104
+ case 'EXCESSIVE_RESOURCE_USAGE':
105
+ description = 'Application terminated due to excessive resource usage';
106
+ break;
107
+ case 'INITIALIZATION_FAILURE':
108
+ description = 'Application failed to initialize';
109
+ break;
110
+ default:
111
+ description = crash.description || 'Application crash';
112
+ break;
113
+ }
114
+ return `${reason}: ${description}, status: ${status}`;
115
+ }
116
+ //# sourceMappingURL=crashErrorMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crashErrorMessage.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/crashErrorMessage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CrashReport } from '../types';\n\nimport { isPlausibleJavaExceptionIdentifier } from './parseAndroidCrashTrace';\nimport type { ParsedAndroidCrashTrace } from './parseAndroidCrashTrace';\n\n/**\n * Human-readable exception value for ApplicationExitInfo crash reports.\n * Priority: parsed trace message → exception class → description → generic fallback.\n */\nexport function resolveCrashErrorMessage(crash: CrashReport, parsedTrace: ParsedAndroidCrashTrace | null): string {\n const fromTrace = parsedTrace?.exceptionMessage?.trim();\n if (fromTrace) {\n return fromTrace;\n }\n\n const fromType = parsedTrace?.exceptionType?.trim();\n if (fromType && isPlausibleJavaExceptionIdentifier(fromType)) {\n return fromType;\n }\n\n const fromDescriptionType = exceptionTypeFromDescription(crash.description);\n if (fromDescriptionType) {\n return fromDescriptionType;\n }\n\n const fromDescription = crash.description?.trim();\n if (fromDescription && !isGenericCrashDescription(fromDescription)) {\n return fromDescription;\n }\n\n const fallback = buildFallbackCrashMessage(crash);\n return fallback.trim() || crash.reason || 'Application crash';\n}\n\nconst ANR_TIMEOUT_DESCRIPTION_PATTERN = /input dispatching timed out|not responding|application not responding/i;\n\n/**\n * Android ANR watchdog descriptions from ApplicationExitInfo (any app).\n */\nexport function isAnrTimeoutDescription(description: string | undefined): boolean {\n const trimmed = description?.trim();\n if (!trimmed) {\n return false;\n }\n return ANR_TIMEOUT_DESCRIPTION_PATTERN.test(trimmed);\n}\n\n/**\n * Skip duplicate ApplicationExitInfo rows that have no stack and no useful title,\n * and ANR incidents mis-tagged as previous-session crashes.\n */\nexport function shouldSkipCrashReport(crash: CrashReport): boolean {\n if (isAnrTimeoutDescription(crash.description)) {\n return true;\n }\n\n return shouldSkipLowSignalCrashReport(crash);\n}\n\n/**\n * Skip duplicate ApplicationExitInfo rows that have no stack and no useful title.\n * These surface in the plugin as a second generic `crash` row for a single button press.\n */\nexport function shouldSkipLowSignalCrashReport(crash: CrashReport): boolean {\n if (crash.trace?.trim()) {\n return false;\n }\n\n const description = crash.description?.trim();\n if (description && !isGenericCrashDescription(description)) {\n return false;\n }\n\n return true;\n}\n\n/** Extract `java.lang.NullPointerException` from ApplicationExitInfo description text. */\nfunction exceptionTypeFromDescription(description: string | undefined): string | undefined {\n const trimmed = description?.trim();\n if (!trimmed || isGenericCrashDescription(trimmed)) {\n return undefined;\n }\n\n const match = trimmed.match(/^([\\w.$]+)(?::(.*))?$/);\n const candidate = match?.[1]?.trim();\n if (!candidate || !isPlausibleJavaExceptionIdentifier(candidate)) {\n return undefined;\n }\n\n return candidate;\n}\n\nfunction isGenericCrashDescription(description: string): boolean {\n const normalized = description.trim().toLowerCase();\n return (\n normalized === 'crash' ||\n normalized === 'native crash' ||\n normalized === 'application crash' ||\n normalized === 'application crash (java/kotlin)' ||\n normalized === 'application crash (native)'\n );\n}\n\n/** Generic fallback when the trace carries no parseable message or type. */\nexport function buildFallbackCrashMessage(crash: CrashReport): string {\n const reason = crash.reason || 'UNKNOWN';\n const status = crash.status ?? 0;\n\n let description: string;\n switch (crash.reason) {\n case 'ANR':\n description = 'Application Not Responding';\n break;\n case 'CRASH':\n description = 'Application crash (Java/Kotlin)';\n break;\n case 'CRASH_NATIVE':\n description = 'Application crash (Native)';\n break;\n case 'LOW_MEMORY':\n description = 'Application terminated due to low memory';\n break;\n case 'EXCESSIVE_RESOURCE_USAGE':\n description = 'Application terminated due to excessive resource usage';\n break;\n case 'INITIALIZATION_FAILURE':\n description = 'Application failed to initialize';\n break;\n default:\n description = crash.description || 'Application crash';\n break;\n }\n\n return `${reason}: ${description}, status: ${status}`;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export { AndroidCrashReportingInstrumentation } from './AndroidCrashReportingInstrumentation';
2
+ export { parseAndroidCrashTrace, normalizeJavaStackTraceForRetrace } from './parseAndroidCrashTrace';
3
+ export { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,sBAAsB,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAErG,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export { AndroidCrashReportingInstrumentation } from './AndroidCrashReportingInstrumentation';\nexport { parseAndroidCrashTrace, normalizeJavaStackTraceForRetrace } from './parseAndroidCrashTrace';\nexport type { ParsedAndroidCrashTrace, AndroidCrashStackFrame } from './parseAndroidCrashTrace';\nexport { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';\n"]}
@@ -0,0 +1,8 @@
1
+ /** Detect Android tombstone backtrace lines used for NDK native retrace. */
2
+ export function looksLikeNativeTombstoneTrace(trace) {
3
+ if (!(trace === null || trace === void 0 ? void 0 : trace.trim())) {
4
+ return false;
5
+ }
6
+ return trace.split('\n').some((line) => /^\s*#\d+\s+pc\s+(?:0x)?[0-9a-fA-F]+\s+\S+/i.test(line));
7
+ }
8
+ //# sourceMappingURL=nativeTombstoneTrace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeTombstoneTrace.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/nativeTombstoneTrace.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,UAAU,6BAA6B,CAAC,KAAyB;IACrE,IAAI,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,EAAE,CAAA,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,4CAA4C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACnG,CAAC","sourcesContent":["/** Detect Android tombstone backtrace lines used for NDK native retrace. */\nexport function looksLikeNativeTombstoneTrace(trace: string | undefined): boolean {\n if (!trace?.trim()) {\n return false;\n }\n\n return trace.split('\\n').some((line) => /^\\s*#\\d+\\s+pc\\s+(?:0x)?[0-9a-fA-F]+\\s+\\S+/i.test(line));\n}\n"]}