useAnalytics.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import {useEffect} from 'react';
  2. import {defined} from 'sentry/utils';
  3. import {trackAnalytics} from 'sentry/utils/analytics';
  4. import {dedupeArray} from 'sentry/utils/dedupeArray';
  5. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  6. import useOrganization from 'sentry/utils/useOrganization';
  7. import {
  8. useExploreDataset,
  9. useExploreFields,
  10. useExploreQuery,
  11. useExploreTitle,
  12. useExploreVisualizes,
  13. } from 'sentry/views/explore/contexts/pageParamsContext';
  14. import type {Visualize} from 'sentry/views/explore/contexts/pageParamsContext/visualizes';
  15. import type {AggregatesTableResult} from 'sentry/views/explore/hooks/useExploreAggregatesTable';
  16. import type {SpansTableResult} from 'sentry/views/explore/hooks/useExploreSpansTable';
  17. import type {TracesTableResult} from 'sentry/views/explore/hooks/useExploreTracesTable';
  18. import {combineConfidenceForSeries} from 'sentry/views/explore/utils';
  19. import type {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries';
  20. import {usePerformanceSubscriptionDetails} from 'sentry/views/performance/newTraceDetails/traceTypeWarnings/usePerformanceSubscriptionDetails';
  21. export function useAnalytics({
  22. queryType,
  23. aggregatesTableResult,
  24. spansTableResult,
  25. tracesTableResult,
  26. timeseriesResult,
  27. }: {
  28. aggregatesTableResult: AggregatesTableResult;
  29. queryType: 'aggregate' | 'samples' | 'traces';
  30. spansTableResult: SpansTableResult;
  31. timeseriesResult: ReturnType<typeof useSortedTimeSeries>;
  32. tracesTableResult: TracesTableResult;
  33. }) {
  34. const organization = useOrganization();
  35. const dataset = useExploreDataset();
  36. const title = useExploreTitle();
  37. const query = useExploreQuery();
  38. const fields = useExploreFields();
  39. const visualizes = useExploreVisualizes();
  40. const {
  41. data: {hasExceededPerformanceUsageLimit},
  42. isLoading: isLoadingSubscriptionDetails,
  43. } = usePerformanceSubscriptionDetails();
  44. useEffect(() => {
  45. if (
  46. queryType !== 'aggregate' ||
  47. aggregatesTableResult.result.isPending ||
  48. timeseriesResult.isPending ||
  49. isLoadingSubscriptionDetails
  50. ) {
  51. return;
  52. }
  53. const search = new MutableSearch(query);
  54. const columns = aggregatesTableResult.eventView.getColumns() as unknown as string[];
  55. trackAnalytics('trace.explorer.metadata', {
  56. organization,
  57. dataset,
  58. result_mode: 'aggregates',
  59. columns,
  60. columns_count: columns.length,
  61. query_status: aggregatesTableResult.result.status,
  62. result_length: aggregatesTableResult.result.data?.length || 0,
  63. result_missing_root: 0,
  64. user_queries: search.formatString(),
  65. user_queries_count: search.tokens.length,
  66. visualizes,
  67. visualizes_count: visualizes.length,
  68. title: title || '',
  69. confidences: computeConfidence(visualizes, timeseriesResult.data),
  70. has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit,
  71. });
  72. }, [
  73. organization,
  74. dataset,
  75. fields,
  76. query,
  77. visualizes,
  78. title,
  79. queryType,
  80. aggregatesTableResult.result.isPending,
  81. aggregatesTableResult.result.status,
  82. aggregatesTableResult.result.data?.length,
  83. aggregatesTableResult.eventView,
  84. timeseriesResult.isPending,
  85. timeseriesResult.data,
  86. hasExceededPerformanceUsageLimit,
  87. isLoadingSubscriptionDetails,
  88. ]);
  89. useEffect(() => {
  90. if (
  91. queryType !== 'samples' ||
  92. spansTableResult.result.isPending ||
  93. timeseriesResult.isPending ||
  94. isLoadingSubscriptionDetails
  95. ) {
  96. return;
  97. }
  98. const search = new MutableSearch(query);
  99. trackAnalytics('trace.explorer.metadata', {
  100. organization,
  101. dataset,
  102. result_mode: 'span samples',
  103. columns: fields,
  104. columns_count: fields.length,
  105. query_status: spansTableResult.result.status,
  106. result_length: spansTableResult.result.data?.length || 0,
  107. result_missing_root: 0,
  108. user_queries: search.formatString(),
  109. user_queries_count: search.tokens.length,
  110. visualizes,
  111. visualizes_count: visualizes.length,
  112. title: title || '',
  113. confidences: computeConfidence(visualizes, timeseriesResult.data),
  114. has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit,
  115. });
  116. }, [
  117. organization,
  118. dataset,
  119. fields,
  120. query,
  121. visualizes,
  122. title,
  123. queryType,
  124. spansTableResult.result.isPending,
  125. spansTableResult.result.status,
  126. spansTableResult.result.data?.length,
  127. timeseriesResult.isPending,
  128. timeseriesResult.data,
  129. hasExceededPerformanceUsageLimit,
  130. isLoadingSubscriptionDetails,
  131. ]);
  132. useEffect(() => {
  133. if (
  134. queryType !== 'traces' ||
  135. tracesTableResult.result.isPending ||
  136. timeseriesResult.isPending ||
  137. isLoadingSubscriptionDetails
  138. ) {
  139. return;
  140. }
  141. const search = new MutableSearch(query);
  142. const columns = [
  143. 'trace id',
  144. 'trace root',
  145. 'total spans',
  146. 'timeline',
  147. 'root duration',
  148. 'timestamp',
  149. ];
  150. const resultMissingRoot =
  151. tracesTableResult.result?.data?.data?.filter(trace => !defined(trace.name))
  152. .length ?? 0;
  153. trackAnalytics('trace.explorer.metadata', {
  154. organization,
  155. dataset,
  156. result_mode: 'trace samples',
  157. columns,
  158. columns_count: columns.length,
  159. query_status: tracesTableResult.result.status,
  160. result_length: tracesTableResult.result.data?.data?.length || 0,
  161. result_missing_root: resultMissingRoot,
  162. user_queries: search.formatString(),
  163. user_queries_count: search.tokens.length,
  164. visualizes,
  165. visualizes_count: visualizes.length,
  166. title: title || '',
  167. confidences: computeConfidence(visualizes, timeseriesResult.data),
  168. has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit,
  169. });
  170. }, [
  171. organization,
  172. dataset,
  173. fields,
  174. query,
  175. visualizes,
  176. title,
  177. queryType,
  178. tracesTableResult.result.isPending,
  179. tracesTableResult.result.status,
  180. tracesTableResult.result.data?.data,
  181. timeseriesResult.isPending,
  182. timeseriesResult.data,
  183. hasExceededPerformanceUsageLimit,
  184. isLoadingSubscriptionDetails,
  185. ]);
  186. }
  187. function computeConfidence(
  188. visualizes: Visualize[],
  189. data: ReturnType<typeof useSortedTimeSeries>['data']
  190. ) {
  191. return visualizes.map(visualize => {
  192. const dedupedYAxes = dedupeArray(visualize.yAxes);
  193. const series = dedupedYAxes.flatMap(yAxis => data[yAxis]).filter(defined);
  194. return combineConfidenceForSeries(series);
  195. });
  196. }