mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-12 06:17:27 -08:00
Add graphing component stub
Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
9093e0ab91
commit
ebb468cedf
172
web/ui/mantine-ui/src/pages/query/Graph.tsx
Normal file
172
web/ui/mantine-ui/src/pages/query/Graph.tsx
Normal file
|
@ -0,0 +1,172 @@
|
|||
import { FC, useEffect, useId } from "react";
|
||||
import { Table, Alert, Skeleton, Box, LoadingOverlay } from "@mantine/core";
|
||||
import { IconAlertTriangle, IconInfoCircle } from "@tabler/icons-react";
|
||||
import {
|
||||
InstantQueryResult,
|
||||
InstantSample,
|
||||
RangeSamples,
|
||||
} from "../../api/responseTypes/query";
|
||||
import SeriesName from "./SeriesName";
|
||||
import { useAPIQuery } from "../../api/api";
|
||||
import classes from "./DataTable.module.css";
|
||||
import { GraphDisplayMode } from "../../state/queryPageSlice";
|
||||
|
||||
const maxFormattableSeries = 1000;
|
||||
const maxDisplayableSeries = 10000;
|
||||
|
||||
const limitSeries = <S extends InstantSample | RangeSamples>(
|
||||
series: S[]
|
||||
): S[] => {
|
||||
if (series.length > maxDisplayableSeries) {
|
||||
return series.slice(0, maxDisplayableSeries);
|
||||
}
|
||||
return series;
|
||||
};
|
||||
|
||||
export interface GraphProps {
|
||||
expr: string;
|
||||
endTime: number | null;
|
||||
range: number;
|
||||
resolution: number | null;
|
||||
showExemplars: boolean;
|
||||
displayMode: GraphDisplayMode;
|
||||
retriggerIdx: number;
|
||||
}
|
||||
|
||||
const Graph: FC<GraphProps> = ({
|
||||
expr,
|
||||
endTime,
|
||||
range,
|
||||
resolution,
|
||||
showExemplars,
|
||||
displayMode,
|
||||
retriggerIdx,
|
||||
}) => {
|
||||
const realEndTime = (endTime !== null ? endTime : Date.now()) / 1000;
|
||||
const { data, error, isFetching, isLoading, refetch } =
|
||||
useAPIQuery<InstantQueryResult>({
|
||||
key: useId(),
|
||||
path: "/query_range",
|
||||
params: {
|
||||
query: expr,
|
||||
step: (
|
||||
resolution || Math.max(Math.floor(range / 250000), 1)
|
||||
).toString(),
|
||||
start: (realEndTime - range / 1000).toString(),
|
||||
end: realEndTime.toString(),
|
||||
},
|
||||
enabled: expr !== "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
expr !== "" && refetch();
|
||||
}, [retriggerIdx, refetch, expr, endTime, range, resolution]);
|
||||
|
||||
// TODO: Share all the loading/error/empty data notices with the DataTable.
|
||||
|
||||
// Show a skeleton only on the first load, not on subsequent ones.
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Box>
|
||||
{Array.from(Array(5), (_, i) => (
|
||||
<Skeleton key={i} height={30} mb={15} />
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Alert
|
||||
color="red"
|
||||
title="Error executing query"
|
||||
icon={<IconAlertTriangle size={14} />}
|
||||
>
|
||||
{error.message}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
if (data === undefined) {
|
||||
return <Alert variant="transparent">No data queried yet</Alert>;
|
||||
}
|
||||
|
||||
const { result, resultType } = data.data;
|
||||
|
||||
if (result.length === 0) {
|
||||
return (
|
||||
<Alert title="Empty query result" icon={<IconInfoCircle size={14} />}>
|
||||
This query returned no data.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
const doFormat = result.length <= maxFormattableSeries;
|
||||
|
||||
return (
|
||||
<Box pos="relative" className={classes.tableWrapper}>
|
||||
<LoadingOverlay
|
||||
visible={isFetching}
|
||||
zIndex={1000}
|
||||
overlayProps={{ radius: "sm", blur: 1 }}
|
||||
loaderProps={{
|
||||
children: <Skeleton m={0} w="100%" h="100%" />,
|
||||
}}
|
||||
styles={{ loader: { width: "100%", height: "100%" } }}
|
||||
/>
|
||||
<Table highlightOnHover fz="xs">
|
||||
<Table.Tbody>
|
||||
{resultType === "vector" ? (
|
||||
limitSeries<InstantSample>(result).map((s, idx) => (
|
||||
<Table.Tr key={idx}>
|
||||
<Table.Td>
|
||||
<SeriesName labels={s.metric} format={doFormat} />
|
||||
</Table.Td>
|
||||
<Table.Td className={classes.numberCell}>
|
||||
{s.value && s.value[1]}
|
||||
{s.histogram && "TODO HISTOGRAM DISPLAY"}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
) : resultType === "matrix" ? (
|
||||
limitSeries<RangeSamples>(result).map((s, idx) => (
|
||||
<Table.Tr key={idx}>
|
||||
<Table.Td>
|
||||
<SeriesName labels={s.metric} format={doFormat} />
|
||||
</Table.Td>
|
||||
<Table.Td className={classes.numberCell}>
|
||||
{s.values &&
|
||||
s.values.map((v, idx) => (
|
||||
<div key={idx}>
|
||||
{v[1]} @ {v[0]}
|
||||
</div>
|
||||
))}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
) : resultType === "scalar" ? (
|
||||
<Table.Tr>
|
||||
<Table.Td>Scalar value</Table.Td>
|
||||
<Table.Td className={classes.numberCell}>{result[1]}</Table.Td>
|
||||
</Table.Tr>
|
||||
) : resultType === "string" ? (
|
||||
<Table.Tr>
|
||||
<Table.Td>String value</Table.Td>
|
||||
<Table.Td>{result[1]}</Table.Td>
|
||||
</Table.Tr>
|
||||
) : (
|
||||
<Alert
|
||||
color="red"
|
||||
title="Invalid query response"
|
||||
icon={<IconAlertTriangle size={14} />}
|
||||
>
|
||||
Invalid result value type
|
||||
</Alert>
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Graph;
|
|
@ -27,6 +27,7 @@ import DataTable from "./DataTable";
|
|||
import TimeInput from "./TimeInput";
|
||||
import RangeInput from "./RangeInput";
|
||||
import ExpressionInput from "./ExpressionInput";
|
||||
import Graph from "./Graph";
|
||||
|
||||
export interface PanelProps {
|
||||
idx: number;
|
||||
|
@ -107,7 +108,6 @@ const QueryPanel: FC<PanelProps> = ({ idx, metricNames }) => {
|
|||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<TimeInput
|
||||
time={panel.visualizer.endTime}
|
||||
range={panel.visualizer.range}
|
||||
|
@ -203,17 +203,15 @@ const QueryPanel: FC<PanelProps> = ({ idx, metricNames }) => {
|
|||
/> */}
|
||||
</Group>
|
||||
<Space h="lg" />
|
||||
<Center
|
||||
style={{
|
||||
height: 450,
|
||||
backgroundColor: "#fbfbfb",
|
||||
border: "2px dotted #e7e7e7",
|
||||
fontSize: 20,
|
||||
color: "#999",
|
||||
}}
|
||||
>
|
||||
GRAPH PLACEHOLDER
|
||||
</Center>
|
||||
<Graph
|
||||
expr={panel.expr}
|
||||
endTime={panel.visualizer.endTime}
|
||||
range={panel.visualizer.range}
|
||||
resolution={panel.visualizer.resolution}
|
||||
showExemplars={panel.visualizer.showExemplars}
|
||||
displayMode={panel.visualizer.displayMode}
|
||||
retriggerIdx={retriggerIdx}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
{/* Link button to remove this panel. */}
|
||||
|
|
Loading…
Reference in a new issue