prometheus

Форк
0
236 строк · 6.7 Кб
1
import React, { FC, ReactNode } from 'react';
2

3
import { Alert, Button, ButtonGroup, Table } from 'reactstrap';
4

5
import SeriesName from './SeriesName';
6
import { Metric, Histogram } from '../../types/types';
7

8
import moment from 'moment';
9

10
import HistogramChart from './HistogramChart';
11

12
export interface DataTableProps {
13
  data:
14
    | null
15
    | {
16
        resultType: 'vector';
17
        result: InstantSample[];
18
      }
19
    | {
20
        resultType: 'matrix';
21
        result: RangeSamples[];
22
      }
23
    | {
24
        resultType: 'scalar';
25
        result: SampleValue;
26
      }
27
    | {
28
        resultType: 'string';
29
        result: SampleValue;
30
      };
31
  useLocalTime: boolean;
32
}
33

34
interface InstantSample {
35
  metric: Metric;
36
  value?: SampleValue;
37
  histogram?: SampleHistogram;
38
}
39

40
interface RangeSamples {
41
  metric: Metric;
42
  values?: SampleValue[];
43
  histograms?: SampleHistogram[];
44
}
45

46
type SampleValue = [number, string];
47
type SampleHistogram = [number, Histogram];
48

49
const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] => {
50
  const maxSeries = 10000;
51

52
  if (series.length > maxSeries) {
53
    return series.slice(0, maxSeries);
54
  }
55
  return series;
56
};
57

58
const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
59
  const [scale, setScale] = React.useState<'linear' | 'exponential'>('exponential');
60

61
  if (data === null) {
62
    return <Alert color="light">No data queried yet</Alert>;
63
  }
64

65
  if (data.result === null || data.result.length === 0) {
66
    return <Alert color="secondary">Empty query result</Alert>;
67
  }
68

69
  const maxFormattableSize = 1000;
70
  let rows: ReactNode[] = [];
71
  let limited = false;
72
  const doFormat = data.result.length <= maxFormattableSize;
73
  switch (data.resultType) {
74
    case 'vector':
75
      rows = (limitSeries(data.result) as InstantSample[]).map((s: InstantSample, index: number): ReactNode => {
76
        return (
77
          <tr key={index}>
78
            <td>
79
              <SeriesName labels={s.metric} format={doFormat} />
80
            </td>
81
            <td>
82
              {s.value && s.value[1]}
83
              {s.histogram && (
84
                <>
85
                  <HistogramChart histogram={s.histogram[1]} index={index} scale={scale} />
86
                  <div className="histogram-summary-wrapper">
87
                    <div className="histogram-summary">
88
                      <span>
89
                        <strong>Total count:</strong> {s.histogram[1].count}
90
                      </span>
91
                      <span>
92
                        <strong>Sum:</strong> {s.histogram[1].sum}
93
                      </span>
94
                    </div>
95
                    <div className="histogram-summary">
96
                      <span>x-axis scale:</span>
97
                      <ButtonGroup className="stacked-input" size="sm">
98
                        <Button
99
                          title="Show histogram on exponential scale"
100
                          onClick={() => setScale('exponential')}
101
                          active={scale === 'exponential'}
102
                        >
103
                          Exponential
104
                        </Button>
105
                        <Button
106
                          title="Show histogram on linear scale"
107
                          onClick={() => setScale('linear')}
108
                          active={scale === 'linear'}
109
                        >
110
                          Linear
111
                        </Button>
112
                      </ButtonGroup>
113
                    </div>
114
                  </div>
115
                  {histogramTable(s.histogram[1])}
116
                </>
117
              )}
118
            </td>
119
          </tr>
120
        );
121
      });
122
      limited = rows.length !== data.result.length;
123
      break;
124
    case 'matrix':
125
      rows = (limitSeries(data.result) as RangeSamples[]).map((s, seriesIdx) => {
126
        const valuesAndTimes = s.values
127
          ? s.values.map((v, valIdx) => {
128
              const printedDatetime = moment.unix(v[0]).toISOString(useLocalTime);
129
              return (
130
                <React.Fragment key={valIdx}>
131
                  {v[1]} @{<span title={printedDatetime}>{v[0]}</span>}
132
                  <br />
133
                </React.Fragment>
134
              );
135
            })
136
          : [];
137
        const histogramsAndTimes = s.histograms
138
          ? s.histograms.map((h, hisIdx) => {
139
              const printedDatetime = moment.unix(h[0]).toISOString(useLocalTime);
140
              return (
141
                <React.Fragment key={-hisIdx}>
142
                  {histogramTable(h[1])} @{<span title={printedDatetime}>{h[0]}</span>}
143
                  <br />
144
                </React.Fragment>
145
              );
146
            })
147
          : [];
148
        return (
149
          <tr style={{ whiteSpace: 'pre' }} key={seriesIdx}>
150
            <td>
151
              <SeriesName labels={s.metric} format={doFormat} />
152
            </td>
153
            <td>
154
              {valuesAndTimes} {histogramsAndTimes}
155
            </td>
156
          </tr>
157
        );
158
      });
159
      limited = rows.length !== data.result.length;
160
      break;
161
    case 'scalar':
162
      rows.push(
163
        <tr key="0">
164
          <td>scalar</td>
165
          <td>{data.result[1]}</td>
166
        </tr>
167
      );
168
      break;
169
    case 'string':
170
      rows.push(
171
        <tr key="0">
172
          <td>string</td>
173
          <td>{data.result[1]}</td>
174
        </tr>
175
      );
176
      break;
177
    default:
178
      return <Alert color="danger">Unsupported result value type</Alert>;
179
  }
180

181
  return (
182
    <>
183
      {limited && (
184
        <Alert color="danger">
185
          <strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}.
186
        </Alert>
187
      )}
188
      {!doFormat && (
189
        <Alert color="secondary">
190
          <strong>Notice:</strong> Showing more than {maxFormattableSize} series, turning off label formatting for
191
          performance reasons.
192
        </Alert>
193
      )}
194
      <Table hover size="sm" className="data-table">
195
        <tbody>{rows}</tbody>
196
      </Table>
197
    </>
198
  );
199
};
200

201
const leftDelim = (br: number): string => (br === 3 || br === 1 ? '[' : '(');
202
const rightDelim = (br: number): string => (br === 3 || br === 0 ? ']' : ')');
203

204
export const bucketRangeString = ([boundaryRule, leftBoundary, rightBoundary, _]: [
205
  number,
206
  string,
207
  string,
208
  string
209
]): string => {
210
  return `${leftDelim(boundaryRule)}${leftBoundary} -> ${rightBoundary}${rightDelim(boundaryRule)}`;
211
};
212

213
export const histogramTable = (h: Histogram): ReactNode => (
214
  <Table size="xs" responsive bordered>
215
    <thead>
216
      <tr>
217
        <th style={{ textAlign: 'center' }} colSpan={2}>
218
          Histogram Sample
219
        </th>
220
      </tr>
221
    </thead>
222
    <tbody>
223
      <tr>
224
        <th>Range</th>
225
        <th>Count</th>
226
      </tr>
227
      {h.buckets?.map((b, i) => (
228
        <tr key={i}>
229
          <td>{bucketRangeString(b)}</td>
230
          <td>{b[3]}</td>
231
        </tr>
232
      ))}
233
    </tbody>
234
  </Table>
235
);
236
export default DataTable;
237

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.