mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
213 lines
5.6 KiB
TypeScript
213 lines
5.6 KiB
TypeScript
import React, { FC, ReactNode } from 'react';
|
|
|
|
import { Alert, Table, UncontrolledTooltip } from 'reactstrap';
|
|
|
|
import SeriesName from './SeriesName';
|
|
import { Metric, Histogram } from '../../types/types';
|
|
|
|
import moment from 'moment';
|
|
|
|
import { Tooltip as ReTooltip, Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts';
|
|
import { parseValue } from './GraphHelpers';
|
|
import HistogramChart from './HistogramChart';
|
|
|
|
export interface DataTableProps {
|
|
data:
|
|
| null
|
|
| {
|
|
resultType: 'vector';
|
|
result: InstantSample[];
|
|
}
|
|
| {
|
|
resultType: 'matrix';
|
|
result: RangeSamples[];
|
|
}
|
|
| {
|
|
resultType: 'scalar';
|
|
result: SampleValue;
|
|
}
|
|
| {
|
|
resultType: 'string';
|
|
result: string;
|
|
};
|
|
useLocalTime: boolean;
|
|
}
|
|
|
|
interface InstantSample {
|
|
metric: Metric;
|
|
value?: SampleValue;
|
|
histogram?: SampleHistogram;
|
|
}
|
|
|
|
interface RangeSamples {
|
|
metric: Metric;
|
|
values?: SampleValue[];
|
|
histograms?: SampleHistogram[];
|
|
}
|
|
|
|
type SampleValue = [number, string];
|
|
type SampleHistogram = [number, Histogram];
|
|
|
|
const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] => {
|
|
const maxSeries = 10000;
|
|
|
|
if (series.length > maxSeries) {
|
|
return series.slice(0, maxSeries);
|
|
}
|
|
return series;
|
|
};
|
|
|
|
const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|
if (data === null) {
|
|
return <Alert color="light">No data queried yet</Alert>;
|
|
}
|
|
|
|
if (data.result === null || data.result.length === 0) {
|
|
return <Alert color="secondary">Empty query result</Alert>;
|
|
}
|
|
|
|
const maxFormattableSize = 1000;
|
|
let rows: ReactNode[] = [];
|
|
let limited = false;
|
|
const doFormat = data.result.length <= maxFormattableSize;
|
|
switch (data.resultType) {
|
|
case 'vector':
|
|
rows = (limitSeries(data.result) as InstantSample[]).map((s: InstantSample, index: number): ReactNode => {
|
|
return (
|
|
<tr key={index}>
|
|
<td>
|
|
<SeriesName labels={s.metric} format={doFormat} />
|
|
</td>
|
|
<td>
|
|
{s.value && s.value[1]}
|
|
{s.histogram && (
|
|
<>
|
|
<HistogramChart histogram={s.histogram[1]} index={index} />
|
|
{histogramTable(s.histogram[1])}
|
|
</>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
);
|
|
});
|
|
limited = rows.length !== data.result.length;
|
|
break;
|
|
case 'matrix':
|
|
rows = (limitSeries(data.result) as RangeSamples[]).map((s, seriesIdx) => {
|
|
const valuesAndTimes = s.values
|
|
? s.values.map((v, valIdx) => {
|
|
const printedDatetime = moment.unix(v[0]).toISOString(useLocalTime);
|
|
return (
|
|
<React.Fragment key={valIdx}>
|
|
{v[1]} @{<span title={printedDatetime}>{v[0]}</span>}
|
|
<br />
|
|
</React.Fragment>
|
|
);
|
|
})
|
|
: [];
|
|
const histogramsAndTimes = s.histograms
|
|
? s.histograms.map((h, hisIdx) => {
|
|
const printedDatetime = moment.unix(h[0]).toISOString(useLocalTime);
|
|
return (
|
|
<React.Fragment key={-hisIdx}>
|
|
{histogramTable(h[1])} @{<span title={printedDatetime}>{h[0]}</span>}
|
|
<br />
|
|
</React.Fragment>
|
|
);
|
|
})
|
|
: [];
|
|
return (
|
|
<tr style={{ whiteSpace: 'pre' }} key={seriesIdx}>
|
|
<td>
|
|
<SeriesName labels={s.metric} format={doFormat} />
|
|
</td>
|
|
<td>
|
|
{valuesAndTimes} {histogramsAndTimes}
|
|
</td>
|
|
</tr>
|
|
);
|
|
});
|
|
limited = rows.length !== data.result.length;
|
|
break;
|
|
case 'scalar':
|
|
rows.push(
|
|
<tr key="0">
|
|
<td>scalar</td>
|
|
<td>{data.result[1]}</td>
|
|
</tr>
|
|
);
|
|
break;
|
|
case 'string':
|
|
rows.push(
|
|
<tr key="0">
|
|
<td>string</td>
|
|
<td>{data.result[1]}</td>
|
|
</tr>
|
|
);
|
|
break;
|
|
default:
|
|
return <Alert color="danger">Unsupported result value type</Alert>;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{limited && (
|
|
<Alert color="danger">
|
|
<strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}.
|
|
</Alert>
|
|
)}
|
|
{!doFormat && (
|
|
<Alert color="secondary">
|
|
<strong>Notice:</strong> Showing more than {maxFormattableSize} series, turning off label formatting for
|
|
performance reasons.
|
|
</Alert>
|
|
)}
|
|
<Table hover size="sm" className="data-table">
|
|
<tbody>{rows}</tbody>
|
|
</Table>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const leftDelim = (br: number): string => (br === 3 || br === 1 ? '[' : '(');
|
|
const rightDelim = (br: number): string => (br === 3 || br === 0 ? ']' : ')');
|
|
|
|
export const bucketRangeString = ([boundaryRule, leftBoundary, rightBoundary, _]: [
|
|
number,
|
|
string,
|
|
string,
|
|
string
|
|
]): string => {
|
|
return `${leftDelim(boundaryRule)}${leftBoundary} 🠒 ${rightBoundary}${rightDelim(boundaryRule)}`;
|
|
};
|
|
|
|
export const histogramTable = (h: Histogram): ReactNode => (
|
|
<Table size="xs" responsive bordered>
|
|
<thead>
|
|
<tr>
|
|
<th style={{ textAlign: 'center' }} colSpan={2}>
|
|
Histogram Sample
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<th>count:</th>
|
|
<td>{h.count}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>sum:</th>
|
|
<td>{h.sum}</td>
|
|
</tr>
|
|
{h.buckets?.map((b, i) => (
|
|
<tr key={i}>
|
|
<th>{bucketRangeString(b)}</th>
|
|
<td>{b[3]}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</Table>
|
|
);
|
|
|
|
export default DataTable;
|