mirror of
https://github.com/grafana/grafana.git
synced 2024-11-23 09:26:43 -06:00
Alerting: Add min interval option to alert rule query creation (#71986)
This features adds a configuration option when creating an alert rule query. This option already exists as part of the alert query model but is not currently configurable through the UI.
This commit is contained in:
parent
5c1e8c108a
commit
8ffd2a71d3
@ -6,7 +6,7 @@ import { relativeToTimeRange } from '@grafana/data/src/datetime/rangeutil';
|
||||
import { clearButtonStyles, Icon, RelativeTimeRangePicker, Toggletip, useStyles2 } from '@grafana/ui';
|
||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { AlertQueryOptions, MaxDataPointsOption } from './QueryWrapper';
|
||||
import { AlertQueryOptions, MaxDataPointsOption, MinIntervalOption } from './QueryWrapper';
|
||||
|
||||
export interface QueryOptionsProps {
|
||||
query: AlertQuery;
|
||||
@ -50,6 +50,7 @@ export const QueryOptions = ({
|
||||
options={queryOptions}
|
||||
onChange={(options) => onChangeQueryOptions(options, index)}
|
||||
/>
|
||||
<MinIntervalOption options={queryOptions} onChange={(options) => onChangeQueryOptions(options, index)} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -67,7 +68,8 @@ export const QueryOptions = ({
|
||||
.locale('en')
|
||||
.fromNow(true)}
|
||||
</span>
|
||||
{queryOptions.maxDataPoints && <span>, MD {queryOptions.maxDataPoints}</span>}
|
||||
{queryOptions.maxDataPoints && <span>, MD = {queryOptions.maxDataPoints}</span>}
|
||||
{queryOptions.minInterval && <span>, Min. Interval = {queryOptions.minInterval}</span>}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -2,7 +2,14 @@ import { omit } from 'lodash';
|
||||
import React, { PureComponent, useState } from 'react';
|
||||
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
|
||||
|
||||
import { DataQuery, DataSourceInstanceSettings, LoadingState, PanelData, RelativeTimeRange } from '@grafana/data';
|
||||
import {
|
||||
DataQuery,
|
||||
DataSourceInstanceSettings,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
rangeUtil,
|
||||
RelativeTimeRange,
|
||||
} from '@grafana/data';
|
||||
import { Stack } from '@grafana/experimental';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { Button, Card, Icon } from '@grafana/ui';
|
||||
@ -61,7 +68,11 @@ export class QueryRows extends PureComponent<Props> {
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
model: { ...item.model, maxDataPoints: options.maxDataPoints },
|
||||
model: {
|
||||
...item.model,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
intervalMs: options.minInterval ? rangeUtil.intervalToMs(options.minInterval) : undefined,
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
|
@ -18,15 +18,18 @@ import { GraphTresholdsStyleMode, Icon, InlineFormLabel, Input, Tooltip, useStyl
|
||||
import { QueryEditorRow } from 'app/features/query/components/QueryEditorRow';
|
||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { msToSingleUnitDuration } from '../../utils/time';
|
||||
import { AlertConditionIndicator } from '../expressions/AlertConditionIndicator';
|
||||
|
||||
import { QueryOptions } from './QueryOptions';
|
||||
import { VizWrapper } from './VizWrapper';
|
||||
|
||||
export const DEFAULT_MAX_DATA_POINTS = 43200;
|
||||
export const DEFAULT_MIN_INTERVAL = '1s';
|
||||
|
||||
export interface AlertQueryOptions {
|
||||
maxDataPoints?: number | undefined;
|
||||
minInterval?: string | undefined;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@ -107,9 +110,13 @@ export const QueryWrapper = ({
|
||||
|
||||
// TODO add a warning label here too when the data looks like time series data and is used as an alert condition
|
||||
function HeaderExtras({ query, error, index }: { query: AlertQuery; error?: Error; index: number }) {
|
||||
const queryOptions: AlertQueryOptions = { maxDataPoints: query.model.maxDataPoints };
|
||||
const queryOptions: AlertQueryOptions = {
|
||||
maxDataPoints: query.model.maxDataPoints,
|
||||
minInterval: query.model.intervalMs ? msToSingleUnitDuration(query.model.intervalMs) : undefined,
|
||||
};
|
||||
const alertQueryOptions: AlertQueryOptions = {
|
||||
maxDataPoints: queryOptions.maxDataPoints,
|
||||
minInterval: queryOptions.minInterval,
|
||||
};
|
||||
|
||||
return (
|
||||
@ -222,6 +229,50 @@ export function MaxDataPointsOption({
|
||||
);
|
||||
}
|
||||
|
||||
export function MinIntervalOption({
|
||||
options,
|
||||
onChange,
|
||||
}: {
|
||||
options: AlertQueryOptions;
|
||||
onChange: (options: AlertQueryOptions) => void;
|
||||
}) {
|
||||
const value = options.minInterval ?? '';
|
||||
|
||||
const onMinIntervalBlur = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const minInterval = event.target.value;
|
||||
if (minInterval !== value) {
|
||||
onChange({
|
||||
...options,
|
||||
minInterval,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="row" alignItems="baseline" gap={1}>
|
||||
<InlineFormLabel
|
||||
width={8}
|
||||
tooltip={
|
||||
<>
|
||||
A lower limit for the interval. Recommended to be set to write frequency, for example <code>1m</code> if
|
||||
your data is written every minute.
|
||||
</>
|
||||
}
|
||||
>
|
||||
Min interval
|
||||
</InlineFormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
className="width-6"
|
||||
placeholder={DEFAULT_MIN_INTERVAL}
|
||||
spellCheck={false}
|
||||
onBlur={onMinIntervalBlur}
|
||||
defaultValue={value}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
wrapper: css`
|
||||
label: AlertingQueryWrapper;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createAction, createReducer } from '@reduxjs/toolkit';
|
||||
|
||||
import { DataQuery, getDefaultRelativeTimeRange, RelativeTimeRange } from '@grafana/data';
|
||||
import { DataQuery, getDefaultRelativeTimeRange, rangeUtil, RelativeTimeRange } from '@grafana/data';
|
||||
import { getNextRefIdChar } from 'app/core/utils/query';
|
||||
import { findDataSourceFromExpressionRecursive } from 'app/features/alerting/utils/dataSourceFromExpression';
|
||||
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
||||
@ -43,6 +43,7 @@ export const rewireExpressions = createAction<{ oldRefId: string; newRefId: stri
|
||||
export const updateExpressionType = createAction<{ refId: string; type: ExpressionQueryType }>('updateExpressionType');
|
||||
export const updateExpressionTimeRange = createAction('updateExpressionTimeRange');
|
||||
export const updateMaxDataPoints = createAction<{ refId: string; maxDataPoints: number }>('updateMaxDataPoints');
|
||||
export const updateMinInterval = createAction<{ refId: string; minInterval: string }>('updateMinInterval');
|
||||
|
||||
export const setRecordingRulesQueries = createAction<{ recordingRuleQueries: AlertQuery[]; expression: string }>(
|
||||
'setRecordingRulesQueries'
|
||||
@ -96,6 +97,19 @@ export const queriesAndExpressionsReducer = createReducer(initialState, (builder
|
||||
}
|
||||
: query;
|
||||
});
|
||||
})
|
||||
.addCase(updateMinInterval, (state, action) => {
|
||||
state.queries = state.queries.map((query) => {
|
||||
return query.refId === action.payload.refId
|
||||
? {
|
||||
...query,
|
||||
model: {
|
||||
...query.model,
|
||||
intervalMs: action.payload.minInterval ? rangeUtil.intervalToMs(action.payload.minInterval) : undefined,
|
||||
},
|
||||
}
|
||||
: query;
|
||||
});
|
||||
});
|
||||
|
||||
// expressions actions
|
||||
|
@ -106,3 +106,17 @@ export const safeParseDurationstr = (duration: string): number => {
|
||||
export const isNullDate = (date: string) => {
|
||||
return date.includes('0001-01-01T00');
|
||||
};
|
||||
|
||||
// Format given time span in MS to the largest single unit duration string up to hours.
|
||||
export function msToSingleUnitDuration(rangeMs: number): string {
|
||||
if (rangeMs % (1000 * 60 * 60) === 0) {
|
||||
return rangeMs / (1000 * 60 * 60) + 'h';
|
||||
}
|
||||
if (rangeMs % (1000 * 60) === 0) {
|
||||
return rangeMs / (1000 * 60) + 'm';
|
||||
}
|
||||
if (rangeMs % 1000 === 0) {
|
||||
return rangeMs / 1000 + 's';
|
||||
}
|
||||
return rangeMs.toFixed() + 'ms';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user