regressionMessage.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {LinkButton} from 'sentry/components/button';
  4. import {DateTime} from 'sentry/components/dateTime';
  5. import {DataSection} from 'sentry/components/events/styles';
  6. import Link from 'sentry/components/links/link';
  7. import PerformanceDuration from 'sentry/components/performanceDuration';
  8. import {Tooltip} from 'sentry/components/tooltip';
  9. import {IconOpen} from 'sentry/icons';
  10. import {t, tct} from 'sentry/locale';
  11. import {space} from 'sentry/styles/space';
  12. import type {Event, Group} from 'sentry/types';
  13. import {IssueType} from 'sentry/types';
  14. import {defined} from 'sentry/utils';
  15. import {formatPercentage} from 'sentry/utils/number/formatPercentage';
  16. import {useRelativeDateTime} from 'sentry/utils/profiling/hooks/useRelativeDateTime';
  17. import normalizeUrl from 'sentry/utils/url/normalizeUrl';
  18. import useOrganization from 'sentry/utils/useOrganization';
  19. import {
  20. DisplayModes,
  21. transactionSummaryRouteWithQuery,
  22. } from 'sentry/views/performance/transactionSummary/utils';
  23. type EventStatisticalDetectorMessageProps = {
  24. event: Event;
  25. group: Group;
  26. };
  27. function EventStatisticalDetectorMessage({
  28. event,
  29. group,
  30. }: EventStatisticalDetectorMessageProps) {
  31. switch (group.issueType) {
  32. case IssueType.PERFORMANCE_DURATION_REGRESSION:
  33. case IssueType.PERFORMANCE_ENDPOINT_REGRESSION: {
  34. return (
  35. <EventStatisticalDetectorRegressedPerformanceMessage
  36. event={event}
  37. group={group}
  38. />
  39. );
  40. }
  41. case IssueType.PROFILE_FUNCTION_REGRESSION_EXPERIMENTAL:
  42. case IssueType.PROFILE_FUNCTION_REGRESSION: {
  43. return (
  44. <EventStatisticalDetectorRegressedFunctionMessage event={event} group={group} />
  45. );
  46. }
  47. default: {
  48. return null;
  49. }
  50. }
  51. }
  52. function EventStatisticalDetectorRegressedPerformanceMessage({
  53. event,
  54. }: EventStatisticalDetectorMessageProps) {
  55. const now = useMemo(() => new Date(), []);
  56. const organization = useOrganization();
  57. const {transaction, breakpoint, aggregateRange1, aggregateRange2, trendPercentage} =
  58. event?.occurrence?.evidenceData ?? {};
  59. const {end: afterDateTime} = useRelativeDateTime({
  60. anchor: breakpoint,
  61. relativeDays: 14,
  62. });
  63. const transactionSummaryLink = transactionSummaryRouteWithQuery({
  64. orgSlug: organization.slug,
  65. transaction,
  66. query: {},
  67. trendFunction: 'p95',
  68. projectID: event.projectID,
  69. display: DisplayModes.TREND,
  70. });
  71. const detectionTime = new Date(breakpoint * 1000);
  72. return (
  73. <DataSection>
  74. <Message>
  75. <span>
  76. {tct(
  77. 'Based on the transaction [transactionName], there was a [absoluteChange] ([percentageAmount]) increase in duration (P95) from [previousDuration] to [regressionDuration] around [date] at [time]. Overall operation percentage changes indicate what may have changed in the regression.',
  78. {
  79. transactionName: (
  80. <Link to={normalizeUrl(transactionSummaryLink)}>{transaction}</Link>
  81. ),
  82. absoluteChange: (
  83. <PerformanceDuration
  84. abbreviation
  85. milliseconds={aggregateRange2 - aggregateRange1}
  86. />
  87. ),
  88. percentageAmount: formatPercentage(trendPercentage - 1),
  89. previousDuration: (
  90. <PerformanceDuration abbreviation milliseconds={aggregateRange1} />
  91. ),
  92. regressionDuration: (
  93. <PerformanceDuration abbreviation milliseconds={aggregateRange2} />
  94. ),
  95. date: <DateTime date={detectionTime} dateOnly />,
  96. time: <DateTime date={detectionTime} timeOnly />,
  97. }
  98. )}
  99. </span>
  100. {afterDateTime && now > afterDateTime && (
  101. <Tooltip
  102. title={t(
  103. 'The current date is over 14 days from the breakpoint. Open the Transaction Summary to see the most up to date transaction behaviour.'
  104. )}
  105. >
  106. <LinkButton to={transactionSummaryLink} size="xs" icon={<IconOpen />}>
  107. {t('Go to Summary')}
  108. </LinkButton>
  109. </Tooltip>
  110. )}
  111. </Message>
  112. </DataSection>
  113. );
  114. }
  115. const Message = styled('div')`
  116. display: flex;
  117. flex-direction: row;
  118. gap: ${space(1)};
  119. align-items: baseline;
  120. `;
  121. function EventStatisticalDetectorRegressedFunctionMessage({
  122. event,
  123. }: EventStatisticalDetectorMessageProps) {
  124. const evidenceData = event?.occurrence?.evidenceData;
  125. const absoluteChange = evidenceData?.trendDifference;
  126. const percentageChange = evidenceData?.trendPercentage;
  127. const detectionTime = new Date(evidenceData?.breakpoint * 1000);
  128. const functionName = evidenceData?.function as string;
  129. return (
  130. <DataSection>
  131. <div style={{display: 'inline'}}>
  132. {tct(
  133. '[functionName] had [change] in duration (P95) from [before] to [after] around [date] at [time]. The example profiles may indicate what changed in the regression.',
  134. {
  135. functionName: <code>{functionName}</code>,
  136. change:
  137. defined(absoluteChange) && defined(percentageChange)
  138. ? t(
  139. 'a %s (%s) increase',
  140. <PerformanceDuration abbreviation nanoseconds={absoluteChange} />,
  141. formatPercentage(percentageChange - 1)
  142. )
  143. : t('an increase'),
  144. before: (
  145. <PerformanceDuration
  146. abbreviation
  147. nanoseconds={evidenceData?.aggregateRange1 ?? 0}
  148. />
  149. ),
  150. after: (
  151. <PerformanceDuration
  152. abbreviation
  153. nanoseconds={evidenceData?.aggregateRange2 ?? 0}
  154. />
  155. ),
  156. date: <DateTime date={detectionTime} dateOnly />,
  157. time: <DateTime date={detectionTime} timeOnly />,
  158. }
  159. )}
  160. </div>
  161. </DataSection>
  162. );
  163. }
  164. export default EventStatisticalDetectorMessage;