error.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import {useMemo} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {LinkButton} from 'sentry/components/button';
  5. import {
  6. getStacktrace,
  7. StackTracePreviewContent,
  8. } from 'sentry/components/groupPreviewTooltip/stackTracePreview';
  9. import LoadingIndicator from 'sentry/components/loadingIndicator';
  10. import {generateIssueEventTarget} from 'sentry/components/quickTrace/utils';
  11. import {t} from 'sentry/locale';
  12. import {space} from 'sentry/styles/space';
  13. import type {EventError} from 'sentry/types/event';
  14. import {useApiQuery} from 'sentry/utils/queryClient';
  15. import type {TraceTreeNodeDetailsProps} from '../../traceDrawer/tabs/traceTreeNodeDetails';
  16. import {TraceIcons} from '../../traceIcons';
  17. import {TraceTree} from '../../traceModels/traceTree';
  18. import type {TraceTreeNode} from '../../traceModels/traceTreeNode';
  19. import {makeTraceNodeBarColor} from '../../traceRow/traceBar';
  20. import {getTraceTabTitle} from '../../traceState/traceTabs';
  21. import {useHasTraceNewUi} from '../../useHasTraceNewUi';
  22. import {IssueList} from './issues/issues';
  23. import {type SectionCardKeyValueList, TraceDrawerComponents} from './styles';
  24. export function ErrorNodeDetails(
  25. props: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.TraceError>>
  26. ) {
  27. const hasTraceNewUi = useHasTraceNewUi();
  28. const {node, organization, onTabScrollToNode} = props;
  29. const issues = useMemo(() => {
  30. return [...node.errors];
  31. }, [node.errors]);
  32. if (!hasTraceNewUi) {
  33. return <LegacyErrorNodeDetails {...props} />;
  34. }
  35. return (
  36. <TraceDrawerComponents.DetailContainer>
  37. <TraceDrawerComponents.HeaderContainer>
  38. <TraceDrawerComponents.Title>
  39. <TraceDrawerComponents.LegacyTitleText>
  40. <TraceDrawerComponents.TitleText>
  41. {t('Error')}
  42. </TraceDrawerComponents.TitleText>
  43. <TraceDrawerComponents.SubtitleWithCopyButton
  44. text={`ID: ${props.node.value.event_id}`}
  45. />
  46. </TraceDrawerComponents.LegacyTitleText>
  47. </TraceDrawerComponents.Title>
  48. <TraceDrawerComponents.NodeActions
  49. node={node}
  50. organization={organization}
  51. onTabScrollToNode={onTabScrollToNode}
  52. />
  53. </TraceDrawerComponents.HeaderContainer>
  54. <TraceDrawerComponents.BodyContainer hasNewTraceUi={hasTraceNewUi}>
  55. <Description>
  56. {t(
  57. 'This error is related to an ongoing issue. For details about how many users this affects and more, go to the issue below.'
  58. )}
  59. </Description>
  60. <IssueList issues={issues} node={node} organization={organization} />
  61. </TraceDrawerComponents.BodyContainer>
  62. </TraceDrawerComponents.DetailContainer>
  63. );
  64. }
  65. const Description = styled('div')`
  66. margin-bottom: ${space(2)};
  67. font-size: ${p => p.theme.fontSizeLarge};
  68. line-height: 1.5;
  69. text-align: left;
  70. `;
  71. function LegacyErrorNodeDetails({
  72. node,
  73. organization,
  74. onTabScrollToNode,
  75. onParentClick,
  76. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.TraceError>>) {
  77. const issues = useMemo(() => {
  78. return [...node.errors];
  79. }, [node.errors]);
  80. const {isPending, data} = useApiQuery<EventError>(
  81. [
  82. `/organizations/${organization.slug}/events/${node.value.project_slug}:${node.value.event_id}/`,
  83. ],
  84. {
  85. staleTime: 2 * 60 * 1000,
  86. }
  87. );
  88. const stackTrace = useMemo(() => {
  89. if (data) {
  90. return getStacktrace(data);
  91. }
  92. return null;
  93. }, [data]);
  94. const theme = useTheme();
  95. const parentTransaction = TraceTree.ParentTransaction(node);
  96. const items: SectionCardKeyValueList = [
  97. {
  98. key: 'title',
  99. subject: t('Title'),
  100. value: <TraceDrawerComponents.CopyableCardValueWithLink value={node.value.title} />,
  101. },
  102. ];
  103. if (parentTransaction) {
  104. items.push({
  105. key: 'parent_transaction',
  106. subject: t('Parent Transaction'),
  107. value: (
  108. <a onClick={() => onParentClick(parentTransaction)}>
  109. {getTraceTabTitle(parentTransaction)}
  110. </a>
  111. ),
  112. });
  113. }
  114. return isPending ? (
  115. <LoadingIndicator />
  116. ) : data ? (
  117. <TraceDrawerComponents.DetailContainer>
  118. <TraceDrawerComponents.LegacyHeaderContainer>
  119. <TraceDrawerComponents.Title>
  120. <TraceDrawerComponents.IconBorder
  121. backgroundColor={makeTraceNodeBarColor(theme, node)}
  122. >
  123. <TraceIcons.Icon event={node.value} />
  124. </TraceDrawerComponents.IconBorder>
  125. <TraceDrawerComponents.LegacyTitleText>
  126. <div>{node.value.level ?? t('error')}</div>
  127. <TraceDrawerComponents.TitleOp
  128. text={node.value.message ?? node.value.title ?? 'Error'}
  129. />
  130. </TraceDrawerComponents.LegacyTitleText>
  131. </TraceDrawerComponents.Title>
  132. <TraceDrawerComponents.Actions>
  133. <TraceDrawerComponents.NodeActions
  134. node={node}
  135. organization={organization}
  136. onTabScrollToNode={onTabScrollToNode}
  137. />
  138. <LinkButton size="xs" to={generateIssueEventTarget(node.value, organization)}>
  139. {t('Go to Issue')}
  140. </LinkButton>
  141. </TraceDrawerComponents.Actions>
  142. </TraceDrawerComponents.LegacyHeaderContainer>
  143. <TraceDrawerComponents.BodyContainer>
  144. <IssueList issues={issues} node={node} organization={organization} />
  145. <TraceDrawerComponents.SectionCard
  146. items={[
  147. {
  148. key: 'stack_trace',
  149. subject: t('Stack Trace'),
  150. subjectNode: null,
  151. value:
  152. stackTrace && data ? (
  153. <StackTraceWrapper>
  154. <StackTracePreviewContent event={data} stacktrace={stackTrace} />
  155. </StackTraceWrapper>
  156. ) : (
  157. t('No stack trace has been reported with this error')
  158. ),
  159. },
  160. ]}
  161. title={t('Stack Trace')}
  162. />
  163. <TraceDrawerComponents.SectionCard items={items} title={t('General')} />
  164. <TraceDrawerComponents.EventTags
  165. projectSlug={node.value.project_slug}
  166. event={data}
  167. />
  168. </TraceDrawerComponents.BodyContainer>
  169. </TraceDrawerComponents.DetailContainer>
  170. ) : null;
  171. }
  172. const StackTraceWrapper = styled('div')`
  173. .traceback {
  174. margin-bottom: 0;
  175. border: 0;
  176. }
  177. `;