@sentry/react-native 5.29.0 → 6.0.0-alpha.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 (280) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/js/client.d.ts +5 -9
  3. package/dist/js/client.d.ts.map +1 -1
  4. package/dist/js/client.js +11 -42
  5. package/dist/js/client.js.map +1 -1
  6. package/dist/js/index.d.ts +5 -10
  7. package/dist/js/index.d.ts.map +1 -1
  8. package/dist/js/index.js +4 -21
  9. package/dist/js/index.js.map +1 -1
  10. package/dist/js/integrations/debugsymbolicator.d.ts +2 -8
  11. package/dist/js/integrations/debugsymbolicator.d.ts.map +1 -1
  12. package/dist/js/integrations/debugsymbolicator.js +1 -9
  13. package/dist/js/integrations/debugsymbolicator.js.map +1 -1
  14. package/dist/js/integrations/default.d.ts.map +1 -1
  15. package/dist/js/integrations/default.js +18 -7
  16. package/dist/js/integrations/default.js.map +1 -1
  17. package/dist/js/integrations/devicecontext.d.ts +2 -8
  18. package/dist/js/integrations/devicecontext.d.ts.map +1 -1
  19. package/dist/js/integrations/devicecontext.js +0 -9
  20. package/dist/js/integrations/devicecontext.js.map +1 -1
  21. package/dist/js/integrations/eventorigin.d.ts +2 -8
  22. package/dist/js/integrations/eventorigin.d.ts.map +1 -1
  23. package/dist/js/integrations/eventorigin.js +0 -8
  24. package/dist/js/integrations/eventorigin.js.map +1 -1
  25. package/dist/js/integrations/expocontext.d.ts +2 -8
  26. package/dist/js/integrations/expocontext.d.ts.map +1 -1
  27. package/dist/js/integrations/expocontext.js +0 -8
  28. package/dist/js/integrations/expocontext.js.map +1 -1
  29. package/dist/js/integrations/exports.d.ts +4 -0
  30. package/dist/js/integrations/exports.d.ts.map +1 -1
  31. package/dist/js/integrations/exports.js +4 -0
  32. package/dist/js/integrations/exports.js.map +1 -1
  33. package/dist/js/integrations/modulesloader.d.ts +2 -8
  34. package/dist/js/integrations/modulesloader.d.ts.map +1 -1
  35. package/dist/js/integrations/modulesloader.js +0 -8
  36. package/dist/js/integrations/modulesloader.js.map +1 -1
  37. package/dist/js/integrations/nativelinkederrors.d.ts +2 -8
  38. package/dist/js/integrations/nativelinkederrors.d.ts.map +1 -1
  39. package/dist/js/integrations/nativelinkederrors.js +0 -8
  40. package/dist/js/integrations/nativelinkederrors.js.map +1 -1
  41. package/dist/js/integrations/reactnativeerrorhandlers.d.ts +2 -8
  42. package/dist/js/integrations/reactnativeerrorhandlers.d.ts.map +1 -1
  43. package/dist/js/integrations/reactnativeerrorhandlers.js +1 -8
  44. package/dist/js/integrations/reactnativeerrorhandlers.js.map +1 -1
  45. package/dist/js/integrations/reactnativeinfo.d.ts +2 -8
  46. package/dist/js/integrations/reactnativeinfo.d.ts.map +1 -1
  47. package/dist/js/integrations/reactnativeinfo.js +0 -8
  48. package/dist/js/integrations/reactnativeinfo.js.map +1 -1
  49. package/dist/js/integrations/release.d.ts +2 -8
  50. package/dist/js/integrations/release.d.ts.map +1 -1
  51. package/dist/js/integrations/release.js +0 -8
  52. package/dist/js/integrations/release.js.map +1 -1
  53. package/dist/js/integrations/rewriteframes.js +1 -1
  54. package/dist/js/integrations/rewriteframes.js.map +1 -1
  55. package/dist/js/integrations/screenshot.d.ts +2 -8
  56. package/dist/js/integrations/screenshot.d.ts.map +1 -1
  57. package/dist/js/integrations/screenshot.js +2 -11
  58. package/dist/js/integrations/screenshot.js.map +1 -1
  59. package/dist/js/integrations/sdkinfo.d.ts +2 -8
  60. package/dist/js/integrations/sdkinfo.d.ts.map +1 -1
  61. package/dist/js/integrations/sdkinfo.js +0 -8
  62. package/dist/js/integrations/sdkinfo.js.map +1 -1
  63. package/dist/js/integrations/spotlight.d.ts +2 -10
  64. package/dist/js/integrations/spotlight.d.ts.map +1 -1
  65. package/dist/js/integrations/spotlight.js +1 -10
  66. package/dist/js/integrations/spotlight.js.map +1 -1
  67. package/dist/js/integrations/viewhierarchy.d.ts +2 -8
  68. package/dist/js/integrations/viewhierarchy.d.ts.map +1 -1
  69. package/dist/js/integrations/viewhierarchy.js +0 -8
  70. package/dist/js/integrations/viewhierarchy.js.map +1 -1
  71. package/dist/js/options.d.ts +43 -0
  72. package/dist/js/options.d.ts.map +1 -1
  73. package/dist/js/options.js.map +1 -1
  74. package/dist/js/profiling/cache.d.ts +1 -1
  75. package/dist/js/profiling/hermes.d.ts +1 -1
  76. package/dist/js/profiling/hermes.d.ts.map +1 -1
  77. package/dist/js/profiling/integration.d.ts +1 -7
  78. package/dist/js/profiling/integration.d.ts.map +1 -1
  79. package/dist/js/profiling/integration.js +39 -25
  80. package/dist/js/profiling/integration.js.map +1 -1
  81. package/dist/js/profiling/utils.js +2 -1
  82. package/dist/js/profiling/utils.js.map +1 -1
  83. package/dist/js/replay/mobilereplay.d.ts +2 -2
  84. package/dist/js/replay/mobilereplay.d.ts.map +1 -1
  85. package/dist/js/replay/mobilereplay.js.map +1 -1
  86. package/dist/js/scopeSync.d.ts +6 -0
  87. package/dist/js/scopeSync.d.ts.map +1 -0
  88. package/dist/js/scopeSync.js +60 -0
  89. package/dist/js/scopeSync.js.map +1 -0
  90. package/dist/js/sdk.d.ts +1 -20
  91. package/dist/js/sdk.d.ts.map +1 -1
  92. package/dist/js/sdk.js +20 -53
  93. package/dist/js/sdk.js.map +1 -1
  94. package/dist/js/tools/sentryMetroSerializer.js.map +1 -1
  95. package/dist/js/touchevents.d.ts +0 -1
  96. package/dist/js/touchevents.d.ts.map +1 -1
  97. package/dist/js/touchevents.js +5 -9
  98. package/dist/js/touchevents.js.map +1 -1
  99. package/dist/js/tracing/gesturetracing.d.ts +1 -6
  100. package/dist/js/tracing/gesturetracing.d.ts.map +1 -1
  101. package/dist/js/tracing/gesturetracing.js +8 -12
  102. package/dist/js/tracing/gesturetracing.js.map +1 -1
  103. package/dist/js/tracing/index.d.ts +6 -7
  104. package/dist/js/tracing/index.d.ts.map +1 -1
  105. package/dist/js/tracing/index.js +4 -7
  106. package/dist/js/tracing/index.js.map +1 -1
  107. package/dist/js/tracing/integrations/appStart.d.ts +39 -0
  108. package/dist/js/tracing/integrations/appStart.d.ts.map +1 -0
  109. package/dist/js/tracing/integrations/appStart.js +301 -0
  110. package/dist/js/tracing/integrations/appStart.js.map +1 -0
  111. package/dist/js/tracing/integrations/nativeFrames.d.ts +20 -0
  112. package/dist/js/tracing/integrations/nativeFrames.d.ts.map +1 -0
  113. package/dist/js/tracing/integrations/nativeFrames.js +256 -0
  114. package/dist/js/tracing/integrations/nativeFrames.js.map +1 -0
  115. package/dist/js/tracing/integrations/stalltracking.d.ts +31 -0
  116. package/dist/js/tracing/integrations/stalltracking.d.ts.map +1 -0
  117. package/dist/js/tracing/integrations/stalltracking.js +236 -0
  118. package/dist/js/tracing/integrations/stalltracking.js.map +1 -0
  119. package/dist/js/tracing/integrations/userInteraction.d.ts +11 -0
  120. package/dist/js/tracing/integrations/userInteraction.d.ts.map +1 -0
  121. package/dist/js/tracing/integrations/userInteraction.js +70 -0
  122. package/dist/js/tracing/integrations/userInteraction.js.map +1 -0
  123. package/dist/js/tracing/onSpanEndUtils.d.ts +17 -0
  124. package/dist/js/tracing/onSpanEndUtils.d.ts.map +1 -0
  125. package/dist/js/tracing/onSpanEndUtils.js +112 -0
  126. package/dist/js/tracing/onSpanEndUtils.js.map +1 -0
  127. package/dist/js/tracing/origin.d.ts +2 -0
  128. package/dist/js/tracing/origin.d.ts.map +1 -0
  129. package/dist/js/tracing/origin.js +2 -0
  130. package/dist/js/tracing/origin.js.map +1 -0
  131. package/dist/js/tracing/reactnativenavigation.d.ts +21 -39
  132. package/dist/js/tracing/reactnativenavigation.d.ts.map +1 -1
  133. package/dist/js/tracing/reactnativenavigation.js +98 -87
  134. package/dist/js/tracing/reactnativenavigation.js.map +1 -1
  135. package/dist/js/tracing/reactnativeprofiler.d.ts.map +1 -1
  136. package/dist/js/tracing/reactnativeprofiler.js +6 -13
  137. package/dist/js/tracing/reactnativeprofiler.js.map +1 -1
  138. package/dist/js/tracing/reactnativetracing.d.ts +44 -160
  139. package/dist/js/tracing/reactnativetracing.d.ts.map +1 -1
  140. package/dist/js/tracing/reactnativetracing.js +51 -481
  141. package/dist/js/tracing/reactnativetracing.js.map +1 -1
  142. package/dist/js/tracing/reactnavigation.d.ts +18 -63
  143. package/dist/js/tracing/reactnavigation.d.ts.map +1 -1
  144. package/dist/js/tracing/reactnavigation.js +200 -205
  145. package/dist/js/tracing/reactnavigation.js.map +1 -1
  146. package/dist/js/tracing/semanticAttributes.d.ts +12 -0
  147. package/dist/js/tracing/semanticAttributes.d.ts.map +1 -0
  148. package/dist/js/tracing/semanticAttributes.js +12 -0
  149. package/dist/js/tracing/semanticAttributes.js.map +1 -0
  150. package/dist/js/tracing/span.d.ts +52 -0
  151. package/dist/js/tracing/span.d.ts.map +1 -0
  152. package/dist/js/tracing/span.js +82 -0
  153. package/dist/js/tracing/span.js.map +1 -0
  154. package/dist/js/tracing/timetodisplay.d.ts.map +1 -1
  155. package/dist/js/tracing/timetodisplay.js +14 -29
  156. package/dist/js/tracing/timetodisplay.js.map +1 -1
  157. package/dist/js/tracing/types.d.ts +2 -9
  158. package/dist/js/tracing/types.d.ts.map +1 -1
  159. package/dist/js/tracing/types.js.map +1 -1
  160. package/dist/js/tracing/utils.d.ts +19 -14
  161. package/dist/js/tracing/utils.d.ts.map +1 -1
  162. package/dist/js/tracing/utils.js +38 -52
  163. package/dist/js/tracing/utils.js.map +1 -1
  164. package/dist/js/transports/encodePolyfill.d.ts +3 -0
  165. package/dist/js/transports/encodePolyfill.d.ts.map +1 -0
  166. package/dist/js/transports/encodePolyfill.js +13 -0
  167. package/dist/js/transports/encodePolyfill.js.map +1 -0
  168. package/dist/js/transports/native.d.ts +2 -2
  169. package/dist/js/transports/native.d.ts.map +1 -1
  170. package/dist/js/transports/native.js +2 -1
  171. package/dist/js/transports/native.js.map +1 -1
  172. package/dist/js/utils/fill.d.ts +7 -0
  173. package/dist/js/utils/fill.d.ts.map +1 -0
  174. package/dist/js/utils/fill.js +9 -0
  175. package/dist/js/utils/fill.js.map +1 -0
  176. package/dist/js/utils/span.d.ts +19 -0
  177. package/dist/js/utils/span.d.ts.map +1 -0
  178. package/dist/js/utils/span.js +29 -0
  179. package/dist/js/utils/span.js.map +1 -0
  180. package/dist/js/vendor/react-native/index.d.ts +1 -1
  181. package/dist/js/vendor/react-native/index.d.ts.map +1 -1
  182. package/dist/js/vendor/react-native/index.js.map +1 -1
  183. package/dist/js/version.d.ts +1 -1
  184. package/dist/js/version.d.ts.map +1 -1
  185. package/dist/js/version.js +1 -1
  186. package/dist/js/version.js.map +1 -1
  187. package/dist/js/wrapper.d.ts.map +1 -1
  188. package/dist/js/wrapper.js +1 -0
  189. package/dist/js/wrapper.js.map +1 -1
  190. package/package.json +10 -12
  191. package/ts3.8/dist/js/client.d.ts +5 -9
  192. package/ts3.8/dist/js/index.d.ts +5 -10
  193. package/ts3.8/dist/js/integrations/debugsymbolicator.d.ts +2 -8
  194. package/ts3.8/dist/js/integrations/devicecontext.d.ts +2 -8
  195. package/ts3.8/dist/js/integrations/eventorigin.d.ts +2 -8
  196. package/ts3.8/dist/js/integrations/expocontext.d.ts +2 -8
  197. package/ts3.8/dist/js/integrations/exports.d.ts +4 -0
  198. package/ts3.8/dist/js/integrations/modulesloader.d.ts +2 -8
  199. package/ts3.8/dist/js/integrations/nativelinkederrors.d.ts +2 -8
  200. package/ts3.8/dist/js/integrations/reactnativeerrorhandlers.d.ts +2 -8
  201. package/ts3.8/dist/js/integrations/reactnativeinfo.d.ts +2 -8
  202. package/ts3.8/dist/js/integrations/release.d.ts +2 -8
  203. package/ts3.8/dist/js/integrations/screenshot.d.ts +2 -8
  204. package/ts3.8/dist/js/integrations/sdkinfo.d.ts +2 -8
  205. package/ts3.8/dist/js/integrations/spotlight.d.ts +2 -10
  206. package/ts3.8/dist/js/integrations/viewhierarchy.d.ts +2 -8
  207. package/ts3.8/dist/js/options.d.ts +43 -0
  208. package/ts3.8/dist/js/profiling/cache.d.ts +1 -1
  209. package/ts3.8/dist/js/profiling/hermes.d.ts +1 -1
  210. package/ts3.8/dist/js/profiling/integration.d.ts +1 -7
  211. package/ts3.8/dist/js/replay/mobilereplay.d.ts +2 -2
  212. package/ts3.8/dist/js/scopeSync.d.ts +6 -0
  213. package/ts3.8/dist/js/sdk.d.ts +1 -20
  214. package/ts3.8/dist/js/touchevents.d.ts +0 -1
  215. package/ts3.8/dist/js/tracing/gesturetracing.d.ts +1 -6
  216. package/ts3.8/dist/js/tracing/index.d.ts +6 -7
  217. package/ts3.8/dist/js/tracing/integrations/appStart.d.ts +39 -0
  218. package/ts3.8/dist/js/tracing/integrations/nativeFrames.d.ts +20 -0
  219. package/ts3.8/dist/js/tracing/integrations/stalltracking.d.ts +31 -0
  220. package/ts3.8/dist/js/tracing/integrations/userInteraction.d.ts +11 -0
  221. package/ts3.8/dist/js/tracing/onSpanEndUtils.d.ts +17 -0
  222. package/ts3.8/dist/js/tracing/origin.d.ts +2 -0
  223. package/ts3.8/dist/js/tracing/reactnativenavigation.d.ts +21 -39
  224. package/ts3.8/dist/js/tracing/reactnativetracing.d.ts +44 -160
  225. package/ts3.8/dist/js/tracing/reactnavigation.d.ts +18 -63
  226. package/ts3.8/dist/js/tracing/semanticAttributes.d.ts +12 -0
  227. package/ts3.8/dist/js/tracing/span.d.ts +52 -0
  228. package/ts3.8/dist/js/tracing/types.d.ts +2 -9
  229. package/ts3.8/dist/js/tracing/utils.d.ts +19 -14
  230. package/ts3.8/dist/js/transports/encodePolyfill.d.ts +3 -0
  231. package/ts3.8/dist/js/transports/native.d.ts +2 -2
  232. package/ts3.8/dist/js/utils/fill.d.ts +7 -0
  233. package/ts3.8/dist/js/utils/span.d.ts +19 -0
  234. package/ts3.8/dist/js/vendor/react-native/index.d.ts +1 -1
  235. package/ts3.8/dist/js/version.d.ts +1 -1
  236. package/dist/js/integrations/index.d.ts +0 -16
  237. package/dist/js/integrations/index.d.ts.map +0 -1
  238. package/dist/js/integrations/index.js +0 -17
  239. package/dist/js/integrations/index.js.map +0 -1
  240. package/dist/js/scope.d.ts +0 -54
  241. package/dist/js/scope.d.ts.map +0 -1
  242. package/dist/js/scope.js +0 -89
  243. package/dist/js/scope.js.map +0 -1
  244. package/dist/js/tracing/addTracingExtensions.d.ts +0 -8
  245. package/dist/js/tracing/addTracingExtensions.d.ts.map +0 -1
  246. package/dist/js/tracing/addTracingExtensions.js +0 -66
  247. package/dist/js/tracing/addTracingExtensions.js.map +0 -1
  248. package/dist/js/tracing/nativeframes.d.ts +0 -60
  249. package/dist/js/tracing/nativeframes.d.ts.map +0 -1
  250. package/dist/js/tracing/nativeframes.js +0 -210
  251. package/dist/js/tracing/nativeframes.js.map +0 -1
  252. package/dist/js/tracing/reactnavigationv4.d.ts +0 -92
  253. package/dist/js/tracing/reactnavigationv4.d.ts.map +0 -1
  254. package/dist/js/tracing/reactnavigationv4.js +0 -229
  255. package/dist/js/tracing/reactnavigationv4.js.map +0 -1
  256. package/dist/js/tracing/routingInstrumentation.d.ts +0 -52
  257. package/dist/js/tracing/routingInstrumentation.d.ts.map +0 -1
  258. package/dist/js/tracing/routingInstrumentation.js +0 -36
  259. package/dist/js/tracing/routingInstrumentation.js.map +0 -1
  260. package/dist/js/tracing/stalltracking.d.ts +0 -99
  261. package/dist/js/tracing/stalltracking.d.ts.map +0 -1
  262. package/dist/js/tracing/stalltracking.js +0 -286
  263. package/dist/js/tracing/stalltracking.js.map +0 -1
  264. package/dist/js/tracing/transaction.d.ts +0 -11
  265. package/dist/js/tracing/transaction.d.ts.map +0 -1
  266. package/dist/js/tracing/transaction.js +0 -37
  267. package/dist/js/tracing/transaction.js.map +0 -1
  268. package/dist/js/transports/TextEncoder.d.ts +0 -3
  269. package/dist/js/transports/TextEncoder.d.ts.map +0 -1
  270. package/dist/js/transports/TextEncoder.js +0 -12
  271. package/dist/js/transports/TextEncoder.js.map +0 -1
  272. package/ts3.8/dist/js/integrations/index.d.ts +0 -16
  273. package/ts3.8/dist/js/scope.d.ts +0 -54
  274. package/ts3.8/dist/js/tracing/addTracingExtensions.d.ts +0 -8
  275. package/ts3.8/dist/js/tracing/nativeframes.d.ts +0 -60
  276. package/ts3.8/dist/js/tracing/reactnavigationv4.d.ts +0 -92
  277. package/ts3.8/dist/js/tracing/routingInstrumentation.d.ts +0 -52
  278. package/ts3.8/dist/js/tracing/stalltracking.d.ts +0 -99
  279. package/ts3.8/dist/js/tracing/transaction.d.ts +0 -11
  280. package/ts3.8/dist/js/transports/TextEncoder.d.ts +0 -3
