mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[AO] Add alert time range annotation for metric threshold (#154440)
Resolves #153851 ## Summary This PR adds alert time range annotation for the metric threshold rule details page.  **Note** I changed the rule criteria to stop the alert, hence the weird graph! ## 🧪 How to test 1. Add `xpack.observability.unsafe.alertDetails.metrics.enabled: true` to the Kibana config 2. Generate a metric threshold alert 3. Go to the related alert details page and check the annotation
This commit is contained in:
parent
625966da23
commit
7dba0145ac
8 changed files with 86 additions and 17 deletions
|
@ -6,4 +6,5 @@
|
|||
*/
|
||||
|
||||
export { AlertAnnotation } from './src/components/alert_annotation';
|
||||
export { getAlertTimeRange } from './src/helpers/get_alert_time_range';
|
||||
export { AlertActiveTimeRangeAnnotation } from './src/components/alert_active_time_range_annotation';
|
||||
export { getPaddedAlertTimeRange } from './src/helpers/get_padded_alert_time_range';
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { RectAnnotation } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
alertStart: number;
|
||||
alertEnd?: number;
|
||||
color: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const RECT_ANNOTATION_TITLE = i18n.translate(
|
||||
'observabilityAlertDetails.alertActiveTimeRangeAnnotation.detailsTooltip',
|
||||
{
|
||||
defaultMessage: 'Active',
|
||||
}
|
||||
);
|
||||
|
||||
export function AlertActiveTimeRangeAnnotation({ alertStart, alertEnd, color, id }: Props) {
|
||||
return (
|
||||
<RectAnnotation
|
||||
id={id}
|
||||
dataValues={[
|
||||
{
|
||||
coordinates: {
|
||||
y0: 0,
|
||||
x0: alertStart,
|
||||
x1: alertEnd,
|
||||
},
|
||||
details: RECT_ANNOTATION_TITLE,
|
||||
},
|
||||
]}
|
||||
style={{ fill: color, opacity: 0.1 }}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -18,9 +18,12 @@ interface Props {
|
|||
id: string;
|
||||
}
|
||||
|
||||
const ANNOTATION_TITLE = i18n.translate('observabilityAlertDetails.alertAnnotation.title', {
|
||||
defaultMessage: 'Alert started',
|
||||
});
|
||||
const ANNOTATION_TITLE = i18n.translate(
|
||||
'observabilityAlertDetails.alertAnnotation.detailsTooltip',
|
||||
{
|
||||
defaultMessage: 'Alert started',
|
||||
}
|
||||
);
|
||||
|
||||
export function AlertAnnotation({ alertStart, color, dateFormat, id }: Props) {
|
||||
return (
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getAlertTimeRange } from './get_alert_time_range';
|
||||
import { getPaddedAlertTimeRange } from './get_padded_alert_time_range';
|
||||
|
||||
describe('getAlertTimeRange', () => {
|
||||
describe('getPaddedAlertTimeRange', () => {
|
||||
const mockedDate = '2023-03-28T09:22:32.660Z';
|
||||
const mockDate = jest
|
||||
.spyOn(global.Date, 'now')
|
||||
|
@ -31,7 +31,7 @@ describe('getAlertTimeRange', () => {
|
|||
];
|
||||
|
||||
it.each(testData)('%s', (_, start, end, output) => {
|
||||
expect(getAlertTimeRange(start, end)).toEqual(output);
|
||||
expect(getPaddedAlertTimeRange(start, end)).toEqual(output);
|
||||
});
|
||||
|
||||
describe('active alert', () => {
|
||||
|
@ -43,7 +43,7 @@ describe('getAlertTimeRange', () => {
|
|||
from: '2023-03-28T03:45:02.660Z',
|
||||
to: mockedDate,
|
||||
};
|
||||
expect(getAlertTimeRange(start)).toEqual(output);
|
||||
expect(getPaddedAlertTimeRange(start)).toEqual(output);
|
||||
});
|
||||
|
||||
it('with end time than 10 minutes before now', () => {
|
||||
|
@ -55,7 +55,7 @@ describe('getAlertTimeRange', () => {
|
|||
from: '2023-03-28T04:47:32.660Z',
|
||||
to: mockedDate,
|
||||
};
|
||||
expect(getAlertTimeRange(start, end)).toEqual(output);
|
||||
expect(getPaddedAlertTimeRange(start, end)).toEqual(output);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,7 +13,7 @@ export interface TimeRange {
|
|||
interval?: string;
|
||||
}
|
||||
|
||||
export const getAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => {
|
||||
export const getPaddedAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => {
|
||||
const alertDuration = moment.duration(moment(alertEnd).diff(moment(alertStart)));
|
||||
const now = moment().toISOString();
|
||||
const durationMs =
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AlertDetailsAppSection should render annotation 1`] = `
|
||||
exports[`AlertDetailsAppSection should render annotations 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"annotations": Array [
|
||||
|
@ -8,7 +8,12 @@ Array [
|
|||
alertStart={1678716383695}
|
||||
color="#BD271E"
|
||||
dateFormat="YYYY-MM-DD HH:mm"
|
||||
id="annotation_alert_start"
|
||||
id="alert_start_annotation"
|
||||
/>,
|
||||
<AlertActiveTimeRangeAnnotation
|
||||
alertStart={1678716383695}
|
||||
color="#BD271E"
|
||||
id="alert_time_range_annotation"
|
||||
/>,
|
||||
],
|
||||
"chartType": "line",
|
||||
|
|
|
@ -19,7 +19,11 @@ import { ExpressionChart } from './expression_chart';
|
|||
|
||||
jest.mock('@kbn/observability-alert-details', () => ({
|
||||
AlertAnnotation: () => {},
|
||||
getAlertTimeRange: () => ({ from: '2023-03-28T10:43:13.802Z', to: '2023-03-29T13:14:09.581Z' }),
|
||||
AlertActiveTimeRangeAnnotation: () => {},
|
||||
getPaddedAlertTimeRange: () => ({
|
||||
from: '2023-03-28T10:43:13.802Z',
|
||||
to: '2023-03-29T13:14:09.581Z',
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('./expression_chart', () => ({
|
||||
|
@ -61,7 +65,7 @@ describe('AlertDetailsAppSection', () => {
|
|||
expect((await result.findByTestId('metricThresholdAppSection')).children.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should render annotation', async () => {
|
||||
it('should render annotations', async () => {
|
||||
const mockedExpressionChart = jest.fn(() => <div data-test-subj="ExpressionChart" />);
|
||||
(ExpressionChart as jest.Mock).mockImplementation(mockedExpressionChart);
|
||||
renderComponent();
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import moment from 'moment';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel, useEuiTheme } from '@elastic/eui';
|
||||
import { TopAlert } from '@kbn/observability-plugin/public';
|
||||
import { ALERT_END, ALERT_START } from '@kbn/rule-data-utils';
|
||||
import { Rule } from '@kbn/alerting-plugin/common';
|
||||
import { AlertAnnotation, getAlertTimeRange } from '@kbn/observability-alert-details';
|
||||
import {
|
||||
AlertAnnotation,
|
||||
getPaddedAlertTimeRange,
|
||||
AlertActiveTimeRangeAnnotation,
|
||||
} from '@kbn/observability-alert-details';
|
||||
import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source';
|
||||
import { generateUniqueKey } from '../lib/generate_unique_key';
|
||||
import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
|
||||
|
@ -28,7 +33,8 @@ export type MetricThresholdRule = Rule<
|
|||
export type MetricThresholdAlert = TopAlert;
|
||||
|
||||
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm';
|
||||
const ALERT_START_ANNOTATION_ID = 'annotation_alert_start';
|
||||
const ALERT_START_ANNOTATION_ID = 'alert_start_annotation';
|
||||
const ALERT_TIME_RANGE_ANNOTATION_ID = 'alert_time_range_annotation';
|
||||
|
||||
interface AppSectionProps {
|
||||
rule: MetricThresholdRule;
|
||||
|
@ -44,7 +50,8 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
|
|||
() => createDerivedIndexPattern(),
|
||||
[createDerivedIndexPattern]
|
||||
);
|
||||
const timeRange = getAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]);
|
||||
const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]);
|
||||
const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined;
|
||||
const annotations = [
|
||||
<AlertAnnotation
|
||||
key={ALERT_START_ANNOTATION_ID}
|
||||
|
@ -53,6 +60,12 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
|
|||
dateFormat={uiSettings.get('dateFormat') || DEFAULT_DATE_FORMAT}
|
||||
id={ALERT_START_ANNOTATION_ID}
|
||||
/>,
|
||||
<AlertActiveTimeRangeAnnotation
|
||||
alertStart={alert.start}
|
||||
alertEnd={alertEnd}
|
||||
color={euiTheme.colors.danger}
|
||||
id={ALERT_TIME_RANGE_ANNOTATION_ID}
|
||||
/>,
|
||||
];
|
||||
|
||||
return !!rule.params.criteria ? (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue