useTraceTimelineEvents.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import {useMemo} from 'react';
  2. import type {Event} from 'sentry/types/event';
  3. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  4. import {getTraceTimeRangeFromEvent} from 'sentry/utils/performance/quickTrace/utils';
  5. import {useApiQuery} from 'sentry/utils/queryClient';
  6. import useOrganization from 'sentry/utils/useOrganization';
  7. interface BaseEvent {
  8. id: string;
  9. 'issue.id': number;
  10. message: string;
  11. project: string;
  12. 'project.name': string;
  13. timestamp: string;
  14. title: string;
  15. transaction: string;
  16. }
  17. interface TimelineDiscoverEvent extends BaseEvent {}
  18. interface TimelineIssuePlatformEvent extends BaseEvent {
  19. 'event.type': string;
  20. 'stack.function': string[];
  21. }
  22. export type TimelineEvent = TimelineDiscoverEvent | TimelineIssuePlatformEvent;
  23. export interface TraceEventResponse {
  24. data: TimelineEvent[];
  25. meta: unknown;
  26. }
  27. interface UseTraceTimelineEventsOptions {
  28. event: Event;
  29. }
  30. export function useTraceTimelineEvents({event}: UseTraceTimelineEventsOptions): {
  31. endTimestamp: number;
  32. isError: boolean;
  33. isLoading: boolean;
  34. oneOtherIssueEvent: TimelineEvent | undefined;
  35. startTimestamp: number;
  36. traceEvents: TimelineEvent[];
  37. } {
  38. const organization = useOrganization();
  39. // If the org has global views, we want to look across all projects,
  40. // otherwise, just look at the current project.
  41. const hasGlobalViews = organization.features.includes('global-views');
  42. const project = hasGlobalViews ? -1 : event.projectID;
  43. const {start, end} = getTraceTimeRangeFromEvent(event);
  44. const traceId = event.contexts?.trace?.trace_id ?? '';
  45. const enabled = !!traceId;
  46. const {
  47. data: issuePlatformData,
  48. isLoading: isLoadingIssuePlatform,
  49. isError: isErrorIssuePlatform,
  50. } = useApiQuery<TraceEventResponse>(
  51. [
  52. `/organizations/${organization.slug}/events/`,
  53. {
  54. query: {
  55. // Get performance issues
  56. dataset: DiscoverDatasets.ISSUE_PLATFORM,
  57. field: ['message', 'title', 'project', 'timestamp', 'issue.id', 'transaction'],
  58. per_page: 100,
  59. query: `trace:${traceId}`,
  60. referrer: 'api.issues.issue_events',
  61. sort: '-timestamp',
  62. start,
  63. end,
  64. project: project,
  65. },
  66. },
  67. ],
  68. {staleTime: Infinity, retry: false, enabled}
  69. );
  70. const {
  71. data: discoverData,
  72. isLoading: isLoadingDiscover,
  73. isError: isErrorDiscover,
  74. } = useApiQuery<{
  75. data: TimelineEvent[];
  76. meta: unknown;
  77. }>(
  78. [
  79. `/organizations/${organization.slug}/events/`,
  80. {
  81. query: {
  82. // Other events
  83. dataset: DiscoverDatasets.DISCOVER,
  84. field: [
  85. 'message',
  86. 'title',
  87. 'project',
  88. 'timestamp',
  89. 'issue.id',
  90. 'transaction',
  91. 'event.type',
  92. 'stack.function',
  93. ],
  94. per_page: 100,
  95. query: `trace:${traceId}`,
  96. referrer: 'api.issues.issue_events',
  97. sort: '-timestamp',
  98. start,
  99. end,
  100. project: project,
  101. },
  102. },
  103. ],
  104. {staleTime: Infinity, retry: false, enabled}
  105. );
  106. const eventData = useMemo(() => {
  107. if (
  108. isLoadingIssuePlatform ||
  109. isLoadingDiscover ||
  110. isErrorIssuePlatform ||
  111. isErrorDiscover
  112. ) {
  113. return {
  114. data: [],
  115. startTimestamp: 0,
  116. endTimestamp: 0,
  117. };
  118. }
  119. // Events is unsorted since they're grouped by date later
  120. const events = [...issuePlatformData.data, ...discoverData.data];
  121. const oneOtherIssueEvent = getOneOtherIssueEvent(event, events);
  122. // The current event might be missing when there is a large number of issues
  123. const hasCurrentEvent = events.some(e => e.id === event.id);
  124. if (!hasCurrentEvent) {
  125. events.push({
  126. id: event.id,
  127. 'issue.id': Number(event.groupID),
  128. message: event.message,
  129. project: event.projectID,
  130. // The project name for current event is not used
  131. 'project.name': '',
  132. timestamp: event.dateCreated!,
  133. title: event.title,
  134. transaction: '',
  135. });
  136. }
  137. const timestamps = events.map(e => new Date(e.timestamp).getTime());
  138. const startTimestamp = Math.min(...timestamps);
  139. const endTimestamp = Math.max(...timestamps);
  140. return {
  141. data: events,
  142. startTimestamp,
  143. endTimestamp,
  144. oneOtherIssueEvent,
  145. };
  146. }, [
  147. event,
  148. issuePlatformData,
  149. discoverData,
  150. isLoadingIssuePlatform,
  151. isLoadingDiscover,
  152. isErrorIssuePlatform,
  153. isErrorDiscover,
  154. ]);
  155. return {
  156. traceEvents: eventData.data,
  157. startTimestamp: eventData.startTimestamp,
  158. endTimestamp: eventData.endTimestamp,
  159. isLoading: isLoadingIssuePlatform || isLoadingDiscover,
  160. isError: isErrorIssuePlatform || isErrorDiscover,
  161. oneOtherIssueEvent: eventData.oneOtherIssueEvent,
  162. };
  163. }
  164. function getOneOtherIssueEvent(
  165. event: Event,
  166. allTraceEvents: TimelineEvent[]
  167. ): TimelineEvent | undefined {
  168. const groupId = event.groupID;
  169. if (!groupId) {
  170. return undefined;
  171. }
  172. const otherIssues = allTraceEvents.filter(
  173. (_event, index, self) =>
  174. _event['issue.id'] !== undefined &&
  175. // Exclude the current issue
  176. _event['issue.id'] !== Number(groupId) &&
  177. self.findIndex(e => e['issue.id'] === _event['issue.id']) === index
  178. );
  179. return otherIssues.length === 1 ? otherIssues[0] : undefined;
  180. }