@@ -0,0 +1,256 @@
1
+ import { __awaiter } from "tslib";
2
+ import { spanToJSON } from '@sentry/core';
3
+ import { logger, timestampInSeconds } from '@sentry/utils';
4
+ import { isRootSpan } from '../../utils/span';
5
+ import { NATIVE } from '../../wrapper';
6
+ /**
7
+ * Timeout from the final native frames fetch to processing the associated transaction.
8
+ * If the transaction is not processed by this time, the native frames will be dropped
9
+ * and not added to the event.
10
+ */
11
+ const FINAL_FRAMES_TIMEOUT_MS = 2000;
12
+ /** The listeners for each native frames response, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */
13
+ const _framesListeners = new Map();
14
+ /** The native frames at the transaction finish time, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */
15
+ const _finishFrames = new Map();
16
+ /**
17
+ * A margin of error of 50ms is allowed for the async native bridge call.
18
+ * Anything larger would reduce the accuracy of our frames measurements.
19
+ */
20
+ const MARGIN_OF_ERROR_SECONDS = 0.05;
21
+ /**
22
+ * Instrumentation to add native slow/frozen frames measurements onto transactions.
23
+ */
24
+ export const nativeFramesIntegration = () => {
25
+ const name = 'NativeFrames';
26
+ /** The native frames at the finish time of the most recent span. */
27
+ let _lastSpanFinishFrames = undefined;
28
+ const _spanToNativeFramesAtStartMap = new Map();
29
+ /**
30
+ * Hooks into the client start and end span events.
31
+ */
32
+ const setup = (client) => {
33
+ const { enableNativeFramesTracking } = client.getOptions();
34
+ if (enableNativeFramesTracking && !NATIVE.enableNative) {
35
+ // Do not enable native frames tracking if native is not available.
36
+ logger.warn('[ReactNativeTracing] NativeFramesTracking is not available on the Web, Expo Go and other platforms without native modules.');
37
+ return;
38
+ }
39
+ if (!enableNativeFramesTracking && NATIVE.enableNative) {
40
+ // Disable native frames tracking when native available and option is false.
41
+ NATIVE.disableNativeFramesTracking();
42
+ return;
43
+ }
44
+ if (!enableNativeFramesTracking) {
45
+ return;
46
+ }
47
+ NATIVE.enableNativeFramesTracking();
48
+ client.on('spanStart', _onSpanStart);
49
+ client.on('spanEnd', _onSpanFinish);
50
+ logger.log('[ReactNativeTracing] Native frames instrumentation initialized.');
51
+ };
52
+ /**
53
+ * Adds frames measurements to an event. Called from a valid event processor.
54
+ * Awaits for finish frames if needed.
55
+ */
56
+ const processEvent = (event) => {
57
+ return _processEvent(event);
58
+ };
59
+ /**
60
+ * Fetches the native frames in background if the given span is a root span.
61
+ *
62
+ * @param {Span} rootSpan - The span that has started.
63
+ */
64
+ const _onSpanStart = (rootSpan) => {
65
+ if (!isRootSpan(rootSpan)) {
66
+ return;
67
+ }
68
+ logger.debug(`[NativeFrames] Fetching frames for root span start (${rootSpan.spanContext().spanId}).`);
69
+ NATIVE.fetchNativeFrames()
70
+ .then(frames => {
71
+ if (!frames) {
72
+ logger.warn(`[NativeFrames] Fetched frames for root span start (${rootSpan.spanContext().spanId}), but no frames were returned.`);
73
+ return;
74
+ }
75
+ _spanToNativeFramesAtStartMap.set(rootSpan.spanContext().traceId, frames);
76
+ })
77
+ .then(undefined, error => {
78
+ logger.error(`[NativeFrames] Error while fetching frames for root span start (${rootSpan.spanContext().spanId})`, error);
79
+ });
80
+ };
81
+ /**
82
+ * Called on a span finish to fetch native frames to support transactions with trimEnd.
83
+ * Only to be called when a span does not have an end timestamp.
84
+ */
85
+ const _onSpanFinish = (span) => {
86
+ if (isRootSpan(span)) {
87
+ return _onTransactionFinish(span);
88
+ }
89
+ const timestamp = timestampInSeconds();
90
+ void NATIVE.fetchNativeFrames()
91
+ .then(frames => {
92
+ if (!frames) {
93
+ return;
94
+ }
95
+ _lastSpanFinishFrames = {
96
+ timestamp,
97
+ nativeFrames: frames,
98
+ };
99
+ })
100
+ .then(undefined, error => {
101
+ logger.error(`[NativeFrames] Error while fetching frames for child span end.`, error);
102
+ });
103
+ };
104
+ /**
105
+ * To be called when a transaction is finished
106
+ */
107
+ const _onTransactionFinish = (span) => {
108
+ _fetchFramesForTransaction(span).then(undefined, (reason) => {
109
+ logger.error(`[NativeFrames] Error while fetching frames for root span start (${span.spanContext().spanId})`, reason);
110
+ });
111
+ };
112
+ /**
113
+ * Returns the computed frames measurements and awaits for them if they are not ready yet.
114
+ */
115
+ const _getFramesMeasurements = (traceId, finalEndTimestamp, startFrames) => {
116
+ if (_finishFrames.has(traceId)) {
117
+ logger.debug(`[NativeFrames] Native end frames already fetched for trace id (${traceId}).`);
118
+ return Promise.resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));
119
+ }
120
+ return new Promise(resolve => {
121
+ const timeout = setTimeout(() => {
122
+ logger.debug(`[NativeFrames] Native end frames listener removed by timeout for trace id (${traceId}).`);
123
+ _framesListeners.delete(traceId);
124
+ resolve(null);
125
+ }, 2000);
126
+ _framesListeners.set(traceId, () => {
127
+ logger.debug(`[NativeFrames] Native end frames listener called for trace id (${traceId}).`);
128
+ resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));
129
+ clearTimeout(timeout);
130
+ _framesListeners.delete(traceId);
131
+ });
132
+ });
133
+ };
134
+ /**
135
+ * Returns the computed frames measurements given ready data
136
+ */
137
+ const _prepareMeasurements = (traceId, finalEndTimestamp, // The actual transaction finish time.
138
+ startFrames) => {
139
+ let finalFinishFrames;
140
+ const finish = _finishFrames.get(traceId);
141
+ if (finish &&
142
+ finish.nativeFrames &&
143
+ // Must be in the margin of error of the actual transaction finish time (finalEndTimestamp)
144
+ Math.abs(finish.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS) {
145
+ logger.debug(`[NativeFrames] Using frames from root span end (traceId, ${traceId}).`);
146
+ finalFinishFrames = finish.nativeFrames;
147
+ }
148
+ else if (_lastSpanFinishFrames &&
149
+ Math.abs(_lastSpanFinishFrames.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS) {
150
+ // Fallback to the last span finish if it is within the margin of error of the actual finish timestamp.
151
+ // This should be the case for trimEnd.
152
+ logger.debug(`[NativeFrames] Using native frames from last span end (traceId, ${traceId}).`);
153
+ finalFinishFrames = _lastSpanFinishFrames.nativeFrames;
154
+ }
155
+ else {
156
+ logger.warn(`[NativeFrames] Frames were collected within larger than margin of error delay for traceId (${traceId}). Dropping the inaccurate values.`);
157
+ return null;
158
+ }
159
+ const measurements = {
160
+ frames_total: {
161
+ value: finalFinishFrames.totalFrames - startFrames.totalFrames,
162
+ unit: 'none',
163
+ },
164
+ frames_frozen: {
165
+ value: finalFinishFrames.frozenFrames - startFrames.frozenFrames,
166
+ unit: 'none',
167
+ },
168
+ frames_slow: {
169
+ value: finalFinishFrames.slowFrames - startFrames.slowFrames,
170
+ unit: 'none',
171
+ },
172
+ };
173
+ if (measurements.frames_frozen.value <= 0 &&
174
+ measurements.frames_slow.value <= 0 &&
175
+ measurements.frames_total.value <= 0) {
176
+ logger.warn(`[NativeFrames] Detected zero slow or frozen frames. Not adding measurements to traceId (${traceId}).`);
177
+ return null;
178
+ }
179
+ return measurements;
180
+ };
181
+ /**
182
+ * Fetch finish frames for a transaction at the current time. Calls any awaiting listeners.
183
+ */
184
+ const _fetchFramesForTransaction = (span) => __awaiter(void 0, void 0, void 0, function* () {
185
+ var _a;
186
+ const traceId = spanToJSON(span).trace_id;
187
+ if (!traceId) {
188
+ return;
189
+ }
190
+ const startFrames = _spanToNativeFramesAtStartMap.get(span.spanContext().traceId);
191
+ // This timestamp marks when the finish frames were retrieved. It should be pretty close to the transaction finish.
192
+ const timestamp = timestampInSeconds();
193
+ let finishFrames = null;
194
+ if (startFrames) {
195
+ finishFrames = yield NATIVE.fetchNativeFrames();
196
+ }
197
+ _finishFrames.set(traceId, {
198
+ nativeFrames: finishFrames,
199
+ timestamp,
200
+ });
201
+ (_a = _framesListeners.get(traceId)) === null || _a === void 0 ? void 0 : _a();
202
+ setTimeout(() => _cancelEndFrames(span), FINAL_FRAMES_TIMEOUT_MS);
203
+ });
204
+ /**
205
+ * On a finish frames failure, we cancel the await.
206
+ */
207
+ const _cancelEndFrames = (span) => {
208
+ const spanJSON = spanToJSON(span);
209
+ const traceId = spanJSON.trace_id;
210
+ if (!traceId) {
211
+ return;
212
+ }
213
+ if (_finishFrames.has(traceId)) {
214
+ _finishFrames.delete(traceId);
215
+ logger.log(`[NativeFrames] Native frames timed out for ${spanJSON.op} transaction ${spanJSON.description}. Not adding native frames measurements.`);
216
+ }
217
+ };
218
+ /**
219
+ * Adds frames measurements to an event. Called from a valid event processor.
220
+ * Awaits for finish frames if needed.
221
+ */
222
+ const _processEvent = (event) => __awaiter(void 0, void 0, void 0, function* () {
223
+ var _b;
224
+ if (event.type !== 'transaction' ||
225
+ !event.transaction ||
226
+ !event.contexts ||
227
+ !event.contexts.trace ||
228
+ !event.timestamp ||
229
+ !event.contexts.trace.trace_id) {
230
+ return event;
231
+ }
232
+ const traceOp = event.contexts.trace.op;
233
+ const traceId = event.contexts.trace.trace_id;
234
+ const startFrames = _spanToNativeFramesAtStartMap.get(traceId);
235
+ _spanToNativeFramesAtStartMap.delete(traceId);
236
+ if (!startFrames) {
237
+ logger.warn(`[NativeFrames] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but it already ended.`);
238
+ return event;
239
+ }
240
+ const measurements = yield _getFramesMeasurements(traceId, event.timestamp, startFrames);
241
+ if (!measurements) {
242
+ logger.log(`[NativeFrames] Could not fetch native frames for ${traceOp} transaction ${event.transaction}. Not adding native frames measurements.`);
243
+ return event;
244
+ }
245
+ logger.log(`[Measurements] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(measurements, undefined, 2)}`);
246
+ event.measurements = Object.assign(Object.assign({}, ((_b = event.measurements) !== null && _b !== void 0 ? _b : {})), measurements);
247
+ _finishFrames.delete(traceId);
248
+ return event;
249
+ });
250
+ return {
251
+ name,
252
+ setup,
253
+ processEvent,
254
+ };
255
+ };
256
+ //# sourceMappingURL=nativeFrames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeFrames.js","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/nativeFrames.ts"],"names":[],"mappings":"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","sourcesContent":["import { spanToJSON } from '@sentry/core';\nimport type { Client, Event, Integration, Measurements, MeasurementUnit, Span } from '@sentry/types';\nimport { logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NativeFramesResponse } from '../../NativeRNSentry';\nimport type { ReactNativeClientOptions } from '../../options';\nimport { isRootSpan } from '../../utils/span';\nimport { NATIVE } from '../../wrapper';\n\n/**\n * Timeout from the final native frames fetch to processing the associated transaction.\n * If the transaction is not processed by this time, the native frames will be dropped\n * and not added to the event.\n */\nconst FINAL_FRAMES_TIMEOUT_MS = 2000;\n\nexport interface FramesMeasurements extends Measurements {\n frames_total: { value: number; unit: MeasurementUnit };\n frames_slow: { value: number; unit: MeasurementUnit };\n frames_frozen: { value: number; unit: MeasurementUnit };\n}\n\n/** The listeners for each native frames response, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */\nconst _framesListeners: Map<string, () => void> = new Map();\n\n/** The native frames at the transaction finish time, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */\nconst _finishFrames: Map<string, { timestamp: number; nativeFrames: NativeFramesResponse | null }> = new Map();\n\n/**\n * A margin of error of 50ms is allowed for the async native bridge call.\n * Anything larger would reduce the accuracy of our frames measurements.\n */\nconst MARGIN_OF_ERROR_SECONDS = 0.05;\n\n/**\n * Instrumentation to add native slow/frozen frames measurements onto transactions.\n */\nexport const nativeFramesIntegration = (): Integration => {\n const name: string = 'NativeFrames';\n\n /** The native frames at the finish time of the most recent span. */\n let _lastSpanFinishFrames:\n | {\n timestamp: number;\n nativeFrames: NativeFramesResponse;\n }\n | undefined = undefined;\n const _spanToNativeFramesAtStartMap: Map<string, NativeFramesResponse> = new Map();\n\n /**\n * Hooks into the client start and end span events.\n */\n const setup = (client: Client): void => {\n const { enableNativeFramesTracking } = client.getOptions() as ReactNativeClientOptions;\n\n if (enableNativeFramesTracking && !NATIVE.enableNative) {\n // Do not enable native frames tracking if native is not available.\n logger.warn(\n '[ReactNativeTracing] NativeFramesTracking is not available on the Web, Expo Go and other platforms without native modules.',\n );\n return;\n }\n\n if (!enableNativeFramesTracking && NATIVE.enableNative) {\n // Disable native frames tracking when native available and option is false.\n NATIVE.disableNativeFramesTracking();\n return;\n }\n\n if (!enableNativeFramesTracking) {\n return;\n }\n\n NATIVE.enableNativeFramesTracking();\n\n client.on('spanStart', _onSpanStart);\n client.on('spanEnd', _onSpanFinish);\n logger.log('[ReactNativeTracing] Native frames instrumentation initialized.');\n };\n\n /**\n * Adds frames measurements to an event. Called from a valid event processor.\n * Awaits for finish frames if needed.\n */\n const processEvent = (event: Event): Promise<Event> => {\n return _processEvent(event);\n };\n\n /**\n * Fetches the native frames in background if the given span is a root span.\n *\n * @param {Span} rootSpan - The span that has started.\n */\n const _onSpanStart = (rootSpan: Span): void => {\n if (!isRootSpan(rootSpan)) {\n return;\n }\n\n logger.debug(`[NativeFrames] Fetching frames for root span start (${rootSpan.spanContext().spanId}).`);\n\n NATIVE.fetchNativeFrames()\n .then(frames => {\n if (!frames) {\n logger.warn(\n `[NativeFrames] Fetched frames for root span start (${\n rootSpan.spanContext().spanId\n }), but no frames were returned.`,\n );\n return;\n }\n\n _spanToNativeFramesAtStartMap.set(rootSpan.spanContext().traceId, frames);\n })\n .then(undefined, error => {\n logger.error(\n `[NativeFrames] Error while fetching frames for root span start (${rootSpan.spanContext().spanId})`,\n error,\n );\n });\n };\n\n /**\n * Called on a span finish to fetch native frames to support transactions with trimEnd.\n * Only to be called when a span does not have an end timestamp.\n */\n const _onSpanFinish = (span: Span): void => {\n if (isRootSpan(span)) {\n return _onTransactionFinish(span);\n }\n\n const timestamp = timestampInSeconds();\n\n void NATIVE.fetchNativeFrames()\n .then(frames => {\n if (!frames) {\n return;\n }\n\n _lastSpanFinishFrames = {\n timestamp,\n nativeFrames: frames,\n };\n })\n .then(undefined, error => {\n logger.error(`[NativeFrames] Error while fetching frames for child span end.`, error);\n });\n };\n\n /**\n * To be called when a transaction is finished\n */\n const _onTransactionFinish = (span: Span): void => {\n _fetchFramesForTransaction(span).then(undefined, (reason: unknown) => {\n logger.error(\n `[NativeFrames] Error while fetching frames for root span start (${span.spanContext().spanId})`,\n reason,\n );\n });\n };\n\n /**\n * Returns the computed frames measurements and awaits for them if they are not ready yet.\n */\n const _getFramesMeasurements = (\n traceId: string,\n finalEndTimestamp: number,\n startFrames: NativeFramesResponse,\n ): Promise<FramesMeasurements | null> => {\n if (_finishFrames.has(traceId)) {\n logger.debug(`[NativeFrames] Native end frames already fetched for trace id (${traceId}).`);\n return Promise.resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));\n }\n\n return new Promise(resolve => {\n const timeout = setTimeout(() => {\n logger.debug(`[NativeFrames] Native end frames listener removed by timeout for trace id (${traceId}).`);\n _framesListeners.delete(traceId);\n\n resolve(null);\n }, 2000);\n\n _framesListeners.set(traceId, () => {\n logger.debug(`[NativeFrames] Native end frames listener called for trace id (${traceId}).`);\n resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));\n\n clearTimeout(timeout);\n _framesListeners.delete(traceId);\n });\n });\n };\n\n /**\n * Returns the computed frames measurements given ready data\n */\n const _prepareMeasurements = (\n traceId: string,\n finalEndTimestamp: number, // The actual transaction finish time.\n startFrames: NativeFramesResponse,\n ): FramesMeasurements | null => {\n let finalFinishFrames: NativeFramesResponse | undefined;\n\n const finish = _finishFrames.get(traceId);\n if (\n finish &&\n finish.nativeFrames &&\n // Must be in the margin of error of the actual transaction finish time (finalEndTimestamp)\n Math.abs(finish.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS\n ) {\n logger.debug(`[NativeFrames] Using frames from root span end (traceId, ${traceId}).`);\n finalFinishFrames = finish.nativeFrames;\n } else if (\n _lastSpanFinishFrames &&\n Math.abs(_lastSpanFinishFrames.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS\n ) {\n // Fallback to the last span finish if it is within the margin of error of the actual finish timestamp.\n // This should be the case for trimEnd.\n logger.debug(`[NativeFrames] Using native frames from last span end (traceId, ${traceId}).`);\n finalFinishFrames = _lastSpanFinishFrames.nativeFrames;\n } else {\n logger.warn(\n `[NativeFrames] Frames were collected within larger than margin of error delay for traceId (${traceId}). Dropping the inaccurate values.`,\n );\n return null;\n }\n\n const measurements = {\n frames_total: {\n value: finalFinishFrames.totalFrames - startFrames.totalFrames,\n unit: 'none',\n },\n frames_frozen: {\n value: finalFinishFrames.frozenFrames - startFrames.frozenFrames,\n unit: 'none',\n },\n frames_slow: {\n value: finalFinishFrames.slowFrames - startFrames.slowFrames,\n unit: 'none',\n },\n };\n\n if (\n measurements.frames_frozen.value <= 0 &&\n measurements.frames_slow.value <= 0 &&\n measurements.frames_total.value <= 0\n ) {\n logger.warn(\n `[NativeFrames] Detected zero slow or frozen frames. Not adding measurements to traceId (${traceId}).`,\n );\n return null;\n }\n\n return measurements;\n };\n\n /**\n * Fetch finish frames for a transaction at the current time. Calls any awaiting listeners.\n */\n const _fetchFramesForTransaction = async (span: Span): Promise<void> => {\n const traceId = spanToJSON(span).trace_id;\n if (!traceId) {\n return;\n }\n\n const startFrames = _spanToNativeFramesAtStartMap.get(span.spanContext().traceId);\n\n // This timestamp marks when the finish frames were retrieved. It should be pretty close to the transaction finish.\n const timestamp = timestampInSeconds();\n let finishFrames: NativeFramesResponse | null = null;\n if (startFrames) {\n finishFrames = await NATIVE.fetchNativeFrames();\n }\n\n _finishFrames.set(traceId, {\n nativeFrames: finishFrames,\n timestamp,\n });\n\n _framesListeners.get(traceId)?.();\n\n setTimeout(() => _cancelEndFrames(span), FINAL_FRAMES_TIMEOUT_MS);\n };\n\n /**\n * On a finish frames failure, we cancel the await.\n */\n const _cancelEndFrames = (span: Span): void => {\n const spanJSON = spanToJSON(span);\n const traceId = spanJSON.trace_id;\n if (!traceId) {\n return;\n }\n\n if (_finishFrames.has(traceId)) {\n _finishFrames.delete(traceId);\n\n logger.log(\n `[NativeFrames] Native frames timed out for ${spanJSON.op} transaction ${spanJSON.description}. Not adding native frames measurements.`,\n );\n }\n };\n\n /**\n * Adds frames measurements to an event. Called from a valid event processor.\n * Awaits for finish frames if needed.\n */\n const _processEvent = async (event: Event): Promise<Event> => {\n if (\n event.type !== 'transaction' ||\n !event.transaction ||\n !event.contexts ||\n !event.contexts.trace ||\n !event.timestamp ||\n !event.contexts.trace.trace_id\n ) {\n return event;\n }\n\n const traceOp = event.contexts.trace.op;\n const traceId = event.contexts.trace.trace_id;\n const startFrames = _spanToNativeFramesAtStartMap.get(traceId);\n _spanToNativeFramesAtStartMap.delete(traceId);\n if (!startFrames) {\n logger.warn(\n `[NativeFrames] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but it already ended.`,\n );\n return event;\n }\n\n const measurements = await _getFramesMeasurements(traceId, event.timestamp, startFrames);\n\n if (!measurements) {\n logger.log(\n `[NativeFrames] Could not fetch native frames for ${traceOp} transaction ${event.transaction}. Not adding native frames measurements.`,\n );\n return event;\n }\n\n logger.log(\n `[Measurements] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(\n measurements,\n undefined,\n 2,\n )}`,\n );\n\n event.measurements = {\n ...(event.measurements ?? {}),\n ...measurements,\n };\n\n _finishFrames.delete(traceId);\n\n return event;\n };\n\n return {\n name,\n setup,\n processEvent,\n };\n};\n"]}
@@ -0,0 +1,31 @@
1
+ import type { Integration, Measurements, MeasurementUnit } from '@sentry/types';
2
+ import { STALL_COUNT, STALL_LONGEST_TIME, STALL_TOTAL_TIME } from '../../measurements';
3
+ export interface StallMeasurements extends Measurements {
4
+ [STALL_COUNT]: {
5
+ value: number;
6
+ unit: MeasurementUnit;
7
+ };
8
+ [STALL_TOTAL_TIME]: {
9
+ value: number;
10
+ unit: MeasurementUnit;
11
+ };
12
+ [STALL_LONGEST_TIME]: {
13
+ value: number;
14
+ unit: MeasurementUnit;
15
+ };
16
+ }
17
+ /**
18
+ * Stall measurement tracker inspired by the `JSEventLoopWatchdog` used internally in React Native:
19
+ * https://github.com/facebook/react-native/blob/006f5afe120c290a37cf6ff896748fbc062bf7ed/Libraries/Interaction/JSEventLoopWatchdog.js
20
+ *
21
+ * However, we modified the interval implementation to instead have a fixed loop timeout interval of `LOOP_TIMEOUT_INTERVAL_MS`.
22
+ * We then would consider that iteration a stall when the total time for that interval to run is greater than `LOOP_TIMEOUT_INTERVAL_MS + minimumStallThreshold`
23
+ */
24
+ export declare const stallTrackingIntegration: ({ minimumStallThresholdMs, }?: {
25
+ /**
26
+ * How long in milliseconds an event loop iteration can be delayed for before being considered a "stall."
27
+ * @default 50
28
+ */
29
+ minimumStallThresholdMs?: number;
30
+ }) => Integration;
31
+ //# sourceMappingURL=stalltracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stalltracking.d.ts","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/stalltracking.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,WAAW,EAAE,YAAY,EAAE,eAAe,EAAQ,MAAM,eAAe,CAAC;AAK9F,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAMvF,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,CAAC,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,eAAe,CAAA;KAAE,CAAC;IACxD,CAAC,gBAAgB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,eAAe,CAAA;KAAE,CAAC;IAC7D,CAAC,kBAAkB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,eAAe,CAAA;KAAE,CAAC;CAChE;AASD;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;IAGnC;;;OAGG;8BACuB,MAAM;MACzB,WA0TR,CAAC"}
@@ -0,0 +1,236 @@
1
+ /* eslint-disable max-lines */
2
+ import { getRootSpan, spanToJSON } from '@sentry/core';
3
+ import { logger, timestampInSeconds } from '@sentry/utils';
4
+ import { AppState } from 'react-native';
5
+ import { STALL_COUNT, STALL_LONGEST_TIME, STALL_TOTAL_TIME } from '../../measurements';
6
+ import { isRootSpan } from '../../utils/span';
7
+ import { getLatestChildSpanEndTimestamp, isNearToNow, setSpanMeasurement } from '../utils';
8
+ const INTEGRATION_NAME = 'StallTracking';
9
+ /** Margin of error of 20ms */
10
+ const MARGIN_OF_ERROR_SECONDS = 0.02;
11
+ /** How long between each iteration in the event loop tracker timeout */
12
+ const LOOP_TIMEOUT_INTERVAL_MS = 50;
13
+ /** Limit for how many transactions the stall tracker will track at a time to prevent leaks due to transactions not being finished */
14
+ const MAX_RUNNING_TRANSACTIONS = 10;
15
+ /**
16
+ * Stall measurement tracker inspired by the `JSEventLoopWatchdog` used internally in React Native:
17
+ * https://github.com/facebook/react-native/blob/006f5afe120c290a37cf6ff896748fbc062bf7ed/Libraries/Interaction/JSEventLoopWatchdog.js
18
+ *
19
+ * However, we modified the interval implementation to instead have a fixed loop timeout interval of `LOOP_TIMEOUT_INTERVAL_MS`.
20
+ * We then would consider that iteration a stall when the total time for that interval to run is greater than `LOOP_TIMEOUT_INTERVAL_MS + minimumStallThreshold`
21
+ */
22
+ export const stallTrackingIntegration = ({ minimumStallThresholdMs = 50, } = {}) => {
23
+ const statsByRootSpan = new Map();
24
+ const state = {
25
+ isTracking: false,
26
+ timeout: null,
27
+ isBackground: false,
28
+ lastIntervalMs: 0,
29
+ totalStallTime: 0,
30
+ stallCount: 0,
31
+ backgroundEventListener: (appState) => {
32
+ if (appState === 'active') {
33
+ state.isBackground = false;
34
+ if (state.timeout != null) {
35
+ state.lastIntervalMs = timestampInSeconds() * 1000;
36
+ state.iteration();
37
+ }
38
+ }
39
+ else {
40
+ state.isBackground = true;
41
+ state.timeout !== null && clearTimeout(state.timeout);
42
+ }
43
+ },
44
+ iteration: () => {
45
+ var _a;
46
+ const now = timestampInSeconds() * 1000;
47
+ const totalTimeTaken = now - state.lastIntervalMs;
48
+ if (totalTimeTaken >= LOOP_TIMEOUT_INTERVAL_MS + minimumStallThresholdMs) {
49
+ const stallTime = totalTimeTaken - LOOP_TIMEOUT_INTERVAL_MS;
50
+ state.stallCount += 1;
51
+ state.totalStallTime += stallTime;
52
+ for (const [transaction, value] of statsByRootSpan.entries()) {
53
+ const longestStallTime = Math.max((_a = value.longestStallTime) !== null && _a !== void 0 ? _a : 0, stallTime);
54
+ statsByRootSpan.set(transaction, Object.assign(Object.assign({}, value), { longestStallTime }));
55
+ }
56
+ }
57
+ state.lastIntervalMs = now;
58
+ if (state.isTracking && !state.isBackground) {
59
+ state.timeout = setTimeout(state.iteration, LOOP_TIMEOUT_INTERVAL_MS);
60
+ }
61
+ },
62
+ };
63
+ const setup = (client) => {
64
+ client.on('spanStart', _onSpanStart);
65
+ client.on('spanEnd', _onSpanEnd);
66
+ };
67
+ const _onSpanStart = (rootSpan) => {
68
+ if (!isRootSpan(rootSpan)) {
69
+ return;
70
+ }
71
+ if (statsByRootSpan.has(rootSpan)) {
72
+ logger.error('[StallTracking] Tried to start stall tracking on a transaction already being tracked. Measurements might be lost.');
73
+ return;
74
+ }
75
+ _startTracking();
76
+ statsByRootSpan.set(rootSpan, {
77
+ longestStallTime: 0,
78
+ atTimestamp: null,
79
+ atStart: _getCurrentStats(rootSpan),
80
+ });
81
+ _flushLeakedTransactions();
82
+ };
83
+ const _onSpanEnd = (rootSpan) => {
84
+ if (!isRootSpan(rootSpan)) {
85
+ return _onChildSpanEnd(rootSpan);
86
+ }
87
+ const transactionStats = statsByRootSpan.get(rootSpan);
88
+ if (!transactionStats) {
89
+ // Transaction has been flushed out somehow, we return null.
90
+ logger.log('[StallTracking] Stall measurements were not added to transaction due to exceeding the max count.');
91
+ statsByRootSpan.delete(rootSpan);
92
+ _shouldStopTracking();
93
+ return;
94
+ }
95
+ // The endTimestamp is always set, but type-wise it's optional
96
+ // https://github.com/getsentry/sentry-javascript/blob/38bd57b0785c97c413f36f89ff931d927e469078/packages/core/src/tracing/sentrySpan.ts#L170
97
+ const endTimestamp = spanToJSON(rootSpan).timestamp;
98
+ let statsOnFinish;
99
+ if (isNearToNow(endTimestamp)) {
100
+ statsOnFinish = _getCurrentStats(rootSpan);
101
+ }
102
+ else {
103
+ // The idleSpan in JS V8 is always trimmed to the last span's endTimestamp (timestamp).
104
+ // The unfinished child spans are removed from the root span after the `spanEnd` event.
105
+ const latestChildSpanEnd = getLatestChildSpanEndTimestamp(rootSpan);
106
+ if (latestChildSpanEnd !== endTimestamp) {
107
+ logger.log('[StallTracking] Stall measurements not added due to a custom `endTimestamp` (root end is not equal to the latest child span end).');
108
+ }
109
+ if (!transactionStats.atTimestamp) {
110
+ logger.log('[StallTracking] Stall measurements not added due to `endTimestamp` not being close to now. And no previous stats from child end were found.');
111
+ }
112
+ if (latestChildSpanEnd === endTimestamp && transactionStats.atTimestamp) {
113
+ statsOnFinish = transactionStats.atTimestamp.stats;
114
+ }
115
+ }
116
+ statsByRootSpan.delete(rootSpan);
117
+ _shouldStopTracking();
118
+ if (!statsOnFinish) {
119
+ if (typeof endTimestamp !== 'undefined') {
120
+ logger.log('[StallTracking] Stall measurements not added due to `endTimestamp` not being close to now.', 'endTimestamp', endTimestamp, 'now', timestampInSeconds());
121
+ }
122
+ return;
123
+ }
124
+ setSpanMeasurement(rootSpan, STALL_COUNT, statsOnFinish.stall_count.value - transactionStats.atStart.stall_count.value, transactionStats.atStart.stall_count.unit);
125
+ setSpanMeasurement(rootSpan, STALL_TOTAL_TIME, statsOnFinish.stall_total_time.value - transactionStats.atStart.stall_total_time.value, transactionStats.atStart.stall_total_time.unit);
126
+ setSpanMeasurement(rootSpan, STALL_LONGEST_TIME, statsOnFinish.stall_longest_time.value, statsOnFinish.stall_longest_time.unit);
127
+ };
128
+ const _onChildSpanEnd = (childSpan) => {
129
+ const rootSpan = getRootSpan(childSpan);
130
+ const finalEndTimestamp = spanToJSON(childSpan).timestamp;
131
+ if (finalEndTimestamp) {
132
+ _markSpanFinish(rootSpan, finalEndTimestamp);
133
+ }
134
+ };
135
+ /**
136
+ * Logs the finish time of the span for use in `trimEnd: true` transactions.
137
+ */
138
+ const _markSpanFinish = (rootSpan, childSpanEndTime) => {
139
+ const previousStats = statsByRootSpan.get(rootSpan);
140
+ if (previousStats) {
141
+ if (Math.abs(timestampInSeconds() - childSpanEndTime) > MARGIN_OF_ERROR_SECONDS) {
142
+ logger.log('[StallTracking] Span end not logged due to end timestamp being outside the margin of error from now.');
143
+ if (previousStats.atTimestamp && previousStats.atTimestamp.timestamp < childSpanEndTime) {
144
+ // We also need to delete the stat for the last span, as the transaction would be trimmed to this span not the last one.
145
+ statsByRootSpan.set(rootSpan, Object.assign(Object.assign({}, previousStats), { atTimestamp: null }));
146
+ }
147
+ }
148
+ else {
149
+ statsByRootSpan.set(rootSpan, Object.assign(Object.assign({}, previousStats), { atTimestamp: {
150
+ timestamp: childSpanEndTime,
151
+ stats: _getCurrentStats(rootSpan),
152
+ } }));
153
+ }
154
+ }
155
+ };
156
+ /**
157
+ * Get the current stats for a transaction at a given time.
158
+ */
159
+ const _getCurrentStats = (span) => {
160
+ var _a, _b;
161
+ return {
162
+ stall_count: { value: state.stallCount, unit: 'none' },
163
+ stall_total_time: { value: state.totalStallTime, unit: 'millisecond' },
164
+ stall_longest_time: {
165
+ value: (_b = (_a = statsByRootSpan.get(span)) === null || _a === void 0 ? void 0 : _a.longestStallTime) !== null && _b !== void 0 ? _b : 0,
166
+ unit: 'millisecond',
167
+ },
168
+ };
169
+ };
170
+ /**
171
+ * Start tracking stalls
172
+ */
173
+ const _startTracking = () => {
174
+ if (!state.isTracking) {
175
+ state.isTracking = true;
176
+ state.lastIntervalMs = Math.floor(timestampInSeconds() * 1000);
177
+ state.iteration();
178
+ }
179
+ };
180
+ /**
181
+ * Stops the stall tracking interval and calls reset().
182
+ */
183
+ const _stopTracking = () => {
184
+ state.isTracking = false;
185
+ if (state.timeout !== null) {
186
+ clearTimeout(state.timeout);
187
+ state.timeout = null;
188
+ }
189
+ _reset();
190
+ };
191
+ /**
192
+ * Will stop tracking if there are no more transactions.
193
+ */
194
+ const _shouldStopTracking = () => {
195
+ if (statsByRootSpan.size === 0) {
196
+ _stopTracking();
197
+ }
198
+ };
199
+ /**
200
+ * Clears all the collected stats
201
+ */
202
+ const _reset = () => {
203
+ state.stallCount = 0;
204
+ state.totalStallTime = 0;
205
+ state.lastIntervalMs = 0;
206
+ statsByRootSpan.clear();
207
+ };
208
+ /**
209
+ * Deletes leaked transactions (Earliest transactions when we have more than MAX_RUNNING_TRANSACTIONS transactions.)
210
+ */
211
+ const _flushLeakedTransactions = () => {
212
+ if (statsByRootSpan.size > MAX_RUNNING_TRANSACTIONS) {
213
+ let counter = 0;
214
+ const len = statsByRootSpan.size - MAX_RUNNING_TRANSACTIONS;
215
+ const transactions = statsByRootSpan.keys();
216
+ for (const t of transactions) {
217
+ if (counter >= len)
218
+ break;
219
+ counter += 1;
220
+ statsByRootSpan.delete(t);
221
+ }
222
+ }
223
+ };
224
+ // Avoids throwing any error if using React Native on a environment that doesn't implement AppState.
225
+ if (AppState === null || AppState === void 0 ? void 0 : AppState.isAvailable) {
226
+ // eslint-disable-next-line @typescript-eslint/unbound-method
227
+ AppState.addEventListener('change', state.backgroundEventListener);
228
+ }
229
+ return {
230
+ name: INTEGRATION_NAME,
231
+ setup,
232
+ /** For testing only @private */
233
+ _internalState: state,
234
+ };
235
+ };
236
+ //# sourceMappingURL=stalltracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stalltracking.js","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/stalltracking.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable max-lines */\nimport { getRootSpan, spanToJSON } from '@sentry/core';\nimport type { Client, Integration, Measurements, MeasurementUnit, Span } from '@sentry/types';\nimport { logger, timestampInSeconds } from '@sentry/utils';\nimport type { AppStateStatus } from 'react-native';\nimport { AppState } from 'react-native';\n\nimport { STALL_COUNT, STALL_LONGEST_TIME, STALL_TOTAL_TIME } from '../../measurements';\nimport { isRootSpan } from '../../utils/span';\nimport { getLatestChildSpanEndTimestamp, isNearToNow, setSpanMeasurement } from '../utils';\n\nconst INTEGRATION_NAME = 'StallTracking';\n\nexport interface StallMeasurements extends Measurements {\n [STALL_COUNT]: { value: number; unit: MeasurementUnit };\n [STALL_TOTAL_TIME]: { value: number; unit: MeasurementUnit };\n [STALL_LONGEST_TIME]: { value: number; unit: MeasurementUnit };\n}\n\n/** Margin of error of 20ms */\nconst MARGIN_OF_ERROR_SECONDS = 0.02;\n/** How long between each iteration in the event loop tracker timeout */\nconst LOOP_TIMEOUT_INTERVAL_MS = 50;\n/** Limit for how many transactions the stall tracker will track at a time to prevent leaks due to transactions not being finished */\nconst MAX_RUNNING_TRANSACTIONS = 10;\n\n/**\n * Stall measurement tracker inspired by the `JSEventLoopWatchdog` used internally in React Native:\n * https://github.com/facebook/react-native/blob/006f5afe120c290a37cf6ff896748fbc062bf7ed/Libraries/Interaction/JSEventLoopWatchdog.js\n *\n * However, we modified the interval implementation to instead have a fixed loop timeout interval of `LOOP_TIMEOUT_INTERVAL_MS`.\n * We then would consider that iteration a stall when the total time for that interval to run is greater than `LOOP_TIMEOUT_INTERVAL_MS + minimumStallThreshold`\n */\nexport const stallTrackingIntegration = ({\n minimumStallThresholdMs = 50,\n}: {\n /**\n * How long in milliseconds an event loop iteration can be delayed for before being considered a \"stall.\"\n * @default 50\n */\n minimumStallThresholdMs?: number;\n} = {}): Integration => {\n const statsByRootSpan: Map<\n Span,\n {\n longestStallTime: number;\n atStart: StallMeasurements;\n atTimestamp: {\n timestamp: number;\n stats: StallMeasurements;\n } | null;\n }\n > = new Map();\n\n const state: {\n isTracking: boolean;\n timeout: ReturnType<typeof setTimeout> | null;\n isBackground: boolean;\n /** Switch that enables the iteration once app moves from background to foreground. */\n backgroundEventListener: (appState: AppStateStatus) => void;\n /** The last timestamp the iteration ran in milliseconds */\n lastIntervalMs: number;\n /** Total amount of time of all stalls that occurred during the current tracking session */\n totalStallTime: number;\n /** Total number of stalls that occurred during the current tracking session */\n stallCount: number;\n /**\n * Iteration of the stall tracking interval. Measures how long the timer strayed from its expected time of running, and how\n * long the stall is for.\n */\n iteration: () => void;\n } = {\n isTracking: false,\n timeout: null,\n isBackground: false,\n lastIntervalMs: 0,\n totalStallTime: 0,\n stallCount: 0,\n backgroundEventListener: (appState: AppStateStatus): void => {\n if (appState === ('active' as AppStateStatus)) {\n state.isBackground = false;\n if (state.timeout != null) {\n state.lastIntervalMs = timestampInSeconds() * 1000;\n state.iteration();\n }\n } else {\n state.isBackground = true;\n state.timeout !== null && clearTimeout(state.timeout);\n }\n },\n iteration: (): void => {\n const now = timestampInSeconds() * 1000;\n const totalTimeTaken = now - state.lastIntervalMs;\n\n if (totalTimeTaken >= LOOP_TIMEOUT_INTERVAL_MS + minimumStallThresholdMs) {\n const stallTime = totalTimeTaken - LOOP_TIMEOUT_INTERVAL_MS;\n state.stallCount += 1;\n state.totalStallTime += stallTime;\n\n for (const [transaction, value] of statsByRootSpan.entries()) {\n const longestStallTime = Math.max(value.longestStallTime ?? 0, stallTime);\n\n statsByRootSpan.set(transaction, {\n ...value,\n longestStallTime,\n });\n }\n }\n\n state.lastIntervalMs = now;\n\n if (state.isTracking && !state.isBackground) {\n state.timeout = setTimeout(state.iteration, LOOP_TIMEOUT_INTERVAL_MS);\n }\n },\n };\n\n const setup = (client: Client): void => {\n client.on('spanStart', _onSpanStart);\n client.on('spanEnd', _onSpanEnd);\n };\n\n const _onSpanStart = (rootSpan: Span): void => {\n if (!isRootSpan(rootSpan)) {\n return;\n }\n\n if (statsByRootSpan.has(rootSpan)) {\n logger.error(\n '[StallTracking] Tried to start stall tracking on a transaction already being tracked. Measurements might be lost.',\n );\n return;\n }\n\n _startTracking();\n statsByRootSpan.set(rootSpan, {\n longestStallTime: 0,\n atTimestamp: null,\n atStart: _getCurrentStats(rootSpan),\n });\n _flushLeakedTransactions();\n };\n\n const _onSpanEnd = (rootSpan: Span): void => {\n if (!isRootSpan(rootSpan)) {\n return _onChildSpanEnd(rootSpan);\n }\n\n const transactionStats = statsByRootSpan.get(rootSpan);\n\n if (!transactionStats) {\n // Transaction has been flushed out somehow, we return null.\n logger.log('[StallTracking] Stall measurements were not added to transaction due to exceeding the max count.');\n\n statsByRootSpan.delete(rootSpan);\n _shouldStopTracking();\n\n return;\n }\n\n // The endTimestamp is always set, but type-wise it's optional\n // https://github.com/getsentry/sentry-javascript/blob/38bd57b0785c97c413f36f89ff931d927e469078/packages/core/src/tracing/sentrySpan.ts#L170\n const endTimestamp = spanToJSON(rootSpan).timestamp;\n\n let statsOnFinish: StallMeasurements | undefined;\n if (isNearToNow(endTimestamp)) {\n statsOnFinish = _getCurrentStats(rootSpan);\n } else {\n // The idleSpan in JS V8 is always trimmed to the last span's endTimestamp (timestamp).\n // The unfinished child spans are removed from the root span after the `spanEnd` event.\n\n const latestChildSpanEnd = getLatestChildSpanEndTimestamp(rootSpan);\n if (latestChildSpanEnd !== endTimestamp) {\n logger.log(\n '[StallTracking] Stall measurements not added due to a custom `endTimestamp` (root end is not equal to the latest child span end).',\n );\n }\n\n if (!transactionStats.atTimestamp) {\n logger.log(\n '[StallTracking] Stall measurements not added due to `endTimestamp` not being close to now. And no previous stats from child end were found.',\n );\n }\n\n if (latestChildSpanEnd === endTimestamp && transactionStats.atTimestamp) {\n statsOnFinish = transactionStats.atTimestamp.stats;\n }\n }\n\n statsByRootSpan.delete(rootSpan);\n _shouldStopTracking();\n\n if (!statsOnFinish) {\n if (typeof endTimestamp !== 'undefined') {\n logger.log(\n '[StallTracking] Stall measurements not added due to `endTimestamp` not being close to now.',\n 'endTimestamp',\n endTimestamp,\n 'now',\n timestampInSeconds(),\n );\n }\n\n return;\n }\n\n setSpanMeasurement(\n rootSpan,\n STALL_COUNT,\n statsOnFinish.stall_count.value - transactionStats.atStart.stall_count.value,\n transactionStats.atStart.stall_count.unit,\n );\n\n setSpanMeasurement(\n rootSpan,\n STALL_TOTAL_TIME,\n statsOnFinish.stall_total_time.value - transactionStats.atStart.stall_total_time.value,\n transactionStats.atStart.stall_total_time.unit,\n );\n\n setSpanMeasurement(\n rootSpan,\n STALL_LONGEST_TIME,\n statsOnFinish.stall_longest_time.value,\n statsOnFinish.stall_longest_time.unit,\n );\n };\n\n const _onChildSpanEnd = (childSpan: Span): void => {\n const rootSpan = getRootSpan(childSpan);\n\n const finalEndTimestamp = spanToJSON(childSpan).timestamp;\n if (finalEndTimestamp) {\n _markSpanFinish(rootSpan, finalEndTimestamp);\n }\n };\n\n /**\n * Logs the finish time of the span for use in `trimEnd: true` transactions.\n */\n const _markSpanFinish = (rootSpan: Span, childSpanEndTime: number): void => {\n const previousStats = statsByRootSpan.get(rootSpan);\n if (previousStats) {\n if (Math.abs(timestampInSeconds() - childSpanEndTime) > MARGIN_OF_ERROR_SECONDS) {\n logger.log(\n '[StallTracking] Span end not logged due to end timestamp being outside the margin of error from now.',\n );\n\n if (previousStats.atTimestamp && previousStats.atTimestamp.timestamp < childSpanEndTime) {\n // We also need to delete the stat for the last span, as the transaction would be trimmed to this span not the last one.\n statsByRootSpan.set(rootSpan, {\n ...previousStats,\n atTimestamp: null,\n });\n }\n } else {\n statsByRootSpan.set(rootSpan, {\n ...previousStats,\n atTimestamp: {\n timestamp: childSpanEndTime,\n stats: _getCurrentStats(rootSpan),\n },\n });\n }\n }\n };\n\n /**\n * Get the current stats for a transaction at a given time.\n */\n const _getCurrentStats = (span: Span): StallMeasurements => {\n return {\n stall_count: { value: state.stallCount, unit: 'none' },\n stall_total_time: { value: state.totalStallTime, unit: 'millisecond' },\n stall_longest_time: {\n value: statsByRootSpan.get(span)?.longestStallTime ?? 0,\n unit: 'millisecond',\n },\n };\n };\n\n /**\n * Start tracking stalls\n */\n const _startTracking = (): void => {\n if (!state.isTracking) {\n state.isTracking = true;\n state.lastIntervalMs = Math.floor(timestampInSeconds() * 1000);\n\n state.iteration();\n }\n };\n\n /**\n * Stops the stall tracking interval and calls reset().\n */\n const _stopTracking = (): void => {\n state.isTracking = false;\n\n if (state.timeout !== null) {\n clearTimeout(state.timeout);\n state.timeout = null;\n }\n\n _reset();\n };\n\n /**\n * Will stop tracking if there are no more transactions.\n */\n const _shouldStopTracking = (): void => {\n if (statsByRootSpan.size === 0) {\n _stopTracking();\n }\n };\n\n /**\n * Clears all the collected stats\n */\n const _reset = (): void => {\n state.stallCount = 0;\n state.totalStallTime = 0;\n state.lastIntervalMs = 0;\n statsByRootSpan.clear();\n };\n\n /**\n * Deletes leaked transactions (Earliest transactions when we have more than MAX_RUNNING_TRANSACTIONS transactions.)\n */\n const _flushLeakedTransactions = (): void => {\n if (statsByRootSpan.size > MAX_RUNNING_TRANSACTIONS) {\n let counter = 0;\n const len = statsByRootSpan.size - MAX_RUNNING_TRANSACTIONS;\n const transactions = statsByRootSpan.keys();\n for (const t of transactions) {\n if (counter >= len) break;\n counter += 1;\n statsByRootSpan.delete(t);\n }\n }\n };\n\n // Avoids throwing any error if using React Native on a environment that doesn't implement AppState.\n if (AppState?.isAvailable) {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n AppState.addEventListener('change', state.backgroundEventListener);\n }\n\n return {\n name: INTEGRATION_NAME,\n setup,\n\n /** For testing only @private */\n _internalState: state,\n } as Integration;\n};\n"]}
@@ -0,0 +1,11 @@
1
+ import type { Integration, Span } from '@sentry/types';
2
+ export declare const userInteractionIntegration: () => Integration;
3
+ /**
4
+ * Starts a new transaction for a user interaction.
5
+ * @param userInteractionId Consists of `op` representation UI Event and `elementId` unique element identifier on current screen.
6
+ */
7
+ export declare const startUserInteractionSpan: (userInteractionId: {
8
+ elementId: string | undefined;
9
+ op: string;
10
+ }) => Span | undefined;
11
+ //# sourceMappingURL=userInteraction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userInteraction.d.ts","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/userInteraction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAoB,MAAM,eAAe,CAAC;AAWzE,eAAO,MAAM,0BAA0B,QAAO,WAI7C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,sBAAuB;IAC1D,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,EAAE,EAAE,MAAM,CAAC;CACZ,KAAG,IAAI,GAAG,SAmEV,CAAC"}