traceAnalytics.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import * as Sentry from '@sentry/react';
  2. import type {Organization} from 'sentry/types/organization';
  3. import type {Project} from 'sentry/types/project';
  4. import {trackAnalytics} from 'sentry/utils/analytics';
  5. import {TraceShape, type TraceTree} from './traceModels/traceTree';
  6. const trackTraceMetadata = (
  7. tree: TraceTree,
  8. projects: Project[],
  9. organization: Organization
  10. ) => {
  11. Sentry.metrics.increment(`trace.trace_shape.${tree.shape}`);
  12. // space[1] represents the node duration (in milliseconds)
  13. const trace_duration_seconds = (tree.root.space?.[1] ?? 0) / 1000;
  14. const projectSlugs = [
  15. ...new Set(
  16. tree.list.map(node => node.metadata.project_slug).filter(slug => slug !== undefined)
  17. ),
  18. ];
  19. const projectPlatforms = projects
  20. .filter(p => projectSlugs.includes(p.slug))
  21. .map(project => project?.platform ?? '');
  22. trackAnalytics('trace.metadata', {
  23. shape: tree.shape,
  24. // round trace_duration_seconds to nearest two decimal places
  25. trace_duration_seconds: Math.round(trace_duration_seconds * 100) / 100,
  26. num_root_children: tree.root.children.length,
  27. num_nodes: tree.list.length,
  28. project_platforms: projectPlatforms,
  29. organization,
  30. });
  31. };
  32. const trackFailedToFetchTraceState = () =>
  33. Sentry.metrics.increment('trace.failed_to_fetch_trace');
  34. const trackEmptyTraceState = () => Sentry.metrics.increment('trace.empty_trace');
  35. const trackLayoutChange = (layout: string, organization: Organization) =>
  36. trackAnalytics('trace.trace_layout.change', {
  37. layout,
  38. organization,
  39. });
  40. const trackDrawerMinimize = (organization: Organization) =>
  41. trackAnalytics('trace.trace_layout.drawer_minimize', {
  42. organization,
  43. });
  44. const trackShowInView = (organization: Organization) =>
  45. trackAnalytics('trace.trace_layout.show_in_view', {
  46. organization,
  47. });
  48. const trackViewEventJSON = (organization: Organization) =>
  49. trackAnalytics('trace.trace_layout.view_event_json', {
  50. organization,
  51. });
  52. const trackViewContinuousProfile = (organization: Organization) =>
  53. trackAnalytics('trace.trace_layout.view_continuous_profile', {
  54. organization,
  55. });
  56. const trackTabPin = (organization: Organization) =>
  57. trackAnalytics('trace.trace_layout.tab_pin', {
  58. organization,
  59. });
  60. const trackTabView = (tab: string, organization: Organization) =>
  61. trackAnalytics('trace.trace_layout.tab_view', {
  62. organization,
  63. tab,
  64. });
  65. const trackSearchFocus = (organization: Organization) =>
  66. trackAnalytics('trace.trace_layout.search_focus', {
  67. organization,
  68. });
  69. const trackResetZoom = (organization: Organization) =>
  70. trackAnalytics('trace.trace_layout.reset_zoom', {
  71. organization,
  72. });
  73. const trackPerformanceSetupChecklistTriggered = (organization: Organization) =>
  74. trackAnalytics('trace.quality.performance_setup.checklist_triggered', {
  75. organization,
  76. });
  77. const trackPerformanceSetupBannerLoaded = (organization: Organization) =>
  78. trackAnalytics('trace.quality.performance_setup.banner_loaded', {
  79. organization,
  80. });
  81. const trackQuotaExceededIncreaseBudgetClicked = (
  82. organization: Organization,
  83. traceType: string
  84. ) =>
  85. trackAnalytics('trace.quality.quota_exceeded.increase_budget_clicked', {
  86. organization,
  87. traceType,
  88. });
  89. const trackMissingSpansDocLinkClicked = (organization: Organization) =>
  90. trackAnalytics('trace.quality.missing_spans.doc_link_clicked', {
  91. organization,
  92. });
  93. const trackQuotaExceededLearnMoreClicked = (
  94. organization: Organization,
  95. traceType: string
  96. ) =>
  97. trackAnalytics('trace.quality.quota_exceeded.learn_more_clicked', {
  98. organization,
  99. traceType,
  100. });
  101. const trackQuotaExceededBannerLoaded = (organization: Organization, traceType: string) =>
  102. trackAnalytics('trace.quality.quota_exceeded.banner_loaded', {
  103. organization,
  104. traceType,
  105. });
  106. const trackPerformanceSetupLearnMoreClicked = (organization: Organization) =>
  107. trackAnalytics('trace.quality.performance_setup.learn_more_clicked', {
  108. organization,
  109. });
  110. const trackViewShortcuts = (organization: Organization) =>
  111. trackAnalytics('trace.trace_layout.view_shortcuts', {
  112. organization,
  113. });
  114. const trackTraceWarningType = (type: TraceShape, organization: Organization) =>
  115. trackAnalytics('trace.trace_warning_type', {
  116. organization,
  117. type,
  118. });
  119. const trackTraceConfigurationsDocsClicked = (organization: Organization, title: string) =>
  120. trackAnalytics('trace.configurations_docs_link_clicked', {
  121. organization,
  122. title,
  123. });
  124. const trackAutogroupingPreferenceChange = (
  125. organization: Organization,
  126. enabled: boolean
  127. ) =>
  128. trackAnalytics('trace.preferences.autogrouping_change', {
  129. organization,
  130. enabled,
  131. });
  132. const trackMissingInstrumentationPreferenceChange = (
  133. organization: Organization,
  134. enabled: boolean
  135. ) =>
  136. trackAnalytics('trace.preferences.missing_instrumentation_change', {
  137. organization,
  138. enabled,
  139. });
  140. function trackTraceShape(
  141. tree: TraceTree,
  142. projects: Project[],
  143. organization: Organization
  144. ) {
  145. switch (tree.shape) {
  146. case TraceShape.BROKEN_SUBTRACES:
  147. case TraceShape.EMPTY_TRACE:
  148. case TraceShape.MULTIPLE_ROOTS:
  149. case TraceShape.ONE_ROOT:
  150. case TraceShape.NO_ROOT:
  151. case TraceShape.ONLY_ERRORS:
  152. case TraceShape.BROWSER_MULTIPLE_ROOTS:
  153. traceAnalytics.trackTraceMetadata(tree, projects, organization);
  154. break;
  155. default: {
  156. Sentry.captureMessage('Unknown trace type');
  157. }
  158. }
  159. }
  160. const traceAnalytics = {
  161. // Trace shape
  162. trackTraceMetadata,
  163. trackTraceShape,
  164. trackEmptyTraceState,
  165. trackFailedToFetchTraceState,
  166. // Drawer actions
  167. trackShowInView,
  168. trackViewEventJSON,
  169. trackViewContinuousProfile,
  170. // Layout actions
  171. trackLayoutChange,
  172. trackDrawerMinimize,
  173. trackSearchFocus,
  174. trackTabPin,
  175. trackTabView,
  176. // Toolbar actions
  177. trackResetZoom,
  178. trackViewShortcuts,
  179. trackTraceWarningType,
  180. // Trace Quality Improvement
  181. trackPerformanceSetupChecklistTriggered,
  182. trackPerformanceSetupLearnMoreClicked,
  183. trackPerformanceSetupBannerLoaded,
  184. trackQuotaExceededIncreaseBudgetClicked,
  185. trackQuotaExceededLearnMoreClicked,
  186. trackQuotaExceededBannerLoaded,
  187. trackTraceConfigurationsDocsClicked,
  188. trackMissingSpansDocLinkClicked,
  189. // Trace Preferences
  190. trackAutogroupingPreferenceChange,
  191. trackMissingInstrumentationPreferenceChange,
  192. };
  193. export {traceAnalytics};