diff --git a/docs/sources/alerting/unified-alerting/alerting-rules/rule-list.md b/docs/sources/alerting/unified-alerting/alerting-rules/rule-list.md index 2f6f83be58f..8c74162d28c 100644 --- a/docs/sources/alerting/unified-alerting/alerting-rules/rule-list.md +++ b/docs/sources/alerting/unified-alerting/alerting-rules/rule-list.md @@ -8,9 +8,10 @@ weight = 400 # View alert rules To view alerts: + 1. In the Grafana menu hover your cursor over the Alerting (bell) icon. -1. Click **Alert Rules**. You can see all configured Grafana alert rules as well as any rules from Loki or Prometheus data sources. -By default, the group view is shown. You can toggle between group or state views by clicking the relevant **View as** buttons in the options area at the top of the page. +1. Click **Alert Rules**. You can see all configured Grafana alert rules as well as any rules from Loki or Prometheus data sources. + By default, the group view is shown. You can toggle between group or state views by clicking the relevant **View as** buttons in the options area at the top of the page. ### Group view @@ -25,9 +26,10 @@ State view shows alert rules grouped by state. Use this view to get an overview  ## Filter alert rules + You can use the following filters to view only alert rules that match specific criteria: -- **Filter alerts by name or label -** Type an alert name, label name or value in the **Search** input. +- **Filter alerts by label -** Search by alert labels using label selectors in the **Search** input. eg: `environment=production,region=~US|EU,severity!=warning` - **Filter alerts by state -** In **States** Select which alert states you want to see. All others are hidden. - **Filter alerts by data source -** Click the **Select data source** and select an alerting data source. Only alert rules that query selected data source will be visible. @@ -39,13 +41,13 @@ A rule row shows the rule state, health, and summary annotation if the rule has ### Edit or delete rule -Grafana rules can only be edited or deleted by users with Edit permissions for the folder which contains the rule. Prometheus or Loki rules can be edited or deleted by users with Editor or Admin roles. +Grafana rules can only be edited or deleted by users with Edit permissions for the folder which contains the rule. Prometheus or Loki rules can be edited or deleted by users with Editor or Admin roles. To edit or delete a rule: -1. Expand this rule to reveal rule controls. +1. Expand this rule to reveal rule controls. 1. Click **Edit** to go to the rule editing form. Make changes following [instructions listed here]({{< relref "./create-grafana-managed-rule.md" >}}). -1. Click **Delete"** to delete a rule. +1. Click **Delete"** to delete a rule. ## Opt-out a Loki or Prometheus data source diff --git a/packages/grafana-data/src/types/data.ts b/packages/grafana-data/src/types/data.ts index ae88e3baef6..344c119b4fb 100644 --- a/packages/grafana-data/src/types/data.ts +++ b/packages/grafana-data/src/types/data.ts @@ -128,7 +128,6 @@ export interface QueryResultBase { export interface Labels { [key: string]: string; } - export interface Column { text: string; // For a Column, the 'text' is the field name filterable?: boolean; diff --git a/public/app/features/alerting/unified/RuleList.test.tsx b/public/app/features/alerting/unified/RuleList.test.tsx index 95e0b221f7b..22554bc065d 100644 --- a/public/app/features/alerting/unified/RuleList.test.tsx +++ b/public/app/features/alerting/unified/RuleList.test.tsx @@ -76,6 +76,7 @@ const ui = { rulesTable: byTestId('rules-table'), ruleRow: byTestId('row'), expandedContent: byTestId('expanded-content'), + rulesFilterInput: byTestId('search-query-input'), }; describe('RuleList', () => { @@ -299,4 +300,140 @@ describe('RuleList', () => { userEvent.click(ui.groupCollapseToggle.get(groups[1])); expect(ui.rulesTable.query()).not.toBeInTheDocument(); }); + + it('filters rules and alerts by labels', async () => { + mocks.getAllDataSourcesMock.mockReturnValue([dataSources.prom]); + setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom })); + + mocks.api.fetchRules.mockImplementation((dataSourceName: string) => { + if (dataSourceName === GRAFANA_RULES_SOURCE_NAME) { + return Promise.resolve([]); + } else { + return Promise.resolve([ + mockPromRuleNamespace({ + groups: [ + mockPromRuleGroup({ + name: 'group-1', + rules: [ + mockPromAlertingRule({ + name: 'alertingrule', + labels: { + severity: 'warning', + foo: 'bar', + }, + query: 'topk(5, foo)[5m]', + annotations: { + message: 'great alert', + }, + alerts: [ + mockPromAlert({ + labels: { + foo: 'bar', + severity: 'warning', + }, + value: '2e+10', + annotations: { + message: 'first alert message', + }, + }), + mockPromAlert({ + labels: { + foo: 'baz', + severity: 'error', + }, + value: '3e+11', + annotations: { + message: 'first alert message', + }, + }), + ], + }), + ], + }), + mockPromRuleGroup({ + name: 'group-2', + rules: [ + mockPromAlertingRule({ + name: 'alertingrule2', + labels: { + severity: 'error', + foo: 'buzz', + }, + query: 'topk(5, foo)[5m]', + annotations: { + message: 'great alert', + }, + alerts: [ + mockPromAlert({ + labels: { + foo: 'buzz', + severity: 'error', + region: 'EU', + }, + value: '2e+10', + annotations: { + message: 'alert message', + }, + }), + mockPromAlert({ + labels: { + foo: 'buzz', + severity: 'error', + region: 'US', + }, + value: '3e+11', + annotations: { + message: 'alert message', + }, + }), + ], + }), + ], + }), + ], + }), + ]); + } + }); + + await renderRuleList(); + const groups = await ui.ruleGroup.findAll(); + expect(groups).toHaveLength(2); + + const filterInput = ui.rulesFilterInput.get(); + userEvent.type(filterInput, '{foo="bar"}'); + + // Input is debounced so wait for it to be visible + waitFor(() => expect(filterInput).toHaveTextContent('{foo="bar"}')); + // Group doesn't contain matching labels + waitFor(() => expect(groups[1]).not.toBeVisible()); + expect(groups[0]).toBeVisible(); + + userEvent.click(ui.groupCollapseToggle.get(groups[0])); + + const ruleRows = ui.ruleRow.getAll(groups[0]); + expect(ruleRows).toHaveLength(1); + + userEvent.click(ui.ruleCollapseToggle.get(ruleRows[0])); + const ruleDetails = ui.expandedContent.get(ruleRows[0]); + + expect(ruleDetails).toHaveTextContent('Labelsseverity=warningfoo=bar'); + + // Check for different label matchers + userEvent.type(filterInput, '{foo!="bar"}'); + waitFor(() => expect(filterInput).toHaveTextContent('{foo!="bar"}')); + // Group doesn't contain matching labels + waitFor(() => expect(groups[0]).not.toBeVisible()); + expect(groups[1]).toBeVisible(); + + userEvent.type(filterInput, '{foo=~"b.+"}'); + waitFor(() => expect(filterInput).toHaveTextContent('{foo=~"b.+"}')); + expect(groups[0]).toBeVisible(); + expect(groups[1]).toBeVisible(); + + userEvent.type(filterInput, '{region="US"}'); + waitFor(() => expect(filterInput).toHaveTextContent('{region="US"}')); + waitFor(() => expect(groups[0]).not.toBeVisible()); + expect(groups[1]).toBeVisible(); + }); }); diff --git a/public/app/features/alerting/unified/components/rules/RulesFilter.tsx b/public/app/features/alerting/unified/components/rules/RulesFilter.tsx index 6b3de2b4906..ca6f4a91603 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesFilter.tsx @@ -1,5 +1,5 @@ import React, { FormEvent, useState } from 'react'; -import { Button, Icon, Input, Label, RadioButtonGroup, useStyles } from '@grafana/ui'; +import { Button, Icon, Input, Label, RadioButtonGroup, Tooltip, useStyles } from '@grafana/ui'; import { DataSourceInstanceSettings, GrafanaTheme, SelectableValue } from '@grafana/data'; import { css, cx } from '@emotion/css'; import { debounce } from 'lodash'; @@ -80,7 +80,19 @@ const RulesFilter = () => {