mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Display last & next rule eval date plus eval duration (#64767)
* Display last & next rule eval date plus eval duration * Show next evaluation date in a humanized format Full date still visible on hover * Only show next evaluation column is group has an interval
This commit is contained in:
parent
95aa9b374a
commit
6b95b3f8aa
@ -1,12 +1,14 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaTheme2, dateTime, dateTimeFormat } from '@grafana/data';
|
||||
import { useStyles2, Tooltip } from '@grafana/ui';
|
||||
import { Time } from 'app/features/explore/Time';
|
||||
import { CombinedRule } from 'app/types/unified-alerting';
|
||||
|
||||
import { useCleanAnnotations } from '../../utils/annotations';
|
||||
import { isRecordingRulerRule } from '../../utils/rules';
|
||||
import { isNullDate } from '../../utils/time';
|
||||
import { AlertLabels } from '../AlertLabels';
|
||||
import { DetailsField } from '../DetailsField';
|
||||
|
||||
@ -63,6 +65,8 @@ interface EvaluationBehaviorSummaryProps {
|
||||
const EvaluationBehaviorSummary = ({ rule }: EvaluationBehaviorSummaryProps) => {
|
||||
let forDuration: string | undefined;
|
||||
let every = rule.group.interval;
|
||||
let lastEvaluation = rule.promRule?.lastEvaluation;
|
||||
let lastEvaluationDuration = rule.promRule?.evaluationTime;
|
||||
|
||||
// recording rules don't have a for duration
|
||||
if (!isRecordingRulerRule(rule.rulerRule)) {
|
||||
@ -81,6 +85,26 @@ const EvaluationBehaviorSummary = ({ rule }: EvaluationBehaviorSummaryProps) =>
|
||||
{forDuration}
|
||||
</DetailsField>
|
||||
)}
|
||||
|
||||
{lastEvaluation && !isNullDate(lastEvaluation) && (
|
||||
<DetailsField label="Last evaluation" horizontal={true}>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
content={`${dateTimeFormat(lastEvaluation, { format: 'YYYY-MM-DD HH:mm:ss' })}`}
|
||||
theme="info"
|
||||
>
|
||||
<span>{`${dateTime(lastEvaluation).locale('en').fromNow(true)} ago`}</span>
|
||||
</Tooltip>
|
||||
</DetailsField>
|
||||
)}
|
||||
|
||||
{lastEvaluation && !isNullDate(lastEvaluation) && lastEvaluationDuration !== undefined && (
|
||||
<DetailsField label="Evaluation time" horizontal={true}>
|
||||
<Tooltip placement="top" content={`${lastEvaluationDuration}s`} theme="info">
|
||||
<span>{Time({ timeInMs: lastEvaluationDuration * 1000, humanize: true })}</span>
|
||||
</Tooltip>
|
||||
</DetailsField>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -234,7 +234,13 @@ export const RulesGroup = React.memo(({ group, namespace, expandAll, viewMode }:
|
||||
)}
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<RulesTable showSummaryColumn={true} className={styles.rulesTable} showGuidelines={true} rules={group.rules} />
|
||||
<RulesTable
|
||||
showSummaryColumn={true}
|
||||
className={styles.rulesTable}
|
||||
showGuidelines={true}
|
||||
showNextEvaluationColumn={Boolean(group.interval)}
|
||||
rules={group.rules}
|
||||
/>
|
||||
)}
|
||||
{isEditingGroup && (
|
||||
<EditCloudGroupModal
|
||||
|
@ -1,14 +1,23 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import {
|
||||
GrafanaTheme2,
|
||||
addDurationToDate,
|
||||
isValidDate,
|
||||
isValidDuration,
|
||||
parseDuration,
|
||||
dateTimeFormat,
|
||||
dateTime,
|
||||
} from '@grafana/data';
|
||||
import { useStyles2, Tooltip } from '@grafana/ui';
|
||||
import { CombinedRule } from 'app/types/unified-alerting';
|
||||
|
||||
import { DEFAULT_PER_PAGE_PAGINATION } from '../../../../../core/constants';
|
||||
import { useHasRuler } from '../../hooks/useHasRuler';
|
||||
import { Annotation } from '../../utils/constants';
|
||||
import { isGrafanaRulerRule, isGrafanaRulerRulePaused } from '../../utils/rules';
|
||||
import { isNullDate } from '../../utils/time';
|
||||
import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable';
|
||||
import { DynamicTableWithGuidelines } from '../DynamicTableWithGuidelines';
|
||||
import { ProvisioningBadge } from '../Provisioning';
|
||||
@ -29,6 +38,7 @@ interface Props {
|
||||
showGuidelines?: boolean;
|
||||
showGroupColumn?: boolean;
|
||||
showSummaryColumn?: boolean;
|
||||
showNextEvaluationColumn?: boolean;
|
||||
emptyMessage?: string;
|
||||
className?: string;
|
||||
}
|
||||
@ -40,6 +50,7 @@ export const RulesTable = ({
|
||||
emptyMessage = 'No rules found.',
|
||||
showGroupColumn = false,
|
||||
showSummaryColumn = false,
|
||||
showNextEvaluationColumn = false,
|
||||
}: Props) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
@ -54,7 +65,7 @@ export const RulesTable = ({
|
||||
});
|
||||
}, [rules]);
|
||||
|
||||
const columns = useColumns(showSummaryColumn, showGroupColumn);
|
||||
const columns = useColumns(showSummaryColumn, showGroupColumn, showNextEvaluationColumn);
|
||||
|
||||
if (!rules.length) {
|
||||
return <div className={cx(wrapperClass, styles.emptyMessage)}>{emptyMessage}</div>;
|
||||
@ -101,9 +112,29 @@ export const getStyles = (theme: GrafanaTheme2) => ({
|
||||
`,
|
||||
});
|
||||
|
||||
function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
|
||||
function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean, showNextEvaluationColumn: boolean) {
|
||||
const { hasRuler, rulerRulesLoaded } = useHasRuler();
|
||||
|
||||
const calculateNextEvaluationDate = useCallback((rule: CombinedRule) => {
|
||||
const isValidLastEvaluation =
|
||||
rule.promRule?.lastEvaluation &&
|
||||
!isNullDate(rule.promRule.lastEvaluation) &&
|
||||
isValidDate(rule.promRule.lastEvaluation);
|
||||
const isValidIntervalDuration = rule.group.interval && isValidDuration(rule.group.interval);
|
||||
|
||||
if (!isValidLastEvaluation || !isValidIntervalDuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastEvaluationDate = Date.parse(rule.promRule?.lastEvaluation || '');
|
||||
const intervalDuration = parseDuration(rule.group.interval!);
|
||||
const nextEvaluationDate = addDurationToDate(lastEvaluationDate, intervalDuration);
|
||||
return {
|
||||
humanized: dateTime(nextEvaluationDate).locale('en').fromNow(true),
|
||||
fullDate: dateTimeFormat(nextEvaluationDate, { format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
};
|
||||
}, []);
|
||||
|
||||
return useMemo((): RuleTableColumnProps[] => {
|
||||
const columns: RuleTableColumnProps[] = [
|
||||
{
|
||||
@ -128,7 +159,7 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
|
||||
label: 'Name',
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderCell: ({ data: rule }) => rule.name,
|
||||
size: 5,
|
||||
size: showNextEvaluationColumn ? 4 : 5,
|
||||
},
|
||||
{
|
||||
id: 'provisioned',
|
||||
@ -169,9 +200,28 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
|
||||
renderCell: ({ data: rule }) => {
|
||||
return <Tokenize input={rule.annotations[Annotation.summary] ?? ''} />;
|
||||
},
|
||||
size: 5,
|
||||
size: showNextEvaluationColumn ? 4 : 5,
|
||||
});
|
||||
}
|
||||
|
||||
if (showNextEvaluationColumn) {
|
||||
columns.push({
|
||||
id: 'nextEvaluation',
|
||||
label: 'Next evaluation',
|
||||
renderCell: ({ data: rule }) => {
|
||||
const nextEvalInfo = calculateNextEvaluationDate(rule);
|
||||
return (
|
||||
nextEvalInfo?.fullDate && (
|
||||
<Tooltip placement="top" content={`${nextEvalInfo?.fullDate}`} theme="info">
|
||||
<span>in {nextEvalInfo?.humanized}</span>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
},
|
||||
size: 2,
|
||||
});
|
||||
}
|
||||
|
||||
if (showGroupColumn) {
|
||||
columns.push({
|
||||
id: 'group',
|
||||
@ -203,5 +253,12 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
|
||||
});
|
||||
|
||||
return columns;
|
||||
}, [showSummaryColumn, showGroupColumn, hasRuler, rulerRulesLoaded]);
|
||||
}, [
|
||||
showSummaryColumn,
|
||||
showGroupColumn,
|
||||
showNextEvaluationColumn,
|
||||
hasRuler,
|
||||
rulerRulesLoaded,
|
||||
calculateNextEvaluationDate,
|
||||
]);
|
||||
}
|
||||
|
@ -99,3 +99,7 @@ export function parsePrometheusDuration(duration: string): number {
|
||||
|
||||
return totalDuration;
|
||||
}
|
||||
|
||||
export const isNullDate = (date: string) => {
|
||||
return date.includes('0001-01-01T00');
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user