mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
Histogram support in table view
Signed-off-by: beorn7 <beorn@grafana.com>
This commit is contained in:
parent
bcc919cb19
commit
77a362b771
|
@ -1,6 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import DataTable, { DataTableProps } from './DataTable';
|
||||
import HistogramString, { HistogramStringProps } from './DataTable';
|
||||
import { Alert, Table } from 'reactstrap';
|
||||
import SeriesName from './SeriesName';
|
||||
|
||||
|
@ -71,7 +72,81 @@ describe('DataTable', () => {
|
|||
const table = dataTable.find(Table);
|
||||
table.find('tr').forEach((row, idx) => {
|
||||
expect(row.find(SeriesName)).toHaveLength(1);
|
||||
expect(row.find('td').at(1).text()).toEqual(`${idx}`);
|
||||
expect(row.find('td').at(1).text()).toEqual(`${idx} <HistogramString />`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when resultType is a vector with histograms', () => {
|
||||
const dataTableProps: DataTableProps = {
|
||||
data: {
|
||||
resultType: 'vector',
|
||||
result: [
|
||||
{
|
||||
metric: {
|
||||
__name__: 'metric_name_1',
|
||||
label1: 'value_1',
|
||||
labeln: 'value_n',
|
||||
},
|
||||
histogram: [
|
||||
1572098246.599,
|
||||
{
|
||||
"count": "10",
|
||||
"sum": "3.3",
|
||||
"buckets": [
|
||||
[ 1, "-1", "-0.5", "2"],
|
||||
[ 3, "-0.5", "0.5", "3"],
|
||||
[ 0, "0.5", "1", "5"],
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
metric: {
|
||||
__name__: 'metric_name_2',
|
||||
label1: 'value_1',
|
||||
labeln: 'value_n',
|
||||
},
|
||||
histogram: [
|
||||
1572098247.599,
|
||||
{
|
||||
"count": "5",
|
||||
"sum": "1.11",
|
||||
"buckets": [
|
||||
[ 0, "0.5", "1", "2"],
|
||||
[ 0, "1", "2", "3"],
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
metric: {
|
||||
__name__: 'metric_name_2',
|
||||
label1: 'value_1',
|
||||
labeln: 'value_n',
|
||||
},
|
||||
|
||||
},
|
||||
],
|
||||
},
|
||||
useLocalTime: false,
|
||||
};
|
||||
const dataTable = shallow(<DataTable {...dataTableProps} />);
|
||||
|
||||
it('renders a table', () => {
|
||||
const table = dataTable.find(Table);
|
||||
expect(table.prop('hover')).toBe(true);
|
||||
expect(table.prop('size')).toEqual('sm');
|
||||
expect(table.prop('className')).toEqual('data-table');
|
||||
expect(table.find('tbody')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders rows', () => {
|
||||
const table = dataTable.find(Table);
|
||||
table.find('tr').forEach((row, idx) => {
|
||||
expect(row.find(SeriesName)).toHaveLength(1);
|
||||
// TODO(beorn7): This doesn't actually test the rendoring yet. Need to trigger it somehow.
|
||||
expect(row.find('td').at(1).text()).toEqual(` <HistogramString />`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -239,7 +314,7 @@ describe('DataTable', () => {
|
|||
expect(table.find('tr')).toHaveLength(3);
|
||||
const row = rows.at(0);
|
||||
expect(row.text()).toEqual(
|
||||
`<SeriesName />9 @1572097950.9310 @1572097965.93111 @1572097980.92912 @1572097995.93113 @1572098010.93214 @1572098025.93315 @1572098040.9316 @1572098055.9317 @1572098070.9318 @1572098085.93619 @1572098100.93620 @1572098115.93321 @1572098130.93222 @1572098145.93223 @1572098160.93324 @1572098175.93425 @1572098190.93726 @1572098205.93427 @1572098220.93328 @1572098235.934`
|
||||
`<SeriesName />9 @1572097950.9310 @1572097965.93111 @1572097980.92912 @1572097995.93113 @1572098010.93214 @1572098025.93315 @1572098040.9316 @1572098055.9317 @1572098070.9318 @1572098085.93619 @1572098100.93620 @1572098115.93321 @1572098130.93222 @1572098145.93223 @1572098160.93324 @1572098175.93425 @1572098190.93726 @1572098205.93427 @1572098220.93328 @1572098235.934 `
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { FC, ReactNode } from 'react';
|
|||
import { Alert, Table } from 'reactstrap';
|
||||
|
||||
import SeriesName from './SeriesName';
|
||||
import { Metric } from '../../types/types';
|
||||
import { Metric, Histogram } from '../../types/types';
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
|
@ -31,15 +31,18 @@ export interface DataTableProps {
|
|||
|
||||
interface InstantSample {
|
||||
metric: Metric;
|
||||
value: SampleValue;
|
||||
value?: SampleValue;
|
||||
histogram?: SampleHistogram;
|
||||
}
|
||||
|
||||
interface RangeSamples {
|
||||
metric: Metric;
|
||||
values: SampleValue[];
|
||||
values?: SampleValue[];
|
||||
histograms?: SampleHistogram[];
|
||||
}
|
||||
|
||||
type SampleValue = [number, string];
|
||||
type SampleHistogram = [number, Histogram];
|
||||
|
||||
const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] => {
|
||||
const maxSeries = 10000;
|
||||
|
@ -71,7 +74,9 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
<td>
|
||||
<SeriesName labels={s.metric} format={doFormat} />
|
||||
</td>
|
||||
<td>{s.value[1]}</td>
|
||||
<td>
|
||||
{s.value && s.value[1]} <HistogramString h={s.histogram && s.histogram[1]} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
@ -79,21 +84,36 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
break;
|
||||
case 'matrix':
|
||||
rows = (limitSeries(data.result) as RangeSamples[]).map((s, seriesIdx) => {
|
||||
const valuesAndTimes = 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 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}>
|
||||
<HistogramString h={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}</td>
|
||||
<td>
|
||||
{valuesAndTimes} {histogramsAndTimes}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
@ -139,4 +159,27 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export interface HistogramStringProps {
|
||||
h?: Histogram;
|
||||
}
|
||||
|
||||
export const HistogramString: FC<HistogramStringProps> = ({ h }) => {
|
||||
if (!h) {
|
||||
return <></>;
|
||||
}
|
||||
const buckets: string[] = [];
|
||||
|
||||
for (const bucket of h.buckets) {
|
||||
const left = bucket[0] === 3 || bucket[0] === 1 ? '[' : '(';
|
||||
const right = bucket[0] === 3 || bucket[0] === 0 ? ']' : ')';
|
||||
buckets.push(left + bucket[1] + ',' + bucket[2] + right + ':' + bucket[3] + ' ');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{'{'} count:{h.count} sum:{h.sum} {buckets} {'}'}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataTable;
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
|
|||
import ReactResizeDetector from 'react-resize-detector';
|
||||
|
||||
import { Legend } from './Legend';
|
||||
import { Metric, ExemplarData, QueryParams } from '../../types/types';
|
||||
import { Metric, Histogram, ExemplarData, QueryParams } from '../../types/types';
|
||||
import { isPresent } from '../../utils';
|
||||
import { normalizeData, getOptions, toHoverColor } from './GraphHelpers';
|
||||
import { Button } from 'reactstrap';
|
||||
|
@ -20,7 +20,7 @@ require('jquery.flot.tooltip');
|
|||
export interface GraphProps {
|
||||
data: {
|
||||
resultType: string;
|
||||
result: Array<{ metric: Metric; values: [number, string][] }>;
|
||||
result: Array<{ metric: Metric; values?: [number, string][]; histograms?: [number, Histogram][] }>;
|
||||
};
|
||||
exemplars: ExemplarData;
|
||||
stacked: boolean;
|
||||
|
|
|
@ -189,17 +189,22 @@ export const normalizeData = ({ queryParams, data, exemplars, stacked }: GraphPr
|
|||
const deviation = stdDeviation(sum, values);
|
||||
|
||||
return {
|
||||
series: data.result.map(({ values, metric }, index) => {
|
||||
series: data.result.map(({ values, histograms, metric }, index) => {
|
||||
// Insert nulls for all missing steps.
|
||||
const data = [];
|
||||
let pos = 0;
|
||||
let valuePos = 0;
|
||||
let histogramPos = 0;
|
||||
|
||||
for (let t = startTime; t <= endTime; t += resolution) {
|
||||
// Allow for floating point inaccuracy.
|
||||
const currentValue = values[pos];
|
||||
if (values.length > pos && currentValue[0] < t + resolution / 100) {
|
||||
const currentValue = values && values[valuePos];
|
||||
const currentHistogram = histograms && histograms[histogramPos];
|
||||
if (currentValue && values.length > valuePos && currentValue[0] < t + resolution / 100) {
|
||||
data.push([currentValue[0] * 1000, parseValue(currentValue[1])]);
|
||||
pos++;
|
||||
valuePos++;
|
||||
} else if (currentHistogram && histograms.length > histogramPos && currentHistogram[0] < t + resolution / 100) {
|
||||
data.push([currentHistogram[0] * 1000, parseValue(currentHistogram[1].sum)]);
|
||||
histogramPos++;
|
||||
} else {
|
||||
data.push([t * 1000, null]);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,12 @@ export interface Metric {
|
|||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface Histogram {
|
||||
count: string;
|
||||
sum: string;
|
||||
buckets: [number, string, string, string][];
|
||||
}
|
||||
|
||||
export interface Exemplar {
|
||||
labels: { [key: string]: string };
|
||||
value: string;
|
||||
|
|
Loading…
Reference in a new issue