error.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 hasNewTraceUi={hasTraceNewUi}>
  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. <Description>
  55. {t(
  56. 'This error is related to an ongoing issue. For details about how many users this affects and more, go to the issue below.'
  57. )}
  58. </Description>
  59. <IssueList issues={issues} node={node} organization={organization} />
  60. </TraceDrawerComponents.DetailContainer>
  61. );
  62. }
  63. const Description = styled('div')`
  64. margin-bottom: ${space(2)};
  65. font-size: ${p => p.theme.fontSizeLarge};
  66. line-height: 1.5;
  67. text-align: left;
  68. `;
  69. function LegacyErrorNodeDetails({
  70. node,
  71. organization,
  72. onTabScrollToNode,
  73. onParentClick,
  74. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.TraceError>>) {
  75. const issues = useMemo(() => {
  76. return [...node.errors];
  77. }, [node.errors]);
  78. const {isPending, data} = useApiQuery<EventError>(
  79. [
  80. `/organizations/${organization.slug}/events/${node.value.project_slug}:${node.value.event_id}/`,
  81. ],
  82. {
  83. staleTime: 2 * 60 * 1000,
  84. }
  85. );
  86. const stackTrace = useMemo(() => {
  87. if (data) {
  88. return getStacktrace(data);
  89. }
  90. return null;
  91. }, [data]);
  92. const theme = useTheme();
  93. const parentTransaction = TraceTree.ParentTransaction(node);
  94. const items: SectionCardKeyValueList = [
  95. {
  96. key: 'title',
  97. subject: t('Title'),
  98. value: <TraceDrawerComponents.CopyableCardValueWithLink value={node.value.title} />,
  99. },
  100. ];
  101. if (parentTransaction) {
  102. items.push({
  103. key: 'parent_transaction',
  104. subject: t('Parent Transaction'),
  105. value: (
  106. <a onClick={() => onParentClick(parentTransaction)}>
  107. {getTraceTabTitle(parentTransaction)}
  108. </a>
  109. ),
  110. });
  111. }
  112. return isPending ? (
  113. <LoadingIndicator />
  114. ) : data ? (
  115. <TraceDrawerComponents.DetailContainer>
  116. <TraceDrawerComponents.LegacyHeaderContainer>
  117. <TraceDrawerComponents.Title>
  118. <TraceDrawerComponents.IconBorder
  119. backgroundColor={makeTraceNodeBarColor(theme, node)}
  120. >
  121. <TraceIcons.Icon event={node.value} />
  122. </TraceDrawerComponents.IconBorder>
  123. <TraceDrawerComponents.LegacyTitleText>
  124. <div>{node.value.level ?? t('error')}</div>
  125. <TraceDrawerComponents.TitleOp
  126. text={node.value.message ?? node.value.title ?? 'Error'}
  127. />
  128. </TraceDrawerComponents.LegacyTitleText>
  129. </TraceDrawerComponents.Title>
  130. <TraceDrawerComponents.Actions>
  131. <TraceDrawerComponents.NodeActions
  132. node={node}
  133. organization={organization}
  134. onTabScrollToNode={onTabScrollToNode}
  135. />
  136. <LinkButton size="xs" to={generateIssueEventTarget(node.value, organization)}>
  137. {t('Go to Issue')}
  138. </LinkButton>
  139. </TraceDrawerComponents.Actions>
  140. </TraceDrawerComponents.LegacyHeaderContainer>
  141. <IssueList issues={issues} node={node} organization={organization} />
  142. <TraceDrawerComponents.SectionCard
  143. items={[
  144. {
  145. key: 'stack_trace',
  146. subject: t('Stack Trace'),
  147. subjectNode: null,
  148. value:
  149. stackTrace && data ? (
  150. <StackTraceWrapper>
  151. <StackTracePreviewContent event={data} stacktrace={stackTrace} />
  152. </StackTraceWrapper>
  153. ) : (
  154. t('No stack trace has been reported with this error')
  155. ),
  156. },
  157. ]}
  158. title={t('Stack Trace')}
  159. />
  160. <TraceDrawerComponents.SectionCard items={items} title={t('General')} />
  161. <TraceDrawerComponents.EventTags
  162. projectSlug={node.value.project_slug}
  163. event={data}
  164. />
  165. </TraceDrawerComponents.DetailContainer>
  166. ) : null;
  167. }
  168. const StackTraceWrapper = styled('div')`
  169. .traceback {
  170. margin-bottom: 0;
  171. border: 0;
  172. }
  173. `;