mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 02:10:45 -06:00
Alerting: Limit instances on alert detail view unless in instances tab (#89368)
This commit is contained in:
parent
b59ebf85bc
commit
87b8da1719
@ -8,7 +8,7 @@ import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
|
||||
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||
import { AlertRuleProvider } from './components/rule-viewer/RuleContext';
|
||||
import DetailView from './components/rule-viewer/RuleViewer';
|
||||
import DetailView, { ActiveTab, useActiveTab } from './components/rule-viewer/RuleViewer';
|
||||
import { useCombinedRule } from './hooks/useCombinedRule';
|
||||
import { stringifyErrorLike } from './utils/misc';
|
||||
import { getRuleIdFromPathname, parse as parseRuleId } from './utils/rule-id';
|
||||
@ -21,6 +21,13 @@ type RuleViewerProps = GrafanaRouteComponentProps<{
|
||||
const RuleViewer = (props: RuleViewerProps): JSX.Element => {
|
||||
const id = getRuleIdFromPathname(props.match.params);
|
||||
|
||||
const [activeTab] = useActiveTab();
|
||||
const instancesTab = activeTab === ActiveTab.Instances;
|
||||
|
||||
// We will fetch no instances by default to speed up loading times and reduce memory footprint _unless_ we are visiting
|
||||
// the "instances" tab. This optimization is only available for the Grafana-managed ruler.
|
||||
const limitAlerts = instancesTab ? undefined : 0; // "0" means "do not include alert rule instances in the response"
|
||||
|
||||
// we convert the stringified ID to a rule identifier object which contains additional
|
||||
// type and source information
|
||||
const identifier = React.useMemo(() => {
|
||||
@ -32,7 +39,7 @@ const RuleViewer = (props: RuleViewerProps): JSX.Element => {
|
||||
}, [id]);
|
||||
|
||||
// we then fetch the rule from the correct API endpoint(s)
|
||||
const { loading, error, result: rule } = useCombinedRule({ ruleIdentifier: identifier });
|
||||
const { loading, error, result: rule } = useCombinedRule({ ruleIdentifier: identifier, limitAlerts });
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
@ -44,6 +44,13 @@ export interface Datasource {
|
||||
export const PREVIEW_URL = '/api/v1/rule/test/grafana';
|
||||
export const PROM_RULES_URL = 'api/prometheus/grafana/api/v1/rules';
|
||||
|
||||
export enum PrometheusAPIFilters {
|
||||
RuleGroup = 'rule_group',
|
||||
Namespace = 'file',
|
||||
FolderUID = 'folder_uid',
|
||||
LimitAlerts = 'limit_alerts',
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
refId: string;
|
||||
relativeTimeRange: RelativeTimeRange;
|
||||
@ -137,12 +144,12 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
||||
// if we're fetching for Grafana managed rules, we should add a limit to the number of alert instances
|
||||
// we do this because the response is large otherwise and we don't show all of them in the UI anyway.
|
||||
if (limitAlerts) {
|
||||
searchParams.set('limit_alerts', String(limitAlerts));
|
||||
searchParams.set(PrometheusAPIFilters.LimitAlerts, String(limitAlerts));
|
||||
}
|
||||
|
||||
if (identifier && (isPrometheusRuleIdentifier(identifier) || isCloudRuleIdentifier(identifier))) {
|
||||
searchParams.set('file', identifier.namespace);
|
||||
searchParams.set('rule_group', identifier.groupName);
|
||||
searchParams.set(PrometheusAPIFilters.Namespace, identifier.namespace);
|
||||
searchParams.set(PrometheusAPIFilters.RuleGroup, identifier.groupName);
|
||||
}
|
||||
|
||||
const params = prepareRulesFilterQueryParams(searchParams, filter);
|
||||
@ -163,9 +170,10 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
||||
ruleName?: string;
|
||||
dashboardUid?: string;
|
||||
panelId?: number;
|
||||
limitAlerts?: number;
|
||||
}
|
||||
>({
|
||||
query: ({ ruleSourceName, namespace, groupName, ruleName, dashboardUid, panelId }) => {
|
||||
query: ({ ruleSourceName, namespace, groupName, ruleName, dashboardUid, panelId, limitAlerts }) => {
|
||||
const queryParams: Record<string, string | undefined> = {
|
||||
rule_group: groupName,
|
||||
rule_name: ruleName,
|
||||
@ -175,12 +183,16 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
||||
|
||||
if (namespace) {
|
||||
if (isGrafanaRulesSource(ruleSourceName)) {
|
||||
set(queryParams, 'folder_uid', namespace);
|
||||
set(queryParams, PrometheusAPIFilters.FolderUID, namespace);
|
||||
} else {
|
||||
set(queryParams, 'file', namespace);
|
||||
set(queryParams, PrometheusAPIFilters.Namespace, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
if (limitAlerts !== undefined) {
|
||||
set(queryParams, PrometheusAPIFilters.LimitAlerts, String(limitAlerts));
|
||||
}
|
||||
|
||||
return {
|
||||
url: `api/prometheus/${getDatasourceAPIUid(ruleSourceName)}/api/v1/rules`,
|
||||
params: queryParams,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { isEmpty, truncate } from 'lodash';
|
||||
import { chain, isEmpty, truncate } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { NavModelItem, UrlQueryValue } from '@grafana/data';
|
||||
@ -8,7 +8,7 @@ import { PageInfoItem } from 'app/core/components/Page/types';
|
||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||
import InfoPausedRule from 'app/features/alerting/unified/components/InfoPausedRule';
|
||||
import { RuleActionsButtons } from 'app/features/alerting/unified/components/rules/RuleActionsButtons';
|
||||
import { CombinedRule, RuleHealth, RuleIdentifier } from 'app/types/unified-alerting';
|
||||
import { AlertInstanceTotalState, CombinedRule, RuleHealth, RuleIdentifier } from 'app/types/unified-alerting';
|
||||
import { PromAlertingRuleState, PromRuleType } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { defaultPageNav } from '../../RuleViewer';
|
||||
@ -42,7 +42,7 @@ import { InstancesList } from './tabs/Instances';
|
||||
import { QueryResults } from './tabs/Query';
|
||||
import { Routing } from './tabs/Routing';
|
||||
|
||||
enum ActiveTab {
|
||||
export enum ActiveTab {
|
||||
Query = 'query',
|
||||
Instances = 'instances',
|
||||
History = 'history',
|
||||
@ -251,7 +251,7 @@ export const Title = ({ name, paused = false, state, health, ruleType, ruleOrigi
|
||||
|
||||
export const isErrorHealth = (health?: RuleHealth) => health === 'error' || health === 'err';
|
||||
|
||||
function useActiveTab(): [ActiveTab, (tab: ActiveTab) => void] {
|
||||
export function useActiveTab(): [ActiveTab, (tab: ActiveTab) => void] {
|
||||
const [queryParams, setQueryParams] = useQueryParams();
|
||||
const tabFromQuery = queryParams['tab'];
|
||||
|
||||
@ -277,7 +277,7 @@ function usePageNav(rule: CombinedRule) {
|
||||
|
||||
const summary = annotations[Annotation.summary];
|
||||
const isAlertType = isAlertingRule(promRule);
|
||||
const numberOfInstance = isAlertType ? (promRule.alerts ?? []).length : undefined;
|
||||
const numberOfInstance = isAlertType ? calculateTotalInstances(rule.instanceTotals) : undefined;
|
||||
|
||||
const namespaceName = decodeGrafanaNamespace(rule.namespace).name;
|
||||
const groupName = rule.group.name;
|
||||
@ -343,6 +343,14 @@ function usePageNav(rule: CombinedRule) {
|
||||
};
|
||||
}
|
||||
|
||||
const calculateTotalInstances = (stats: CombinedRule['instanceTotals']) => {
|
||||
return chain(stats)
|
||||
.pick([AlertInstanceTotalState.Alerting, AlertInstanceTotalState.Pending, AlertInstanceTotalState.Normal])
|
||||
.values()
|
||||
.sum()
|
||||
.value();
|
||||
};
|
||||
|
||||
const getStyles = () => ({
|
||||
title: css({
|
||||
display: 'flex',
|
||||
|
@ -171,10 +171,15 @@ interface RequestState<T> {
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
ruleIdentifier: RuleIdentifier;
|
||||
limitAlerts?: number;
|
||||
}
|
||||
|
||||
// Many places still use the old way of fetching code so synchronizing cache expiration is difficult
|
||||
// Hence, this hook fetches a fresh version of a rule most of the time
|
||||
// Due to enabled filtering for Prometheus and Ruler rules it shouldn't be a problem
|
||||
export function useCombinedRule({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }): RequestState<CombinedRule> {
|
||||
export function useCombinedRule({ ruleIdentifier, limitAlerts }: Props): RequestState<CombinedRule> {
|
||||
const { ruleSourceName } = ruleIdentifier;
|
||||
const ruleSource = getRulesSourceFromIdentifier(ruleIdentifier);
|
||||
|
||||
@ -195,6 +200,7 @@ export function useCombinedRule({ ruleIdentifier }: { ruleIdentifier: RuleIdenti
|
||||
namespace: ruleLocation?.namespace,
|
||||
groupName: ruleLocation?.group,
|
||||
ruleName: ruleLocation?.ruleName,
|
||||
limitAlerts,
|
||||
},
|
||||
{
|
||||
skip: !ruleLocation || isLoadingRuleLocation,
|
||||
|
Loading…
Reference in New Issue
Block a user