exclusiveTimeTimeSeries.tsx 6.3 KB

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