diff --git a/rules/alerting.go b/rules/alerting.go index 9a6ff0a113..b0151d7cb3 100644 --- a/rules/alerting.go +++ b/rules/alerting.go @@ -50,8 +50,10 @@ const ( type AlertState int const ( + // StateUnknown is the state of an alert that has not yet been evaluated. + StateUnknown AlertState = iota // StateInactive is the state of an alert that is neither firing nor pending. - StateInactive AlertState = iota + StateInactive // StatePending is the state of an alert that has been active for less than // the configured threshold duration. StatePending @@ -62,6 +64,8 @@ const ( func (s AlertState) String() string { switch s { + case StateUnknown: + return "unknown" case StateInactive: return "inactive" case StatePending: @@ -530,10 +534,14 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t } // State returns the maximum state of alert instances for this rule. -// StateFiring > StatePending > StateInactive. +// StateFiring > StatePending > StateInactive > StateUnknown. func (r *AlertingRule) State() AlertState { r.activeMtx.Lock() defer r.activeMtx.Unlock() + // Check if the rule has been evaluated + if r.evaluationTimestamp.Load().IsZero() { + return StateUnknown + } maxState := StateInactive for _, a := range r.active { diff --git a/rules/alerting_test.go b/rules/alerting_test.go index 49817cc286..fff1d40048 100644 --- a/rules/alerting_test.go +++ b/rules/alerting_test.go @@ -85,6 +85,8 @@ func TestAlertingRuleState(t *testing.T) { for i, test := range tests { rule := NewAlertingRule(test.name, nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil) rule.active = test.active + // Set evaluation timestamp to simulate that the rule has been evaluated + rule.SetEvaluationTimestamp(time.Now()) got := rule.State() require.Equal(t, test.want, got, "test case %d unexpected AlertState, want:%d got:%d", i, test.want, got) } diff --git a/web/ui/mantine-ui/src/api/responseTypes/rules.ts b/web/ui/mantine-ui/src/api/responseTypes/rules.ts index ff04ad2cc9..89ce0f4acc 100644 --- a/web/ui/mantine-ui/src/api/responseTypes/rules.ts +++ b/web/ui/mantine-ui/src/api/responseTypes/rules.ts @@ -1,4 +1,4 @@ -type RuleState = "pending" | "firing" | "inactive"; +type RuleState = "pending" | "firing" | "inactive" | "unknown"; export interface Alert { labels: Record; diff --git a/web/ui/mantine-ui/src/pages/AlertsPage.tsx b/web/ui/mantine-ui/src/pages/AlertsPage.tsx index 00e6595fd4..2608117c8f 100644 --- a/web/ui/mantine-ui/src/pages/AlertsPage.tsx +++ b/web/ui/mantine-ui/src/pages/AlertsPage.tsx @@ -45,6 +45,7 @@ type AlertsPageData = { inactive: number; pending: number; firing: number; + unknown: number; }; groups: { name: string; @@ -55,6 +56,7 @@ type AlertsPageData = { inactive: number; pending: number; firing: number; + unknown: number; }; rules: { rule: AlertingRule; @@ -82,6 +84,7 @@ const buildAlertsPageData = ( inactive: 0, pending: 0, firing: 0, + unknown: 0, }, groups: [], }; @@ -92,6 +95,7 @@ const buildAlertsPageData = ( inactive: 0, pending: 0, firing: 0, + unknown: 0, }; for (const r of group.rules) { @@ -109,6 +113,10 @@ const buildAlertsPageData = ( pageData.globalCounts.pending++; groupCounts.pending++; break; + case "unknown": + pageData.globalCounts.unknown++; + groupCounts.unknown++; + break; default: throw new Error(`Unknown rule state: ${r.state}`); } @@ -239,6 +247,11 @@ export default function AlertsPage() { pending ({g.counts.pending}) )} + {g.counts.unknown > 0 && ( + + unknown ({g.counts.unknown}) + + )} {g.counts.inactive > 0 && ( inactive ({g.counts.inactive}) @@ -285,7 +298,9 @@ export default function AlertsPage() { ? panelClasses.panelHealthErr : r.counts.pending > 0 ? panelClasses.panelHealthWarn - : panelClasses.panelHealthOk + : r.rule.state === "unknown" + ? panelClasses.panelHealthUnknown + : panelClasses.panelHealthOk } > o === "inactive" ? badgeClasses.healthOk : o === "pending" ? badgeClasses.healthWarn - : badgeClasses.healthErr + : o === "firing" + ? badgeClasses.healthErr + : badgeClasses.healthUnknown } optionCount={(o) => alertsPageData.globalCounts[