eventChart.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {Component} from 'react';
  2. import type {Client} from 'sentry/api';
  3. import MiniBarChart from 'sentry/components/charts/miniBarChart';
  4. import LoadingError from 'sentry/components/loadingError';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {t} from 'sentry/locale';
  7. import type {TimeseriesValue} from 'sentry/types/core';
  8. import type {SeriesDataUnit} from 'sentry/types/echarts';
  9. import theme from 'sentry/utils/theme';
  10. import withApi from 'sentry/utils/withApi';
  11. type Props = {
  12. api: Client;
  13. resolution: string;
  14. since: number;
  15. };
  16. type State = {
  17. error: boolean;
  18. loading: boolean;
  19. rawData: Record<string, TimeseriesValue[]>;
  20. stats: Record<string, SeriesDataUnit[]>;
  21. };
  22. const initialState: State = {
  23. error: false,
  24. loading: true,
  25. rawData: {
  26. 'events.total': [],
  27. 'events.dropped': [],
  28. },
  29. stats: {received: [], rejected: []},
  30. };
  31. class EventChart extends Component<Props, State> {
  32. state: State = initialState;
  33. UNSAFE_componentWillMount() {
  34. this.fetchData();
  35. }
  36. UNSAFE_componentWillReceiveProps(nextProps: Props) {
  37. if (this.props.since !== nextProps.since) {
  38. this.setState(initialState, this.fetchData);
  39. }
  40. }
  41. fetchData = () => {
  42. const statNameList = ['events.total', 'events.dropped'];
  43. statNameList.forEach(statName => {
  44. // query the organization stats via a separate call as its possible the project stats
  45. // are too heavy
  46. this.props.api.request('/internal/stats/', {
  47. method: 'GET',
  48. data: {
  49. since: this.props.since,
  50. resolution: this.props.resolution,
  51. key: statName,
  52. },
  53. success: data => {
  54. this.setState(prevState => {
  55. const rawData = prevState.rawData;
  56. rawData[statName] = data;
  57. return {
  58. rawData,
  59. };
  60. }, this.requestFinished);
  61. },
  62. error: () => {
  63. this.setState({
  64. error: true,
  65. });
  66. },
  67. });
  68. });
  69. };
  70. requestFinished() {
  71. const {rawData} = this.state;
  72. if (rawData['events.total'] && rawData['events.dropped']) {
  73. this.processOrgData();
  74. }
  75. }
  76. processOrgData() {
  77. const {rawData} = this.state;
  78. const sReceived: Record<string, number> = {};
  79. const sRejected: Record<string, number> = {};
  80. const aReceived = [0, 0]; // received, points
  81. rawData['events.total'].forEach((point, idx) => {
  82. const dReceived = point[1];
  83. const dRejected = rawData['events.dropped'][idx]?.[1];
  84. const ts = point[0];
  85. if (sReceived[ts] === undefined) {
  86. sReceived[ts] = dReceived;
  87. sRejected[ts] = dRejected;
  88. } else {
  89. sReceived[ts] += dReceived;
  90. sRejected[ts] += dRejected;
  91. }
  92. if (dReceived > 0) {
  93. aReceived[0] += dReceived;
  94. aReceived[1] += 1;
  95. }
  96. });
  97. this.setState({
  98. stats: {
  99. rejected: Object.keys(sRejected).map(ts => ({
  100. name: parseInt(ts, 10) * 1000,
  101. value: sRejected[ts] || 0,
  102. })),
  103. accepted: Object.keys(sReceived).map(ts =>
  104. // total number of events accepted (received - rejected)
  105. ({name: parseInt(ts, 10) * 1000, value: sReceived[ts] - sRejected[ts]})
  106. ),
  107. },
  108. loading: false,
  109. });
  110. }
  111. getChartSeries() {
  112. const {stats} = this.state;
  113. return [
  114. {
  115. seriesName: t('Accepted'),
  116. data: stats.accepted,
  117. color: theme.blue300,
  118. },
  119. {
  120. seriesName: t('Dropped'),
  121. data: stats.rejected,
  122. color: theme.red200,
  123. },
  124. ];
  125. }
  126. render() {
  127. const {loading, error} = this.state;
  128. if (loading) {
  129. return <LoadingIndicator />;
  130. }
  131. if (error) {
  132. return <LoadingError onRetry={this.fetchData} />;
  133. }
  134. const series = this.getChartSeries();
  135. const colors = series.map(({color}) => color);
  136. return (
  137. <MiniBarChart
  138. series={series}
  139. colors={colors}
  140. height={110}
  141. stacked
  142. isGroupedByDate
  143. showTimeInTooltip
  144. labelYAxisExtents
  145. />
  146. );
  147. }
  148. }
  149. export default withApi(EventChart);