instrument.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. const debugBuild = require('../common/debug-build.js');
  4. const getCLS = require('./web-vitals/getCLS.js');
  5. const getFID = require('./web-vitals/getFID.js');
  6. const getLCP = require('./web-vitals/getLCP.js');
  7. const observe = require('./web-vitals/lib/observe.js');
  8. const handlers = {};
  9. const instrumented = {};
  10. let _previousCls;
  11. let _previousFid;
  12. let _previousLcp;
  13. /**
  14. * Add a callback that will be triggered when a CLS metric is available.
  15. * Returns a cleanup callback which can be called to remove the instrumentation handler.
  16. *
  17. * Pass `stopOnCallback = true` to stop listening for CLS when the cleanup callback is called.
  18. * This will lead to the CLS being finalized and frozen.
  19. */
  20. function addClsInstrumentationHandler(
  21. callback,
  22. stopOnCallback = false,
  23. ) {
  24. return addMetricObserver('cls', callback, instrumentCls, _previousCls, stopOnCallback);
  25. }
  26. /**
  27. * Add a callback that will be triggered when a LCP metric is available.
  28. * Returns a cleanup callback which can be called to remove the instrumentation handler.
  29. *
  30. * Pass `stopOnCallback = true` to stop listening for LCP when the cleanup callback is called.
  31. * This will lead to the LCP being finalized and frozen.
  32. */
  33. function addLcpInstrumentationHandler(
  34. callback,
  35. stopOnCallback = false,
  36. ) {
  37. return addMetricObserver('lcp', callback, instrumentLcp, _previousLcp, stopOnCallback);
  38. }
  39. /**
  40. * Add a callback that will be triggered when a FID metric is available.
  41. * Returns a cleanup callback which can be called to remove the instrumentation handler.
  42. */
  43. function addFidInstrumentationHandler(callback) {
  44. return addMetricObserver('fid', callback, instrumentFid, _previousFid);
  45. }
  46. /**
  47. * Add a callback that will be triggered when a performance observer is triggered,
  48. * and receives the entries of the observer.
  49. * Returns a cleanup callback which can be called to remove the instrumentation handler.
  50. */
  51. function addPerformanceInstrumentationHandler(
  52. type,
  53. callback,
  54. ) {
  55. addHandler(type, callback);
  56. if (!instrumented[type]) {
  57. instrumentPerformanceObserver(type);
  58. instrumented[type] = true;
  59. }
  60. return getCleanupCallback(type, callback);
  61. }
  62. /** Trigger all handlers of a given type. */
  63. function triggerHandlers(type, data) {
  64. const typeHandlers = handlers[type];
  65. if (!typeHandlers || !typeHandlers.length) {
  66. return;
  67. }
  68. for (const handler of typeHandlers) {
  69. try {
  70. handler(data);
  71. } catch (e) {
  72. debugBuild.DEBUG_BUILD &&
  73. utils.logger.error(
  74. `Error while triggering instrumentation handler.\nType: ${type}\nName: ${utils.getFunctionName(handler)}\nError:`,
  75. e,
  76. );
  77. }
  78. }
  79. }
  80. function instrumentCls() {
  81. return getCLS.onCLS(metric => {
  82. triggerHandlers('cls', {
  83. metric,
  84. });
  85. _previousCls = metric;
  86. });
  87. }
  88. function instrumentFid() {
  89. return getFID.onFID(metric => {
  90. triggerHandlers('fid', {
  91. metric,
  92. });
  93. _previousFid = metric;
  94. });
  95. }
  96. function instrumentLcp() {
  97. return getLCP.onLCP(metric => {
  98. triggerHandlers('lcp', {
  99. metric,
  100. });
  101. _previousLcp = metric;
  102. });
  103. }
  104. function addMetricObserver(
  105. type,
  106. callback,
  107. instrumentFn,
  108. previousValue,
  109. stopOnCallback = false,
  110. ) {
  111. addHandler(type, callback);
  112. let stopListening;
  113. if (!instrumented[type]) {
  114. stopListening = instrumentFn();
  115. instrumented[type] = true;
  116. }
  117. if (previousValue) {
  118. callback({ metric: previousValue });
  119. }
  120. return getCleanupCallback(type, callback, stopOnCallback ? stopListening : undefined);
  121. }
  122. function instrumentPerformanceObserver(type) {
  123. const options = {};
  124. // Special per-type options we want to use
  125. if (type === 'event') {
  126. options.durationThreshold = 0;
  127. }
  128. observe.observe(
  129. type,
  130. entries => {
  131. triggerHandlers(type, { entries });
  132. },
  133. options,
  134. );
  135. }
  136. function addHandler(type, handler) {
  137. handlers[type] = handlers[type] || [];
  138. (handlers[type] ).push(handler);
  139. }
  140. // Get a callback which can be called to remove the instrumentation handler
  141. function getCleanupCallback(
  142. type,
  143. callback,
  144. stopListening,
  145. ) {
  146. return () => {
  147. if (stopListening) {
  148. stopListening();
  149. }
  150. const typeHandlers = handlers[type];
  151. if (!typeHandlers) {
  152. return;
  153. }
  154. const index = typeHandlers.indexOf(callback);
  155. if (index !== -1) {
  156. typeHandlers.splice(index, 1);
  157. }
  158. };
  159. }
  160. exports.addClsInstrumentationHandler = addClsInstrumentationHandler;
  161. exports.addFidInstrumentationHandler = addFidInstrumentationHandler;
  162. exports.addLcpInstrumentationHandler = addLcpInstrumentationHandler;
  163. exports.addPerformanceInstrumentationHandler = addPerformanceInstrumentationHandler;
  164. //# sourceMappingURL=instrument.js.map