eventContext.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import {Fragment, useEffect} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Location} from 'history';
  4. import {
  5. getStacktrace,
  6. StackTracePreviewContent,
  7. } from 'sentry/components/groupPreviewTooltip/stackTracePreview';
  8. import {t} from 'sentry/locale';
  9. import space from 'sentry/styles/space';
  10. import {Event, Project} from 'sentry/types';
  11. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  12. import EventView from 'sentry/utils/discover/eventView';
  13. import {getDuration} from 'sentry/utils/formatters';
  14. import TraceMetaQuery from 'sentry/utils/performance/quickTrace/traceMetaQuery';
  15. import {getTraceTimeRangeFromEvent} from 'sentry/utils/performance/quickTrace/utils';
  16. import {useQuery} from 'sentry/utils/queryClient';
  17. import {
  18. getStatusBodyText,
  19. HttpStatus,
  20. } from 'sentry/views/performance/transactionDetails/eventMetas';
  21. import ActionDropDown, {ContextValueType} from './actionDropdown';
  22. import {NoContext} from './quickContextWrapper';
  23. import {
  24. ContextBody,
  25. ContextContainer,
  26. ContextHeader,
  27. ContextRow,
  28. ContextTitle,
  29. NoContextWrapper,
  30. Wrapper,
  31. } from './styles';
  32. import {BaseContextProps, ContextType, tenSecondInMs} from './utils';
  33. interface EventContextProps extends BaseContextProps {
  34. eventView?: EventView;
  35. location?: Location;
  36. projects?: Project[];
  37. }
  38. function EventContext(props: EventContextProps) {
  39. const {organization, dataRow, eventView, location, projects} = props;
  40. const {isLoading, isError, data} = useQuery<Event>(
  41. [
  42. `/organizations/${organization.slug}/events/${dataRow['project.name']}:${dataRow.id}/`,
  43. ],
  44. {
  45. staleTime: tenSecondInMs,
  46. }
  47. );
  48. useEffect(() => {
  49. if (data) {
  50. trackAdvancedAnalyticsEvent('discover_v2.quick_context_hover_contexts', {
  51. organization,
  52. contextType: ContextType.EVENT,
  53. eventType: data.type,
  54. });
  55. }
  56. }, [data, organization]);
  57. if (isLoading || isError) {
  58. return <NoContext isLoading={isLoading} />;
  59. }
  60. if (data.type === 'transaction') {
  61. const traceId = data.contexts?.trace?.trace_id ?? '';
  62. const {start, end} = getTraceTimeRangeFromEvent(data);
  63. const project = projects?.find(p => p.slug === data.projectID);
  64. const transactionDuration = getDuration(
  65. data.endTimestamp - data.startTimestamp,
  66. 2,
  67. true
  68. );
  69. return (
  70. <Wrapper data-test-id="quick-context-hover-body">
  71. <EventContextContainer>
  72. <ContextHeader>
  73. <ContextTitle>{t('Transaction Duration')}</ContextTitle>
  74. {location && eventView && (
  75. <ActionDropDown
  76. dataRow={dataRow}
  77. contextValueType={ContextValueType.DURATION}
  78. location={location}
  79. eventView={eventView}
  80. organization={organization}
  81. queryKey="transaction.duration"
  82. value={transactionDuration}
  83. />
  84. )}
  85. </ContextHeader>
  86. <EventContextBody>{transactionDuration}</EventContextBody>
  87. </EventContextContainer>
  88. {location && (
  89. <EventContextContainer>
  90. <TraceMetaQuery
  91. location={location}
  92. orgSlug={organization.slug}
  93. traceId={traceId}
  94. start={start}
  95. end={end}
  96. >
  97. {metaResults => {
  98. const status = getStatusBodyText(project, data, metaResults?.meta);
  99. return (
  100. <Fragment>
  101. <ContextHeader>
  102. <ContextTitle>{t('Status')}</ContextTitle>
  103. {location && eventView && (
  104. <ActionDropDown
  105. dataRow={dataRow}
  106. contextValueType={ContextValueType.STRING}
  107. location={location}
  108. eventView={eventView}
  109. organization={organization}
  110. queryKey="transaction.status"
  111. value={status}
  112. />
  113. )}
  114. </ContextHeader>
  115. <EventContextBody>
  116. <ContextRow>
  117. {status}
  118. <HttpStatusWrapper>
  119. (<HttpStatus event={data} />)
  120. </HttpStatusWrapper>
  121. </ContextRow>
  122. </EventContextBody>
  123. </Fragment>
  124. );
  125. }}
  126. </TraceMetaQuery>
  127. </EventContextContainer>
  128. )}
  129. </Wrapper>
  130. );
  131. }
  132. const stackTrace = getStacktrace(data);
  133. return stackTrace ? (
  134. <Fragment>
  135. {!dataRow.title && (
  136. <ErrorTitleContainer>
  137. <ContextHeader>
  138. <ContextTitle>{t('Title')}</ContextTitle>
  139. {location && eventView && (
  140. <ActionDropDown
  141. dataRow={dataRow}
  142. contextValueType={ContextValueType.STRING}
  143. location={location}
  144. eventView={eventView}
  145. organization={organization}
  146. queryKey="title"
  147. value={data.title}
  148. />
  149. )}
  150. </ContextHeader>
  151. <ErrorTitleBody>{data.title}</ErrorTitleBody>
  152. </ErrorTitleContainer>
  153. )}
  154. <StackTraceWrapper>
  155. <StackTracePreviewContent event={data} stacktrace={stackTrace} />
  156. </StackTraceWrapper>
  157. </Fragment>
  158. ) : (
  159. <NoContextWrapper>
  160. {t('There is no stack trace available for this event.')}
  161. </NoContextWrapper>
  162. );
  163. }
  164. const ErrorTitleContainer = styled(ContextContainer)`
  165. padding: ${space(1.5)};
  166. `;
  167. const ErrorTitleBody = styled(ContextBody)`
  168. margin: 0;
  169. max-width: 450px;
  170. ${p => p.theme.overflowEllipsis}
  171. `;
  172. const EventContextBody = styled(ContextBody)`
  173. font-size: ${p => p.theme.fontSizeExtraLarge};
  174. margin: 0;
  175. align-items: flex-start;
  176. flex-direction: column;
  177. `;
  178. const EventContextContainer = styled(ContextContainer)`
  179. & + & {
  180. margin-top: ${space(2)};
  181. }
  182. `;
  183. const StackTraceWrapper = styled('div')`
  184. overflow: hidden;
  185. max-height: 300px;
  186. width: 500px;
  187. overflow-y: auto;
  188. .traceback {
  189. margin-bottom: 0;
  190. border: 0;
  191. box-shadow: none;
  192. }
  193. border-radius: ${p => p.theme.borderRadius};
  194. `;
  195. const HttpStatusWrapper = styled('span')`
  196. margin-left: ${space(0.5)};
  197. `;
  198. export default EventContext;