eventRegressionSummary.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {LinkButton} from 'sentry/components/button';
  4. import KeyValueList from 'sentry/components/events/interfaces/keyValueList';
  5. import {t} from 'sentry/locale';
  6. import type {Event} from 'sentry/types/event';
  7. import type {Group, KeyValueListData} from 'sentry/types/group';
  8. import {IssueType} from 'sentry/types/group';
  9. import type {Organization} from 'sentry/types/organization';
  10. import {defined} from 'sentry/utils';
  11. import {getFormattedDate} from 'sentry/utils/dates';
  12. import getDuration from 'sentry/utils/duration/getDuration';
  13. import {formatPercentage} from 'sentry/utils/number/formatPercentage';
  14. import useOrganization from 'sentry/utils/useOrganization';
  15. import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
  16. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  17. import {
  18. DisplayModes,
  19. transactionSummaryRouteWithQuery,
  20. } from 'sentry/views/performance/transactionSummary/utils';
  21. interface EventRegressionSummaryProps {
  22. event: Event;
  23. group: Group;
  24. }
  25. export function EventRegressionSummary({event, group}: EventRegressionSummaryProps) {
  26. const organization = useOrganization();
  27. const data = useMemo(
  28. () => getKeyValueListData(organization, group.issueType, event),
  29. [organization, event, group.issueType]
  30. );
  31. if (!defined(data)) {
  32. return null;
  33. }
  34. return (
  35. <InterimSection type={SectionKey.REGRESSION_SUMMARY} title={t('Regression Summary')}>
  36. <StyledKeyValueList data={data} shouldSort={false} />
  37. </InterimSection>
  38. );
  39. }
  40. export function getKeyValueListData(
  41. organization: Organization,
  42. issueType: IssueType,
  43. event: Event
  44. ): KeyValueListData | null {
  45. const evidenceData = event.occurrence?.evidenceData;
  46. if (!defined(evidenceData)) {
  47. return null;
  48. }
  49. switch (issueType) {
  50. case IssueType.PERFORMANCE_DURATION_REGRESSION:
  51. case IssueType.PERFORMANCE_ENDPOINT_REGRESSION: {
  52. const target = transactionSummaryRouteWithQuery({
  53. organization,
  54. transaction: evidenceData.transaction,
  55. query: {},
  56. trendFunction: 'p95',
  57. projectID: event.projectID,
  58. display: DisplayModes.TREND,
  59. });
  60. return [
  61. {
  62. key: 'endpoint',
  63. subject: t('Endpoint Name'),
  64. value: evidenceData.transaction,
  65. actionButton: (
  66. <LinkButton size="xs" to={target}>
  67. {t('View Transaction')}
  68. </LinkButton>
  69. ),
  70. },
  71. {
  72. key: 'duration change',
  73. subject: t('Change in Duration'),
  74. value: formatDurationChange(
  75. evidenceData.aggregateRange1 / 1e3,
  76. evidenceData.aggregateRange2 / 1e3,
  77. evidenceData.trendDifference,
  78. evidenceData.trendPercentage
  79. ),
  80. },
  81. {
  82. key: 'regression date',
  83. subject: t('Approx. Start Time'),
  84. value: formatBreakpoint(evidenceData.breakpoint),
  85. },
  86. ];
  87. }
  88. case IssueType.PROFILE_FUNCTION_REGRESSION_EXPERIMENTAL:
  89. case IssueType.PROFILE_FUNCTION_REGRESSION: {
  90. return [
  91. {
  92. key: 'function',
  93. subject: t('Function Name'),
  94. value: evidenceData?.function || t('unknown'),
  95. },
  96. {
  97. key: 'package',
  98. subject: t('Package Name'),
  99. value: evidenceData.package || evidenceData.module || t('unknown'),
  100. },
  101. {
  102. key: 'file',
  103. subject: t('File Name'),
  104. value: evidenceData.file || t('unknown'),
  105. },
  106. {
  107. key: 'duration change',
  108. subject: t('Change in Duration'),
  109. value: formatDurationChange(
  110. evidenceData.aggregateRange1 / 1e9,
  111. evidenceData.aggregateRange2 / 1e9,
  112. evidenceData.trendDifference,
  113. evidenceData.trendPercentage
  114. ),
  115. },
  116. {
  117. key: 'breakpoint',
  118. subject: t('Approx. Start Time'),
  119. value: formatBreakpoint(evidenceData.breakpoint),
  120. },
  121. ];
  122. }
  123. default:
  124. return null;
  125. }
  126. }
  127. function formatDurationChange(
  128. before: number,
  129. after: number,
  130. difference: number,
  131. percentage: number
  132. ) {
  133. return t(
  134. '%s to %s (%s%s)',
  135. getDuration(before, 0, true),
  136. getDuration(after, 0, true),
  137. difference > 0 ? '+' : difference < 0 ? '-' : '',
  138. formatPercentage(percentage - 1)
  139. );
  140. }
  141. function formatBreakpoint(breakpoint: number) {
  142. return getFormattedDate(breakpoint * 1000, 'MMM D, YYYY hh:mm:ss A z', {
  143. local: true,
  144. });
  145. }
  146. const StyledKeyValueList = styled(KeyValueList)`
  147. margin-bottom: 0 !important;
  148. `;