error.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {Location} from 'history';
  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, Organization} from 'sentry/types';
  15. import {useApiQuery} from 'sentry/utils/queryClient';
  16. import {Row, Tags} from 'sentry/views/performance/traceDetails/styles';
  17. import type {TraceTree, TraceTreeNode} from '../../traceTree';
  18. import {TraceDrawerComponents} from './styles';
  19. export function ErrorNodeDetails({
  20. node,
  21. organization,
  22. location,
  23. scrollToNode,
  24. }: {
  25. location: Location;
  26. node: TraceTreeNode<TraceTree.TraceError>;
  27. organization: Organization;
  28. scrollToNode: (node: TraceTreeNode<TraceTree.NodeValue>) => void;
  29. }) {
  30. const {isLoading, data} = useApiQuery<EventError>(
  31. [
  32. `/organizations/${organization.slug}/events/${node.value.project_slug}:${node.value.event_id}/`,
  33. ],
  34. {
  35. staleTime: 2 * 60 * 1000,
  36. }
  37. );
  38. const stackTrace = useMemo(() => {
  39. if (data) {
  40. return getStacktrace(data);
  41. }
  42. return null;
  43. }, [data]);
  44. return isLoading ? (
  45. <LoadingIndicator />
  46. ) : data ? (
  47. <TraceDrawerComponents.DetailContainer>
  48. <TraceDrawerComponents.HeaderContainer>
  49. <TraceDrawerComponents.IconTitleWrapper>
  50. <TraceDrawerComponents.IconBorder errored>
  51. <IconFire color="errorText" size="md" />
  52. </TraceDrawerComponents.IconBorder>
  53. <div style={{fontWeight: 'bold'}}>{t('Error')}</div>
  54. </TraceDrawerComponents.IconTitleWrapper>
  55. <TraceDrawerComponents.Actions>
  56. <Button size="xs" onClick={_e => scrollToNode(node)}>
  57. {t('Show in view')}
  58. </Button>
  59. <Button size="xs" to={generateIssueEventTarget(node.value, organization)}>
  60. {t('Go to Issue')}
  61. </Button>
  62. </TraceDrawerComponents.Actions>
  63. </TraceDrawerComponents.HeaderContainer>
  64. <TraceDrawerComponents.Table className="table key-value">
  65. <tbody>
  66. {stackTrace && (
  67. <tr>
  68. <StackTraceTitle>{t('Stack Trace')}</StackTraceTitle>
  69. <StackTraceWrapper>
  70. <StackTracePreviewContent event={data} stacktrace={stackTrace} />
  71. </StackTraceWrapper>
  72. </tr>
  73. )}
  74. <Tags
  75. enableHiding
  76. location={location}
  77. organization={organization}
  78. event={node.value}
  79. tags={data.tags}
  80. />
  81. <Row
  82. title={t('Title')}
  83. extra={<CopyToClipboardButton size="xs" borderless text={node.value.title} />}
  84. >
  85. {node.value.title}
  86. </Row>
  87. </tbody>
  88. </TraceDrawerComponents.Table>
  89. </TraceDrawerComponents.DetailContainer>
  90. ) : null;
  91. }
  92. const StackTraceWrapper = styled('td')`
  93. .traceback {
  94. margin-bottom: 0;
  95. border: 0;
  96. }
  97. `;
  98. const StackTraceTitle = styled('td')`
  99. font-weight: 600;
  100. font-size: 13px;
  101. width: 175px;
  102. `;