error.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import {useMemo} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {Button} from 'sentry/components/button';
  5. import {CopyToClipboardButton} from 'sentry/components/copyToClipboardButton';
  6. import {
  7. getStacktrace,
  8. StackTracePreviewContent,
  9. } from 'sentry/components/groupPreviewTooltip/stackTracePreview';
  10. import LoadingIndicator from 'sentry/components/loadingIndicator';
  11. import {generateIssueEventTarget} from 'sentry/components/quickTrace/utils';
  12. import {IconFire} from 'sentry/icons';
  13. import {t} from 'sentry/locale';
  14. import type {EventError} from 'sentry/types';
  15. import {useApiQuery} from 'sentry/utils/queryClient';
  16. import {useLocation} from 'sentry/utils/useLocation';
  17. import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails';
  18. import {getTraceTabTitle} from 'sentry/views/performance/newTraceDetails/traceTabs';
  19. import {Row, Tags} from 'sentry/views/performance/traceDetails/styles';
  20. import {makeTraceNodeBarColor, type TraceTree, type TraceTreeNode} from '../../traceTree';
  21. import {IssueList} from './issues/issues';
  22. import {TraceDrawerComponents} from './styles';
  23. export function ErrorNodeDetails({
  24. node,
  25. organization,
  26. scrollToNode,
  27. onParentClick,
  28. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.TraceError>>) {
  29. const location = useLocation();
  30. const issues = useMemo(() => {
  31. return [...node.errors];
  32. }, [node.errors]);
  33. const {isLoading, data} = useApiQuery<EventError>(
  34. [
  35. `/organizations/${organization.slug}/events/${node.value.project_slug}:${node.value.event_id}/`,
  36. ],
  37. {
  38. staleTime: 2 * 60 * 1000,
  39. }
  40. );
  41. const stackTrace = useMemo(() => {
  42. if (data) {
  43. return getStacktrace(data);
  44. }
  45. return null;
  46. }, [data]);
  47. const theme = useTheme();
  48. const parentTransaction = node.parent_transaction;
  49. return isLoading ? (
  50. <LoadingIndicator />
  51. ) : data ? (
  52. <TraceDrawerComponents.DetailContainer>
  53. <TraceDrawerComponents.HeaderContainer>
  54. <TraceDrawerComponents.Title>
  55. <TraceDrawerComponents.IconBorder
  56. backgroundColor={makeTraceNodeBarColor(theme, node)}
  57. >
  58. <IconFire size="md" />
  59. </TraceDrawerComponents.IconBorder>
  60. <TraceDrawerComponents.TitleText>
  61. <div>{t('error')}</div>
  62. <TraceDrawerComponents.TitleOp>
  63. {' '}
  64. {node.value.title}
  65. </TraceDrawerComponents.TitleOp>
  66. </TraceDrawerComponents.TitleText>
  67. </TraceDrawerComponents.Title>
  68. <TraceDrawerComponents.Actions>
  69. <Button size="xs" onClick={_e => scrollToNode(node)}>
  70. {t('Show in view')}
  71. </Button>
  72. <TraceDrawerComponents.EventDetailsLink
  73. eventId={node.value.event_id}
  74. projectSlug={node.metadata.project_slug}
  75. />
  76. <Button size="xs" to={generateIssueEventTarget(node.value, organization)}>
  77. {t('Go to Issue')}
  78. </Button>
  79. </TraceDrawerComponents.Actions>
  80. </TraceDrawerComponents.HeaderContainer>
  81. <IssueList issues={issues} node={node} organization={organization} />
  82. <TraceDrawerComponents.Table className="table key-value">
  83. <tbody>
  84. {stackTrace ? (
  85. <tr>
  86. <StackTraceTitle>{t('Stack Trace')}</StackTraceTitle>
  87. <StackTraceWrapper>
  88. <StackTracePreviewContent event={data} stacktrace={stackTrace} />
  89. </StackTraceWrapper>
  90. </tr>
  91. ) : (
  92. <Row title={t('Stack Trace')}>
  93. {t('No stack trace has been reported with this error')}
  94. </Row>
  95. )}
  96. <Tags
  97. enableHiding
  98. location={location}
  99. organization={organization}
  100. event={node.value}
  101. tags={data.tags}
  102. />
  103. <Row
  104. title={t('Title')}
  105. extra={<CopyToClipboardButton size="xs" borderless text={node.value.title} />}
  106. >
  107. {node.value.title}
  108. </Row>
  109. {parentTransaction ? (
  110. <Row title="Parent Transaction">
  111. <td className="value">
  112. <a href="#" onClick={() => onParentClick(parentTransaction)}>
  113. {getTraceTabTitle(parentTransaction)}
  114. </a>
  115. </td>
  116. </Row>
  117. ) : null}
  118. </tbody>
  119. </TraceDrawerComponents.Table>
  120. </TraceDrawerComponents.DetailContainer>
  121. ) : null;
  122. }
  123. const StackTraceWrapper = styled('td')`
  124. .traceback {
  125. margin-bottom: 0;
  126. border: 0;
  127. }
  128. `;
  129. const StackTraceTitle = styled('td')`
  130. font-weight: 600;
  131. font-size: 13px;
  132. width: 175px;
  133. `;