mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
Show rules page as collapsed accordion instead of rules table
Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
8165a6e337
commit
719b31f1b5
|
@ -1,7 +1,7 @@
|
|||
import { Badge, Card, Group, useComputedColorScheme } from "@mantine/core";
|
||||
import { Badge, Box, Card, Group, useComputedColorScheme } from "@mantine/core";
|
||||
import { IconClockPause, IconClockPlay } from "@tabler/icons-react";
|
||||
import { FC } from "react";
|
||||
import { formatDuration } from "./lib/formatTime";
|
||||
import { formatPrometheusDuration } from "./lib/formatTime";
|
||||
import codeboxClasses from "./codebox.module.css";
|
||||
import badgeClasses from "./Badge.module.css";
|
||||
import { Rule } from "./api/responseTypes/rules";
|
||||
|
@ -14,6 +14,7 @@ import {
|
|||
promqlHighlighter,
|
||||
} from "./codemirror/theme";
|
||||
import { PromQLExtension } from "@prometheus-io/codemirror-promql";
|
||||
import { LabelBadges } from "./LabelBadges";
|
||||
|
||||
const promqlExtension = new PromQLExtension();
|
||||
|
||||
|
@ -46,7 +47,7 @@ const RuleDefinition: FC<{ rule: Rule }> = ({ rule }) => {
|
|||
styles={{ label: { textTransform: "none" } }}
|
||||
leftSection={<IconClockPause size={12} />}
|
||||
>
|
||||
for: {formatDuration(rule.duration * 1000)}
|
||||
for: {formatPrometheusDuration(rule.duration * 1000)}
|
||||
</Badge>
|
||||
)}
|
||||
{rule.keepFiringFor && (
|
||||
|
@ -55,24 +56,15 @@ const RuleDefinition: FC<{ rule: Rule }> = ({ rule }) => {
|
|||
styles={{ label: { textTransform: "none" } }}
|
||||
leftSection={<IconClockPlay size={12} />}
|
||||
>
|
||||
keep_firing_for: {formatDuration(rule.duration * 1000)}
|
||||
keep_firing_for: {formatPrometheusDuration(rule.duration * 1000)}
|
||||
</Badge>
|
||||
)}
|
||||
</Group>
|
||||
)}
|
||||
{rule.labels && Object.keys(rule.labels).length > 0 && (
|
||||
<Group mt="md" gap="xs">
|
||||
{Object.entries(rule.labels).map(([k, v]) => (
|
||||
<Badge
|
||||
variant="light"
|
||||
className={badgeClasses.labelBadge}
|
||||
styles={{ label: { textTransform: "none" } }}
|
||||
key={k}
|
||||
>
|
||||
{k}: {v}
|
||||
</Badge>
|
||||
))}
|
||||
</Group>
|
||||
<Box mt="md">
|
||||
<LabelBadges labels={rule.labels} />
|
||||
</Box>
|
||||
)}
|
||||
{/* {Object.keys(r.annotations).length > 0 && (
|
||||
<Group mt="md" gap="xs">
|
||||
|
|
|
@ -13,7 +13,7 @@ type CommonRuleFields = {
|
|||
name: string;
|
||||
query: string;
|
||||
evaluationTime: string;
|
||||
health: string;
|
||||
health: "ok" | "unknown" | "err";
|
||||
lastError?: string;
|
||||
lastEvaluation: string;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
Accordion,
|
||||
Alert,
|
||||
Badge,
|
||||
Card,
|
||||
|
@ -9,12 +10,17 @@ import {
|
|||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
// import { useQuery } from "react-query";
|
||||
import { formatRelative, humanizeDuration, now } from "../lib/formatTime";
|
||||
import {
|
||||
humanizeDurationRelative,
|
||||
humanizeDuration,
|
||||
now,
|
||||
} from "../lib/formatTime";
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconBell,
|
||||
IconDatabaseImport,
|
||||
IconHourglass,
|
||||
IconInfoCircle,
|
||||
IconRefresh,
|
||||
IconRepeat,
|
||||
} from "@tabler/icons-react";
|
||||
|
@ -32,7 +38,7 @@ const healthBadgeClass = (state: string) => {
|
|||
case "unknown":
|
||||
return badgeClasses.healthUnknown;
|
||||
default:
|
||||
return "orange";
|
||||
throw new Error("Unknown rule health state");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -41,6 +47,11 @@ export default function RulesPage() {
|
|||
|
||||
return (
|
||||
<Stack mt="xs">
|
||||
{data.data.groups.length === 0 && (
|
||||
<Alert title="No rule groups" icon={<IconInfoCircle size={14} />}>
|
||||
No rule groups configured.
|
||||
</Alert>
|
||||
)}
|
||||
{data.data.groups.map((g, i) => (
|
||||
<Card
|
||||
shadow="xs"
|
||||
|
@ -66,7 +77,7 @@ export default function RulesPage() {
|
|||
styles={{ label: { textTransform: "none" } }}
|
||||
leftSection={<IconRefresh size={12} />}
|
||||
>
|
||||
last run {formatRelative(g.lastEvaluation, now())}
|
||||
last run {humanizeDurationRelative(g.lastEvaluation, now())}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
<Tooltip label="Duration of last group evaluation" withArrow>
|
||||
|
@ -91,21 +102,34 @@ export default function RulesPage() {
|
|||
</Tooltip>
|
||||
</Group>
|
||||
</Group>
|
||||
<Table>
|
||||
<Table.Tbody>
|
||||
{g.rules.map((r) => (
|
||||
// TODO: Find a stable and definitely unique key.
|
||||
<Table.Tr key={r.name}>
|
||||
<Table.Td p="md" py="xl" valign="top">
|
||||
{g.rules.length === 0 && (
|
||||
<Alert title="No rules" icon={<IconInfoCircle size={14} />}>
|
||||
No rules in rule group.
|
||||
</Alert>
|
||||
)}
|
||||
<Accordion multiple variant="separated">
|
||||
{g.rules.map((r, j) => (
|
||||
<Accordion.Item
|
||||
key={j}
|
||||
value={j.toString()}
|
||||
style={{
|
||||
borderLeft:
|
||||
r.health === "err"
|
||||
? "5px solid var(--mantine-color-red-4)"
|
||||
: r.health === "unknown"
|
||||
? "5px solid var(--mantine-color-gray-5)"
|
||||
: "5px solid var(--mantine-color-green-4)",
|
||||
}}
|
||||
>
|
||||
<Accordion.Control>
|
||||
<Group wrap="nowrap" justify="space-between" mr="lg">
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
{r.type === "alerting" ? (
|
||||
<IconBell size={14} />
|
||||
<IconBell size={15} />
|
||||
) : (
|
||||
<IconDatabaseImport size={14} />
|
||||
<IconDatabaseImport size={15} />
|
||||
)}
|
||||
<Text fz="sm" fw={600}>
|
||||
{r.name}
|
||||
</Text>
|
||||
<Text>{r.name}</Text>
|
||||
</Group>
|
||||
<Group mt="md" gap="xs">
|
||||
<Badge className={healthBadgeClass(r.health)}>
|
||||
|
@ -120,7 +144,7 @@ export default function RulesPage() {
|
|||
styles={{ label: { textTransform: "none" } }}
|
||||
leftSection={<IconRefresh size={12} />}
|
||||
>
|
||||
{formatRelative(r.lastEvaluation, now())}
|
||||
{humanizeDurationRelative(r.lastEvaluation, now())}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
|
||||
|
@ -141,24 +165,24 @@ export default function RulesPage() {
|
|||
</Tooltip>
|
||||
</Group>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td p="md" py="xl">
|
||||
<RuleDefinition rule={r} />
|
||||
{r.lastError && (
|
||||
<Alert
|
||||
color="red"
|
||||
mt="sm"
|
||||
title="Rule failed to evaluate"
|
||||
icon={<IconAlertTriangle size={14} />}
|
||||
>
|
||||
<strong>Error:</strong> {r.lastError}
|
||||
</Alert>
|
||||
)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Group>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<RuleDefinition rule={r} />
|
||||
{r.lastError && (
|
||||
<Alert
|
||||
color="red"
|
||||
mt="sm"
|
||||
title="Rule failed to evaluate"
|
||||
icon={<IconAlertTriangle size={14} />}
|
||||
>
|
||||
<strong>Error:</strong> {r.lastError}
|
||||
</Alert>
|
||||
)}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
</Accordion>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
|
|
Loading…
Reference in a new issue