github.com/thanos-io/thanos@v0.32.5/pkg/ui/react-app/src/pages/graph/DataTable.tsx (about) 1 import React, { FC, ReactNode } from 'react'; 2 3 import { UncontrolledAlert, Table } from 'reactstrap'; 4 5 import SeriesName from './SeriesName'; 6 import { Metric, Histogram, SampleValue, SampleHistogram } from '../../types/types'; 7 8 import moment from 'moment'; 9 10 export interface QueryResult { 11 data: 12 | null 13 | { 14 resultType: 'vector'; 15 result: InstantSample[]; 16 } 17 | { 18 resultType: 'matrix'; 19 result: RangeSamples[]; 20 } 21 | { 22 resultType: 'scalar'; 23 result: SampleValue; 24 } 25 | { 26 resultType: 'string'; 27 result: string; 28 }; 29 } 30 31 interface InstantSample { 32 metric: Metric; 33 value?: SampleValue; 34 histogram?: SampleHistogram; 35 } 36 37 interface RangeSamples { 38 metric: Metric; 39 values?: SampleValue[]; 40 histograms?: SampleHistogram[]; 41 } 42 43 const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] => { 44 const maxSeries = 10000; 45 46 if (series.length > maxSeries) { 47 return series.slice(0, maxSeries); 48 } 49 return series; 50 }; 51 52 const DataTable: FC<QueryResult> = ({ data }) => { 53 if (data === null) { 54 return <UncontrolledAlert color="light">No data queried yet</UncontrolledAlert>; 55 } 56 57 if (data.result === null || data.result.length === 0) { 58 return <UncontrolledAlert color="secondary">Empty query result</UncontrolledAlert>; 59 } 60 61 const maxFormattableSize = 1000; 62 let rows: ReactNode[] = []; 63 let limited = false; 64 const doFormat = data.result.length <= maxFormattableSize; 65 switch (data.resultType) { 66 case 'vector': 67 rows = (limitSeries(data.result) as InstantSample[]).map((s: InstantSample, index: number): ReactNode => { 68 return ( 69 <tr key={index}> 70 <td> 71 <SeriesName labels={s.metric} format={doFormat} /> 72 </td> 73 <td> 74 {s.value && s.value[1]} <HistogramString h={s.histogram && s.histogram[1]} /> 75 </td> 76 </tr> 77 ); 78 }); 79 limited = rows.length !== data.result.length; 80 break; 81 case 'matrix': 82 rows = (limitSeries(data.result) as RangeSamples[]).map((s, index) => { 83 const valuesAndTimes = s.values 84 ? s.values 85 .map((v) => { 86 return v[1] + ' @' + v[0]; 87 }) 88 .join('\n') 89 : []; 90 const histogramsAndTimes = s.histograms 91 ? s.histograms.map((h, hisIdx) => { 92 const printedDatetime = moment.unix(h[0]).toISOString(false); 93 return ( 94 <React.Fragment key={-hisIdx}> 95 <HistogramString h={h[1]} /> @{<span title={printedDatetime}>{h[0]}</span>} 96 <br /> 97 </React.Fragment> 98 ); 99 }) 100 : []; 101 return ( 102 <tr style={{ whiteSpace: 'pre' }} key={index}> 103 <td> 104 <SeriesName labels={s.metric} format={doFormat} /> 105 </td> 106 <td> 107 {valuesAndTimes} {histogramsAndTimes} 108 </td> 109 </tr> 110 ); 111 }); 112 limited = rows.length !== data.result.length; 113 break; 114 case 'scalar': 115 rows.push( 116 <tr key="0"> 117 <td>scalar</td> 118 <td>{data.result[1]}</td> 119 </tr> 120 ); 121 break; 122 case 'string': 123 rows.push( 124 <tr key="0"> 125 <td>string</td> 126 <td>{data.result[1]}</td> 127 </tr> 128 ); 129 break; 130 default: 131 return <UncontrolledAlert color="danger">Unsupported result value type</UncontrolledAlert>; 132 } 133 134 return ( 135 <> 136 {limited && ( 137 <UncontrolledAlert color="danger"> 138 <strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}. 139 </UncontrolledAlert> 140 )} 141 {!doFormat && ( 142 <UncontrolledAlert color="secondary"> 143 <strong>Notice:</strong> Showing more than {maxFormattableSize} series, turning off label formatting for 144 performance reasons. 145 </UncontrolledAlert> 146 )} 147 <Table hover size="sm" className="data-table"> 148 <tbody>{rows}</tbody> 149 </Table> 150 </> 151 ); 152 }; 153 154 export interface HistogramStringProps { 155 h?: Histogram; 156 } 157 158 export const HistogramString: FC<HistogramStringProps> = ({ h }) => { 159 if (!h) { 160 return <></>; 161 } 162 const buckets: string[] = []; 163 164 if (h.buckets) { 165 for (const bucket of h.buckets) { 166 const left = bucket[0] === 3 || bucket[0] === 1 ? '[' : '('; 167 const right = bucket[0] === 3 || bucket[0] === 0 ? ']' : ')'; 168 buckets.push(left + bucket[1] + ',' + bucket[2] + right + ':' + bucket[3] + ' '); 169 } 170 } 171 172 return ( 173 <> 174 {'{'} count:{h.count} sum:{h.sum} {buckets} {'}'} 175 </> 176 ); 177 }; 178 179 export default DataTable;