diff --git a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx index d2c5af0e37a..72de14dc6ba 100644 --- a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx +++ b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'; import { keyBy, startCase, uniqueId } from 'lodash'; import * as React from 'react'; -import { DataSourceInstanceSettings, GrafanaTheme2, PanelData, rangeUtil, urlUtil } from '@grafana/data'; +import { DataSourceInstanceSettings, GrafanaTheme2, PanelData, urlUtil } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { DataSourceRef } from '@grafana/schema'; @@ -25,6 +25,7 @@ import { import alertDef, { EvalFunction } from '../state/alertDef'; import { Spacer } from './components/Spacer'; +import { TimeRangeLabel } from './components/TimeRangeLabel'; import { WithReturnButton } from './components/WithReturnButton'; import { ExpressionResult } from './components/expressions/Expression'; import { ThresholdDefinition, getThresholdsForQueries } from './components/rule-editor/util'; @@ -123,12 +124,7 @@ export function QueryPreview({ if (relativeTimeRange) { headerItems.push( - - {'{{from}}'} to now - + ); } diff --git a/public/app/features/alerting/unified/components/TimeRangeLabel.test.tsx b/public/app/features/alerting/unified/components/TimeRangeLabel.test.tsx new file mode 100644 index 00000000000..30babeaf45a --- /dev/null +++ b/public/app/features/alerting/unified/components/TimeRangeLabel.test.tsx @@ -0,0 +1,21 @@ +import { render, screen } from 'test/test-utils'; + +import { TimeRangeLabel } from './TimeRangeLabel'; + +describe('TimeRangeLabel', () => { + it('renders "to now" when to is 0', () => { + render(); + + // 900 seconds -> 15m + expect(screen.getByText(/to now/i)).toBeInTheDocument(); + expect(screen.getByText('15m')).toBeInTheDocument(); + }); + + it('renders "to " when to > 0', () => { + render(); + + // 900 seconds -> 15m, 60 seconds -> 1m + const container = screen.getByText(/to/i).closest('span') || screen.getByText(/to/i).parentElement || document.body; + expect(container).toHaveTextContent(/15m to 1m/); + }); +}); diff --git a/public/app/features/alerting/unified/components/TimeRangeLabel.tsx b/public/app/features/alerting/unified/components/TimeRangeLabel.tsx new file mode 100644 index 00000000000..472aa71d119 --- /dev/null +++ b/public/app/features/alerting/unified/components/TimeRangeLabel.tsx @@ -0,0 +1,32 @@ +import { RelativeTimeRange, rangeUtil } from '@grafana/data'; +import { Trans } from '@grafana/i18n'; + +interface RuleTimeRangeLabelProps { + relativeTimeRange: RelativeTimeRange; +} + +/** + * Displays a human-readable relative time range label like: + * - "15m to now" when to === 0 or not set + * - "15m to 1m" when to > 0 + */ +export function TimeRangeLabel({ relativeTimeRange }: RuleTimeRangeLabelProps) { + const fromLabel = rangeUtil.secondsToHms(relativeTimeRange.from); + const toSeconds = relativeTimeRange.to ?? 0; + const toIsNow = !toSeconds || toSeconds <= 0; + const toLabel = toIsNow ? 'now' : rangeUtil.secondsToHms(toSeconds); + + if (toIsNow) { + return ( + + {'{{from}}'} to now + + ); + } + + return ( + + {'{{from}}'} to {'{{to}}'} + + ); +} diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryOptions.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryOptions.tsx index 1b3afdb9c52..c9d319ae215 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryOptions.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryOptions.tsx @@ -1,11 +1,13 @@ import { css } from '@emotion/css'; import { useState } from 'react'; -import { GrafanaTheme2, RelativeTimeRange, dateTime, getDefaultRelativeTimeRange, rangeUtil } from '@grafana/data'; +import { GrafanaTheme2, RelativeTimeRange, getDefaultRelativeTimeRange } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { Icon, InlineField, RelativeTimeRangePicker, Toggletip, clearButtonStyles, useStyles2 } from '@grafana/ui'; import { AlertQuery } from 'app/types/unified-alerting-dto'; +import { TimeRangeLabel } from '../TimeRangeLabel'; + import { AlertQueryOptions, MaxDataPointsOption, MinIntervalOption } from './QueryWrapper'; export interface QueryOptionsProps { @@ -27,8 +29,6 @@ export const QueryOptions = ({ const [showOptions, setShowOptions] = useState(false); - const timeRange = query.relativeTimeRange ? rangeUtil.relativeToTimeRange(query.relativeTimeRange) : undefined; - const separator = , ; return ( @@ -58,7 +58,9 @@ export const QueryOptions = ({
- {dateTime(timeRange?.from).locale('en').fromNow(true)} + + + {queryOptions.maxDataPoints && ( <> diff --git a/public/app/features/alerting/unified/utils/query.ts b/public/app/features/alerting/unified/utils/query.ts index daa30207e94..3567429653d 100644 --- a/public/app/features/alerting/unified/utils/query.ts +++ b/public/app/features/alerting/unified/utils/query.ts @@ -1,5 +1,3 @@ -import { produce } from 'immer'; - import { DataSourceInstanceSettings } from '@grafana/data'; import { PromQuery } from '@grafana/prometheus'; import { DataQuery } from '@grafana/schema'; @@ -9,7 +7,6 @@ import { AlertQuery } from 'app/types/unified-alerting-dto'; import { isCloudRulesSource, isSupportedExternalRulesSourceType } from './datasource'; import { rulerRuleType } from './rules'; -import { safeParsePrometheusDuration } from './time'; export function alertRuleToQueries(combinedRule: CombinedRule | undefined | null): AlertQuery[] { if (!combinedRule) { @@ -19,8 +16,7 @@ export function alertRuleToQueries(combinedRule: CombinedRule | undefined | null const { rulesSource } = namespace; if (rulerRuleType.grafana.rule(rulerRule)) { - const query = rulerRule.grafana_alert.data; - return widenRelativeTimeRanges(query, rulerRule.for ?? '', combinedRule.group.interval); + return rulerRule.grafana_alert.data; } if (isCloudRulesSource(rulesSource)) { @@ -32,37 +28,6 @@ export function alertRuleToQueries(combinedRule: CombinedRule | undefined | null return []; } -/** - * This function will figure out how large the time range for visualizing the alert rule detail view should be - * We try to show as much data as is relevant for triaging / root cause analysis - * - * The function for it is; - * - * Math.max(3 * pending period, query range + (2 * pending period)) - * - * We can safely ignore the evaluation interval because the pending period is guaranteed to be largen than or equal that - */ -export function widenRelativeTimeRanges(queries: AlertQuery[], pendingPeriod: string, groupInterval?: string) { - // if pending period is zero that means inherit from group interval, if that is empty then assume 1m - const pendingPeriodDurationMillis = - safeParsePrometheusDuration(pendingPeriod) ?? safeParsePrometheusDuration(groupInterval ?? '1m'); - const pendingPeriodDuration = Math.floor(pendingPeriodDurationMillis / 1000); - - return queries.map((query) => - produce(query, (draft) => { - const fromQueryRange = draft.relativeTimeRange?.from ?? 0; - - // use whichever has the largest time range - const from = Math.max(pendingPeriodDuration * 3, fromQueryRange + pendingPeriodDuration * 2); - - draft.relativeTimeRange = { - from, - to: 0, - }; - }) - ); -} - export function dataQueryToAlertQuery(dataQuery: DataQuery, dataSourceUid: string): AlertQuery { return { refId: dataQuery.refId, diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 4a2217515fd..e974a23708a 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2213,9 +2213,6 @@ "max-data-points": "MD = {{maxDataPoints}}", "min-interval": "Min. Interval = {{minInterval}}" }, - "query-preview": { - "relative-time-range": "<0>{{from}} to now" - }, "queryAndExpressionsStep": { "disableAdvancedOptions": { "text": "The selected queries and expressions cannot be converted to default. If you deactivate advanced options, your query and condition will be reset to default settings." @@ -2561,6 +2558,10 @@ "recording": "{{recordingStats}} recording", "recovering": "{{recoveringStats}} recovering" }, + "rule-time-range-label": { + "relative": "<0>{{from}} to now", + "relative-with-to": "<0>{{from}} to <2>{{to}}" + }, "rule-type-picker": { "grafana-managed": "Select “Grafana managed” unless you have a Mimir, Loki or Cortex data source with the Ruler API enabled." },