resourceSummaryCharts.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import {t} from 'sentry/locale';
  2. import type {Series} from 'sentry/types/echarts';
  3. import {formatBytesBase2} from 'sentry/utils';
  4. import {formatRate} from 'sentry/utils/formatters';
  5. import getDynamicText from 'sentry/utils/getDynamicText';
  6. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  7. import {RESOURCE_THROUGHPUT_UNIT} from 'sentry/views/performance/browser/resources';
  8. import {useResourceModuleFilters} from 'sentry/views/performance/browser/resources/utils/useResourceFilters';
  9. import {AVG_COLOR, THROUGHPUT_COLOR} from 'sentry/views/starfish/colours';
  10. import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
  11. import ChartPanel from 'sentry/views/starfish/components/chartPanel';
  12. import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useSpanMetricsSeries';
  13. import {SpanMetricsField} from 'sentry/views/starfish/types';
  14. import {
  15. DataTitles,
  16. getDurationChartTitle,
  17. getThroughputChartTitle,
  18. } from 'sentry/views/starfish/views/spans/types';
  19. import {Block, BlockContainer} from 'sentry/views/starfish/views/spanSummaryPage/block';
  20. const {
  21. SPAN_SELF_TIME,
  22. HTTP_RESPONSE_CONTENT_LENGTH,
  23. HTTP_DECODED_RESPONSE_CONTENT_LENGTH,
  24. HTTP_RESPONSE_TRANSFER_SIZE,
  25. RESOURCE_RENDER_BLOCKING_STATUS,
  26. } = SpanMetricsField;
  27. function ResourceSummaryCharts(props: {groupId: string}) {
  28. const filters = useResourceModuleFilters();
  29. // console.log({
  30. // ...(filters[RESOURCE_RENDER_BLOCKING_STATUS]
  31. // ? {[RESOURCE_RENDER_BLOCKING_STATUS]: filters[RESOURCE_RENDER_BLOCKING_STATUS]}
  32. // : {}),
  33. // });
  34. const {data: spanMetricsSeriesData, isLoading: areSpanMetricsSeriesLoading} =
  35. useSpanMetricsSeries({
  36. search: MutableSearch.fromQueryObject({
  37. 'span.group': props.groupId,
  38. ...(filters[RESOURCE_RENDER_BLOCKING_STATUS]
  39. ? {[RESOURCE_RENDER_BLOCKING_STATUS]: filters[RESOURCE_RENDER_BLOCKING_STATUS]}
  40. : {}),
  41. }),
  42. yAxis: [
  43. `spm()`,
  44. `avg(${SPAN_SELF_TIME})`,
  45. `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`,
  46. `avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`,
  47. `avg(${HTTP_RESPONSE_TRANSFER_SIZE})`,
  48. ],
  49. enabled: Boolean(props.groupId),
  50. });
  51. if (spanMetricsSeriesData) {
  52. spanMetricsSeriesData[`avg(${HTTP_RESPONSE_TRANSFER_SIZE})`].lineStyle = {
  53. type: 'dashed',
  54. };
  55. spanMetricsSeriesData[`avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`].lineStyle = {
  56. type: 'dashed',
  57. };
  58. }
  59. return (
  60. <BlockContainer>
  61. <Block>
  62. <ChartPanel title={getThroughputChartTitle('http', RESOURCE_THROUGHPUT_UNIT)}>
  63. <Chart
  64. height={160}
  65. data={[spanMetricsSeriesData?.[`spm()`]]}
  66. loading={areSpanMetricsSeriesLoading}
  67. type={ChartType.LINE}
  68. definedAxisTicks={4}
  69. aggregateOutputFormat="rate"
  70. rateUnit={RESOURCE_THROUGHPUT_UNIT}
  71. stacked
  72. chartColors={[THROUGHPUT_COLOR]}
  73. tooltipFormatterOptions={{
  74. valueFormatter: value => formatRate(value, RESOURCE_THROUGHPUT_UNIT),
  75. nameFormatter: () => t('Requests'),
  76. }}
  77. />
  78. </ChartPanel>
  79. </Block>
  80. <Block>
  81. <ChartPanel title={getDurationChartTitle('http')}>
  82. <Chart
  83. height={160}
  84. data={[spanMetricsSeriesData?.[`avg(${SPAN_SELF_TIME})`]]}
  85. loading={areSpanMetricsSeriesLoading}
  86. chartColors={[AVG_COLOR]}
  87. type={ChartType.LINE}
  88. definedAxisTicks={4}
  89. />
  90. </ChartPanel>
  91. </Block>
  92. <Block>
  93. <ChartPanel title={t('Average Resource Size')}>
  94. <Chart
  95. height={160}
  96. aggregateOutputFormat="size"
  97. data={[
  98. spanMetricsSeriesData?.[`avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`],
  99. spanMetricsSeriesData?.[`avg(${HTTP_RESPONSE_TRANSFER_SIZE})`],
  100. spanMetricsSeriesData?.[`avg(${HTTP_RESPONSE_CONTENT_LENGTH})`],
  101. ]}
  102. loading={areSpanMetricsSeriesLoading}
  103. chartColors={[AVG_COLOR]}
  104. type={ChartType.LINE}
  105. definedAxisTicks={4}
  106. tooltipFormatterOptions={{
  107. valueFormatter: bytes =>
  108. getDynamicText({
  109. value: formatBytesBase2(bytes),
  110. fixed: 'xx KiB',
  111. }),
  112. nameFormatter: name => DataTitles[name],
  113. }}
  114. />
  115. </ChartPanel>
  116. </Block>
  117. </BlockContainer>
  118. );
  119. }
  120. /**
  121. * Ensures a series has no zeros between two non-zero datapoints. This is useful in
  122. * @param series the series to fill
  123. * @returns a reference to the initial series filled
  124. */
  125. export const fillSeries = (series: Series): Series => {
  126. if (!series.data.length) {
  127. return series;
  128. }
  129. let lastSeenValue = series.data[0].value;
  130. return {
  131. ...series,
  132. data: series.data.map(dataPoint => {
  133. const value = dataPoint.value;
  134. if (value !== lastSeenValue && value !== 0) {
  135. lastSeenValue = value;
  136. return {...dataPoint};
  137. }
  138. return {...dataPoint, value: lastSeenValue};
  139. }),
  140. };
  141. };
  142. export default ResourceSummaryCharts;