startDurationWidget.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import {getInterval} from 'sentry/components/charts/utils';
  2. import {t} from 'sentry/locale';
  3. import {space} from 'sentry/styles/space';
  4. import type {Series, SeriesDataUnit} from 'sentry/types/echarts';
  5. import type {MultiSeriesEventsStats} from 'sentry/types/organization';
  6. import {defined} from 'sentry/utils';
  7. import {tooltipFormatterUsingAggregateOutputType} from 'sentry/utils/discover/charts';
  8. import EventView from 'sentry/utils/discover/eventView';
  9. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  10. import {formatVersion} from 'sentry/utils/formatters';
  11. import {decodeScalar} from 'sentry/utils/queryString';
  12. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  13. import {useLocation} from 'sentry/utils/useLocation';
  14. import usePageFilters from 'sentry/utils/usePageFilters';
  15. import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
  16. import {
  17. PRIMARY_RELEASE_COLOR,
  18. SECONDARY_RELEASE_COLOR,
  19. } from 'sentry/views/starfish/colors';
  20. import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
  21. import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
  22. import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
  23. import {SpanMetricsField} from 'sentry/views/starfish/types';
  24. import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
  25. import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/starfish/utils/constants';
  26. import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
  27. import {useEventsStatsQuery} from 'sentry/views/starfish/utils/useEventsStatsQuery';
  28. const COLD_START_CONDITIONS = ['span.op:app.start.cold', 'span.description:"Cold Start"'];
  29. const WARM_START_CONDITIONS = ['span.op:app.start.warm', 'span.description:"Warm Start"'];
  30. export function transformData(data?: MultiSeriesEventsStats, primaryRelease?: string) {
  31. const transformedSeries: {[releaseName: string]: Series} = {};
  32. if (defined(data)) {
  33. Object.keys(data).forEach(releaseName => {
  34. transformedSeries[releaseName] = {
  35. seriesName: releaseName,
  36. data:
  37. data[releaseName]?.data?.map(datum => {
  38. return {
  39. name: datum[0] * 1000,
  40. value: datum[1][0].count,
  41. } as SeriesDataUnit;
  42. }) ?? [],
  43. ...(primaryRelease === releaseName
  44. ? {color: PRIMARY_RELEASE_COLOR}
  45. : {
  46. color: SECONDARY_RELEASE_COLOR,
  47. lineStyle: {type: 'dashed'},
  48. }),
  49. };
  50. });
  51. }
  52. return transformedSeries;
  53. }
  54. interface Props {
  55. chartHeight: number;
  56. additionalFilters?: string[];
  57. }
  58. function StartDurationWidget({additionalFilters, chartHeight}: Props) {
  59. const pageFilter = usePageFilters();
  60. const location = useLocation();
  61. const {
  62. primaryRelease,
  63. secondaryRelease,
  64. isLoading: isReleasesLoading,
  65. } = useReleaseSelection();
  66. const startType =
  67. decodeScalar(location.query[SpanMetricsField.APP_START_TYPE]) ?? COLD_START_TYPE;
  68. const query = new MutableSearch([
  69. ...(startType === COLD_START_TYPE ? COLD_START_CONDITIONS : WARM_START_CONDITIONS),
  70. ...(additionalFilters ?? []),
  71. ]);
  72. const queryString = appendReleaseFilters(query, primaryRelease, secondaryRelease);
  73. const {
  74. data: series,
  75. isLoading: isSeriesLoading,
  76. error: seriesError,
  77. } = useEventsStatsQuery({
  78. eventView: EventView.fromNewQueryWithPageFilters(
  79. {
  80. name: '',
  81. topEvents: '2',
  82. fields: ['release', 'avg(span.duration)'],
  83. yAxis: ['avg(span.duration)'],
  84. query: queryString,
  85. dataset: DiscoverDatasets.SPANS_METRICS,
  86. version: 2,
  87. interval: getInterval(
  88. pageFilter.selection.datetime,
  89. STARFISH_CHART_INTERVAL_FIDELITY
  90. ),
  91. },
  92. pageFilter.selection
  93. ),
  94. enabled: !isReleasesLoading,
  95. referrer: 'api.starfish.mobile-startup-series',
  96. initialData: {},
  97. });
  98. // The expected response is a multi series response, but if there is no data
  99. // then we get an object representing a single series with all empty values
  100. // (i.e without being grouped by release)
  101. const hasReleaseData = series && !('data' in series);
  102. // Only transform the data is we know there's at least one release
  103. const transformedSeries = hasReleaseData
  104. ? Object.values(transformData(series, primaryRelease)).sort((releaseA, _releaseB) =>
  105. releaseA.seriesName === primaryRelease ? -1 : 1
  106. )
  107. : [];
  108. return (
  109. <MiniChartPanel
  110. title={
  111. startType === COLD_START_TYPE ? t('Average Cold Start') : t('Average Warm Start')
  112. }
  113. subtitle={
  114. primaryRelease
  115. ? t(
  116. '%s v. %s',
  117. formatVersionAndCenterTruncate(primaryRelease, 12),
  118. secondaryRelease ? formatVersionAndCenterTruncate(secondaryRelease, 12) : ''
  119. )
  120. : ''
  121. }
  122. >
  123. <Chart
  124. data={transformedSeries}
  125. height={chartHeight}
  126. loading={isSeriesLoading}
  127. grid={{
  128. left: '0',
  129. right: '0',
  130. top: space(2),
  131. bottom: '0',
  132. }}
  133. showLegend
  134. definedAxisTicks={2}
  135. type={ChartType.LINE}
  136. aggregateOutputFormat="duration"
  137. tooltipFormatterOptions={{
  138. valueFormatter: value =>
  139. tooltipFormatterUsingAggregateOutputType(value, 'duration'),
  140. nameFormatter: value => formatVersion(value),
  141. }}
  142. legendFormatter={value => formatVersion(value)}
  143. error={seriesError}
  144. />
  145. </MiniChartPanel>
  146. );
  147. }
  148. export default StartDurationWidget;