Fetch and display full query stats in hover tooltip in table query tab (#16723)

Fixes https://github.com/prometheus/prometheus/issues/5857

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2025-06-12 16:16:41 +02:00 committed by GitHub
parent c9d638fd8f
commit 4eeeb6ee88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 7 deletions

View File

@ -23,9 +23,14 @@ export interface RangeSamples {
export type SampleValue = [number, string];
export type SampleHistogram = [number, Histogram];
export type QueryStats = {
timings: Record<string, number>;
samples: Record<string, number>;
};
// Result type for /api/v1/query endpoint.
// See: https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries
export type InstantQueryResult =
export type InstantQueryResult = (
| {
resultType: "vector";
result: InstantSample[];
@ -41,11 +46,13 @@ export type InstantQueryResult =
| {
resultType: "string";
result: SampleValue;
};
}
) & { stats?: QueryStats };
// Result type for /api/v1/query_range endpoint.
// See: https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries
export type RangeQueryResult = {
resultType: "matrix";
result: RangeSamples[];
stats?: QueryStats;
};

View File

@ -0,0 +1,49 @@
import { FC } from "react";
import { Box, Text, Tooltip, Table } from "@mantine/core";
import { QueryStats } from "../../api/responseTypes/query";
const statsTable = (stats: Record<string, number>) => {
return (
<Table withRowBorders={false}>
<Table.Tbody>
{Object.entries(stats).map(([k, v]) => (
<Table.Tr key={k}>
<Table.Td pl={0} py={3} c="dimmed">
{k}
</Table.Td>
<Table.Td pr={0} py={3} ta="right">
{v}
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
);
};
const QueryStatsDisplay: FC<{
numResults: number;
responseTime: number;
stats: QueryStats;
}> = ({ numResults, responseTime, stats }) => {
return (
<Tooltip
label={
<Box p="xs">
<Text mb="xs">Timing stats (s):</Text>
{statsTable(stats.timings)}
<Text mt="sm" mb="xs">
Sample stats:
</Text>
{statsTable(stats.samples)}
</Box>
}
>
<Text size="xs" c="gray">
Load time: {responseTime}ms &ensp; Result series: {numResults}
</Text>
</Tooltip>
);
};
export default QueryStatsDisplay;

View File

@ -1,5 +1,5 @@
import { FC, useEffect, useId, useLayoutEffect, useState } from "react";
import { Alert, Skeleton, Box, Group, Stack, Text } from "@mantine/core";
import { Alert, Skeleton, Box, Group, Stack } from "@mantine/core";
import { IconAlertTriangle, IconInfoCircle } from "@tabler/icons-react";
import { InstantQueryResult } from "../../api/responseTypes/query";
import { useAPIQuery } from "../../api/api";
@ -9,6 +9,7 @@ import { useAppDispatch, useAppSelector } from "../../state/hooks";
import { setVisualizer } from "../../state/queryPageSlice";
import TimeInput from "./TimeInput";
import DataTable from "./DataTable";
import QueryStatsDisplay from "./QueryStatsDisplay";
dayjs.extend(timezone);
export interface TableTabProps {
@ -34,6 +35,7 @@ const TableTab: FC<TableTabProps> = ({ panelIdx, retriggerIdx, expr }) => {
params: {
query: expr,
time: `${(endTime !== null ? endTime : Date.now()) / 1000}`,
stats: "true",
},
enabled: expr !== "",
recordResponseTime: setResponseTime,
@ -66,10 +68,11 @@ const TableTab: FC<TableTabProps> = ({ panelIdx, retriggerIdx, expr }) => {
}
/>
{!isFetching && data !== undefined && (
<Text size="xs" c="gray">
Load time: {responseTime}ms &ensp; Result series:{" "}
{data.data.result.length}
</Text>
<QueryStatsDisplay
numResults={data.data.result.length}
responseTime={responseTime}
stats={data.data.stats!}
/>
)}
</Group>
{isFetching ? (