exclusiveTimeTimeSeries.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {Fragment} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import ChartZoom from 'sentry/components/charts/chartZoom';
  4. import ErrorPanel from 'sentry/components/charts/errorPanel';
  5. import EventsRequest from 'sentry/components/charts/eventsRequest';
  6. import {LineChart} from 'sentry/components/charts/lineChart';
  7. import {HeaderTitleLegend} from 'sentry/components/charts/styles';
  8. import TransitionChart from 'sentry/components/charts/transitionChart';
  9. import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
  10. import {getInterval, getSeriesSelection} from 'sentry/components/charts/utils';
  11. import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
  12. import Placeholder from 'sentry/components/placeholder';
  13. import QuestionTooltip from 'sentry/components/questionTooltip';
  14. import {IconWarning} from 'sentry/icons';
  15. import {t} from 'sentry/locale';
  16. import type {Organization} from 'sentry/types/organization';
  17. import {browserHistory} from 'sentry/utils/browserHistory';
  18. import {getUtcToLocalDateObject} from 'sentry/utils/dates';
  19. import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
  20. import type EventView from 'sentry/utils/discover/eventView';
  21. import getDynamicText from 'sentry/utils/getDynamicText';
  22. import type {SpanSlug} from 'sentry/utils/performance/suspectSpans/types';
  23. import useApi from 'sentry/utils/useApi';
  24. import {useLocation} from 'sentry/utils/useLocation';
  25. import useRouter from 'sentry/utils/useRouter';
  26. import {getExclusiveTimeDisplayedValue} from '../utils';
  27. type Props = {
  28. eventView: EventView;
  29. organization: Organization;
  30. spanSlug: SpanSlug;
  31. withoutZerofill: boolean;
  32. };
  33. export default function ExclusiveTimeTimeSeries(props: Props) {
  34. const location = useLocation();
  35. const router = useRouter();
  36. const {organization, eventView, spanSlug, withoutZerofill} = props;
  37. const api = useApi();
  38. const theme = useTheme();
  39. const period = eventView.statsPeriod;
  40. const start = eventView.start ? getUtcToLocalDateObject(eventView.start) : null;
  41. const end = eventView.end ? getUtcToLocalDateObject(eventView.end) : null;
  42. const {utc} = normalizeDateTimeParams(location.query);
  43. const datetimeSelection = {
  44. start,
  45. end,
  46. period,
  47. };
  48. const yAxis = [
  49. 'percentileArray(spans_exclusive_time, 0.50)',
  50. 'percentileArray(spans_exclusive_time, 0.75)',
  51. 'percentileArray(spans_exclusive_time, 0.95)',
  52. 'percentileArray(spans_exclusive_time, 0.99)',
  53. ];
  54. const handleLegendSelectChanged = legendChange => {
  55. const {selected} = legendChange;
  56. const unselected = Object.keys(selected).filter(key => !selected[key]);
  57. const to = {
  58. ...location,
  59. query: {
  60. ...location.query,
  61. unselectedSeries: unselected,
  62. },
  63. };
  64. browserHistory.push(to);
  65. };
  66. return (
  67. <Fragment>
  68. <HeaderTitleLegend>
  69. {t('Self Time Breakdown')}
  70. <QuestionTooltip
  71. size="sm"
  72. position="top"
  73. title={t(
  74. 'Self Time Breakdown reflects the span self time by percentile over time.'
  75. )}
  76. />
  77. </HeaderTitleLegend>
  78. <ChartZoom
  79. router={router}
  80. period={period}
  81. start={start}
  82. end={end}
  83. utc={utc === 'true'}
  84. >
  85. {zoomRenderProps => (
  86. <EventsRequest
  87. api={api}
  88. organization={organization}
  89. project={eventView.project}
  90. environment={eventView.environment}
  91. start={start}
  92. end={end}
  93. period={period}
  94. interval={getInterval(datetimeSelection, 'high')}
  95. showLoading={false}
  96. query={eventView.query}
  97. includePrevious={false}
  98. yAxis={yAxis}
  99. partial
  100. withoutZerofill={withoutZerofill}
  101. queryExtras={{span: `${spanSlug.op}:${spanSlug.group}`}}
  102. generatePathname={org => `/organizations/${org.slug}/events-spans-stats/`}
  103. >
  104. {({results, errored, loading, reloading, timeframe}) => {
  105. if (errored) {
  106. return (
  107. <ErrorPanel>
  108. <IconWarning color="gray300" size="lg" />
  109. </ErrorPanel>
  110. );
  111. }
  112. const chartOptions = {
  113. grid: {
  114. left: '10px',
  115. right: '10px',
  116. top: '40px',
  117. bottom: '0px',
  118. },
  119. colors: theme.charts.getColorPalette(yAxis.length - 2),
  120. seriesOptions: {
  121. showSymbol: false,
  122. },
  123. tooltip: {
  124. trigger: 'axis' as const,
  125. // p50() coerces the axis to be time based
  126. valueFormatter: (value, _seriesName) =>
  127. tooltipFormatter(value, 'duration'),
  128. },
  129. xAxis: timeframe
  130. ? {
  131. min: timeframe.start,
  132. max: timeframe.end,
  133. }
  134. : undefined,
  135. yAxis: {
  136. axisLabel: {
  137. color: theme.chartLabel,
  138. formatter: (value: number) => axisLabelFormatter(value, 'duration'),
  139. },
  140. },
  141. };
  142. const legend = {
  143. right: 10,
  144. top: 5,
  145. selected: getSeriesSelection(location),
  146. };
  147. const formattedResults = results?.map(result => ({
  148. ...result,
  149. seriesName: getExclusiveTimeDisplayedValue(result.seriesName),
  150. }));
  151. return (
  152. <TransitionChart loading={loading} reloading={reloading}>
  153. <TransparentLoadingMask visible={reloading} />
  154. {getDynamicText({
  155. value: (
  156. <LineChart
  157. {...zoomRenderProps}
  158. {...chartOptions}
  159. legend={legend}
  160. onLegendSelectChanged={handleLegendSelectChanged}
  161. series={formattedResults ?? []}
  162. />
  163. ),
  164. fixed: <Placeholder height="200px" />,
  165. })}
  166. </TransitionChart>
  167. );
  168. }}
  169. </EventsRequest>
  170. )}
  171. </ChartZoom>
  172. </Fragment>
  173. );
  174. }