responseRateChart.tsx 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import type {Series} from 'sentry/types/echarts';
  2. import {formatPercentage} from 'sentry/utils/number/formatPercentage';
  3. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  4. import {
  5. HTTP_RESPONSE_3XX_COLOR,
  6. HTTP_RESPONSE_4XX_COLOR,
  7. HTTP_RESPONSE_5XX_COLOR,
  8. } from 'sentry/views/insights/colors';
  9. import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
  10. import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
  11. import {DataTitles} from 'sentry/views/insights/common/views/spans/types';
  12. import {ALERTS} from 'sentry/views/insights/http/alerts';
  13. import {CHART_HEIGHT} from 'sentry/views/insights/http/settings';
  14. import type {SpanMetricsQueryFilters} from 'sentry/views/insights/types';
  15. interface Props {
  16. isLoading: boolean;
  17. series: [Series, Series, Series];
  18. error?: Error | null;
  19. filters?: SpanMetricsQueryFilters;
  20. }
  21. export function ResponseRateChart({series, isLoading, error, filters}: Props) {
  22. const filterString = filters && MutableSearch.fromQueryObject(filters).formatString();
  23. const alertConfig = [
  24. {...ALERTS.threeHundreds, query: filterString ?? ALERTS.threeHundreds.query},
  25. {...ALERTS.fourHundreds, query: filterString ?? ALERTS.fourHundreds.query},
  26. {...ALERTS.fiveHundreds, query: filterString ?? ALERTS.fiveHundreds.query},
  27. ];
  28. return (
  29. <ChartPanel title={DataTitles.unsuccessfulHTTPCodes} alertConfigs={alertConfig}>
  30. <Chart
  31. showLegend
  32. height={CHART_HEIGHT}
  33. grid={{
  34. left: '4px',
  35. right: '0',
  36. top: '8px',
  37. bottom: '0',
  38. }}
  39. data={series}
  40. loading={isLoading}
  41. error={error}
  42. chartColors={[
  43. HTTP_RESPONSE_3XX_COLOR,
  44. HTTP_RESPONSE_4XX_COLOR,
  45. HTTP_RESPONSE_5XX_COLOR,
  46. ]}
  47. type={ChartType.LINE}
  48. aggregateOutputFormat="percentage"
  49. dataMax={getAxisMaxForPercentageSeries(series)}
  50. tooltipFormatterOptions={{
  51. valueFormatter: value => formatPercentage(value),
  52. }}
  53. />
  54. </ChartPanel>
  55. );
  56. }
  57. /**
  58. * Given a set of `Series` objects that contain percentage data (i.e., every item in `data` has a `value` between 0 and 1) return an appropriate max value.
  59. *
  60. * e.g., for series with very low values (like 5xx rates), it rounds to the nearest significant digit. For other cases, it limits it to 100
  61. */
  62. export function getAxisMaxForPercentageSeries(series: Series[]): number {
  63. const maxValue = Math.max(
  64. ...series.map(serie => Math.max(...serie.data.map(datum => datum.value)))
  65. );
  66. const maxNumberOfDecimalPlaces = Math.ceil(Math.min(0, Math.log10(maxValue)));
  67. return Math.pow(10, maxNumberOfDecimalPlaces);
  68. }