tracesChart.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {getInterval} from 'sentry/components/charts/utils';
  4. import {CHART_PALETTE} from 'sentry/constants/chartPalette';
  5. import {t} from 'sentry/locale';
  6. import type {Series} from 'sentry/types/echarts';
  7. import {RateUnit} from 'sentry/utils/discover/fields';
  8. import {formatRate} from 'sentry/utils/formatters';
  9. import {decodeList} from 'sentry/utils/queryString';
  10. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. import usePageFilters from 'sentry/utils/usePageFilters';
  13. import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
  14. import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
  15. import {useSpanIndexedSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
  16. import {CHART_HEIGHT} from 'sentry/views/insights/database/settings';
  17. import {areQueriesEmpty} from './utils';
  18. interface Props {}
  19. export function TracesChart({}: Props) {
  20. const location = useLocation();
  21. const queries = useMemo(() => {
  22. return decodeList(location.query.query)?.map(query => query.trim());
  23. }, [location.query.query]);
  24. const enabled = useMemo(
  25. () => [
  26. true, // always visualize the first series
  27. Boolean(queries?.[1]),
  28. Boolean(queries?.[2]),
  29. ],
  30. [queries]
  31. );
  32. const firstCountSeries = useTraceCountSeries({
  33. query: queries?.[0] || '',
  34. enabled: enabled[0],
  35. });
  36. const secondCountSeries = useTraceCountSeries({
  37. query: queries?.[1],
  38. enabled: enabled[1],
  39. });
  40. const thirdCountSeries = useTraceCountSeries({
  41. query: queries?.[2],
  42. enabled: enabled[2],
  43. });
  44. const seriesAreLoading =
  45. // Disabled queries have `isLoading: true`, but this changes in v5.
  46. // To handle this gracefully, we check if the query is enabled + isLoading.
  47. //
  48. // References
  49. // - https://tanstack.com/query/v4/docs/framework/react/guides/disabling-queries
  50. // - https://tanstack.com/query/latest/docs/framework/react/guides/disabling-queries#isloading-previously-isinitialloading
  51. (enabled[0] && firstCountSeries.isLoading) ||
  52. (enabled[1] && secondCountSeries.isLoading) ||
  53. (enabled[2] && thirdCountSeries.isLoading);
  54. const chartData = useMemo<Series[]>(() => {
  55. const series = [firstCountSeries.data, secondCountSeries.data, thirdCountSeries.data];
  56. const allData: Series[] = [];
  57. for (let i = 0; i < series.length; i++) {
  58. if (!enabled[i]) {
  59. continue;
  60. }
  61. const data = series[i]['count()'];
  62. data.color = CHART_PALETTE[2][i];
  63. data.seriesName =
  64. `span ${i + 1}: ${queries[i] || t('All spans')}` || t('All spans');
  65. allData.push(data);
  66. }
  67. return allData;
  68. }, [
  69. enabled,
  70. queries,
  71. firstCountSeries.data,
  72. secondCountSeries.data,
  73. thirdCountSeries.data,
  74. ]);
  75. return (
  76. <ChartContainer>
  77. <ChartPanel
  78. title={areQueriesEmpty(queries) ? t('All Spans') : t('All Matching Spans')}
  79. >
  80. <Chart
  81. height={CHART_HEIGHT}
  82. grid={{
  83. left: '0',
  84. right: '0',
  85. top: '8px',
  86. bottom: '0',
  87. }}
  88. data={chartData}
  89. loading={seriesAreLoading}
  90. chartColors={CHART_PALETTE[2]}
  91. type={ChartType.LINE}
  92. aggregateOutputFormat="number"
  93. showLegend
  94. tooltipFormatterOptions={{
  95. valueFormatter: value => formatRate(value, RateUnit.PER_MINUTE),
  96. }}
  97. />
  98. </ChartPanel>
  99. </ChartContainer>
  100. );
  101. }
  102. const ChartContainer = styled('div')`
  103. display: grid;
  104. gap: 0;
  105. grid-template-columns: 1fr;
  106. `;
  107. const useTraceCountSeries = ({
  108. enabled,
  109. query,
  110. }: {
  111. enabled: boolean;
  112. query: string | null;
  113. }) => {
  114. const pageFilters = usePageFilters();
  115. return useSpanIndexedSeries(
  116. {
  117. search: new MutableSearch(query ?? ''),
  118. yAxis: ['count()'],
  119. interval: getInterval(pageFilters.selection.datetime, 'metrics'),
  120. overriddenRoute: 'traces-stats',
  121. enabled,
  122. },
  123. 'api.trace-explorer.stats'
  124. );
  125. };