Skip to content

Commit

Permalink
[Monitoring] Add KQL filter bar to alerts (#111663)
Browse files Browse the repository at this point in the history
* [Monitoring] Add KQL filter bar to alerts

* Finish adding filters to UI

* Adding filterQuery to all the backend alert functions

* removing unused translations

* fixing types

* Moving alerting code to async imports

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
simianhacker and kibanamachine committed Sep 22, 2021
1 parent ef8518d commit a29fdf5
Show file tree
Hide file tree
Showing 79 changed files with 1,208 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

import { isFunction, get } from 'lodash';
import type { MonitoringConfig } from '../config';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import type { MonitoringConfig } from '../server/config';

type Config = Partial<MonitoringConfig> & {
get?: (key: string) => any;
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/monitoring/common/types/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ export interface CommonAlertParams {
duration: string;
threshold?: number;
limit?: string;
filterQuery?: string;
filterQueryText?: string;
[key: string]: unknown;
}

export interface ThreadPoolRejectionsAlertParams {
threshold: number;
duration: string;
filterQuery?: string;
filterQueryText?: string;
}

export interface AlertEnableAction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
RULE_REQUIRES_APP_CONTEXT,
} from '../../../common/constants';
import { AlertTypeParams } from '../../../../alerting/common';
import { MonitoringConfig } from '../../types';

interface ValidateOptions extends AlertTypeParams {
duration: string;
Expand All @@ -36,7 +37,9 @@ const validate = (inputValues: ValidateOptions): ValidationResult => {
return validationResult;
};

export function createCCRReadExceptionsAlertType(): AlertTypeModel<ValidateOptions> {
export function createCCRReadExceptionsAlertType(
config: MonitoringConfig
): AlertTypeModel<ValidateOptions> {
return {
id: RULE_CCR_READ_EXCEPTIONS,
description: RULE_DETAILS[RULE_CCR_READ_EXCEPTIONS].description,
Expand All @@ -45,7 +48,11 @@ export function createCCRReadExceptionsAlertType(): AlertTypeModel<ValidateOptio
return `${docLinks.links.monitoring.alertsKibanaCCRReadExceptions}`;
},
alertParamsExpression: (props: Props) => (
<Expression {...props} paramDetails={RULE_DETAILS[RULE_CCR_READ_EXCEPTIONS].paramDetails} />
<Expression
{...props}
config={config}
paramDetails={RULE_DETAILS[RULE_CCR_READ_EXCEPTIONS].paramDetails}
/>
),
validate,
defaultActionMessage: '{{context.internalFullMessage}}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,38 @@
* 2.0.
*/

import React, { Fragment } from 'react';
import { EuiForm, EuiSpacer } from '@elastic/eui';
import React, { Fragment, useCallback } from 'react';
import { EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { debounce } from 'lodash';
import { i18n } from '@kbn/i18n';
import { CommonAlertParamDetails } from '../../../../common/types/alerts';
import { AlertParamDuration } from '../../flyout_expressions/alert_param_duration';
import { AlertParamType } from '../../../../common/enums';
import { AlertParamPercentage } from '../../flyout_expressions/alert_param_percentage';
import { AlertParamNumber } from '../../flyout_expressions/alert_param_number';
import { AlertParamTextField } from '../../flyout_expressions/alert_param_textfield';
import { MonitoringConfig } from '../../../types';
import { useDerivedIndexPattern } from './use_derived_index_pattern';
import { KueryBar } from '../../../components/kuery_bar';
import { convertKueryToElasticSearchQuery } from '../../../lib/kuery';

const FILTER_TYPING_DEBOUNCE_MS = 500;

export interface Props {
alertParams: { [property: string]: any };
setAlertParams: (property: string, value: any) => void;
setAlertProperty: (property: string, value: any) => void;
errors: { [key: string]: string[] };
paramDetails: CommonAlertParamDetails;
data: DataPublicPluginStart;
config?: MonitoringConfig;
}

export const Expression: React.FC<Props> = (props) => {
const { alertParams, paramDetails, setAlertParams, errors } = props;
const { alertParams, paramDetails, setAlertParams, errors, config, data } = props;

const { derivedIndexPattern } = useDerivedIndexPattern(data, config);

const alertParamsUi = Object.keys(paramDetails).map((alertParamName) => {
const details = paramDetails[alertParamName];
Expand Down Expand Up @@ -77,10 +90,44 @@ export const Expression: React.FC<Props> = (props) => {
}
});

const onFilterChange = useCallback(
(filter: string) => {
setAlertParams('filterQueryText', filter);
setAlertParams(
'filterQuery',
convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || ''
);
},
[setAlertParams, derivedIndexPattern]
);

/* eslint-disable-next-line react-hooks/exhaustive-deps */
const debouncedOnFilterChange = useCallback(debounce(onFilterChange, FILTER_TYPING_DEBOUNCE_MS), [
onFilterChange,
]);

return (
<Fragment>
<EuiForm component="form">{alertParamsUi}</EuiForm>
<EuiSpacer />
<EuiForm component="form">
{alertParamsUi}
<EuiSpacer />
<EuiFormRow
label={i18n.translate('xpack.monitoring.alerts.filterLable', {
defaultMessage: 'Filter',
})}
helpText={i18n.translate('xpack.monitoring.alerts.filterHelpText', {
defaultMessage: 'Use a KQL expression to limit the scope of your alert trigger.',
})}
>
<KueryBar
value={alertParams.filterQueryText}
derivedIndexPattern={derivedIndexPattern}
onSubmit={onFilterChange}
onChange={debouncedOnFilterChange}
/>
</EuiFormRow>
<EuiSpacer />
</EuiForm>
</Fragment>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 { useEffect, useState } from 'react';
import { DataPublicPluginStart, IFieldType, IIndexPattern } from 'src/plugins/data/public';
import {
INDEX_PATTERN_BEATS,
INDEX_PATTERN_ELASTICSEARCH,
INDEX_PATTERN_KIBANA,
INDEX_PATTERN_LOGSTASH,
} from '../../../../common/constants';
import { prefixIndexPattern } from '../../../../common/ccs_utils';
import { MonitoringConfig } from '../../../types';

const INDEX_PATTERNS = `${INDEX_PATTERN_ELASTICSEARCH},${INDEX_PATTERN_KIBANA},${INDEX_PATTERN_LOGSTASH},${INDEX_PATTERN_BEATS}`;

export const useDerivedIndexPattern = (
data: DataPublicPluginStart,
config?: MonitoringConfig
): { loading: boolean; derivedIndexPattern: IIndexPattern } => {
const indexPattern = prefixIndexPattern(config || ({} as MonitoringConfig), INDEX_PATTERNS, '*');
const [loading, setLoading] = useState<boolean>(true);
const [fields, setFields] = useState<IFieldType[]>([]);
useEffect(() => {
(async function fetchData() {
const result = await data.indexPatterns.getFieldsForWildcard({
pattern: indexPattern,
});
setFields(result);
setLoading(false);
})();
}, [indexPattern, data.indexPatterns]);
return {
loading,
derivedIndexPattern: {
title: indexPattern,
fields,
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
import { RULE_CPU_USAGE, RULE_DETAILS, RULE_REQUIRES_APP_CONTEXT } from '../../../common/constants';
import { validate, MonitoringAlertTypeParams } from '../components/param_details_form/validation';
import { Expression, Props } from '../components/param_details_form/expression';
import { MonitoringConfig } from '../../types';

export function createCpuUsageAlertType(): AlertTypeModel<MonitoringAlertTypeParams> {
export function createCpuUsageAlertType(
config: MonitoringConfig
): AlertTypeModel<MonitoringAlertTypeParams> {
return {
id: RULE_CPU_USAGE,
description: RULE_DETAILS[RULE_CPU_USAGE].description,
Expand All @@ -21,7 +24,11 @@ export function createCpuUsageAlertType(): AlertTypeModel<MonitoringAlertTypePar
return `${docLinks.links.monitoring.alertsKibanaCpuThreshold}`;
},
alertParamsExpression: (props: Props) => (
<Expression {...props} paramDetails={RULE_DETAILS[RULE_CPU_USAGE].paramDetails} />
<Expression
{...props}
config={config}
paramDetails={RULE_DETAILS[RULE_CPU_USAGE].paramDetails}
/>
),
validate,
defaultActionMessage: '{{context.internalFullMessage}}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import {
RULE_DETAILS,
RULE_REQUIRES_APP_CONTEXT,
} from '../../../common/constants';
import { MonitoringConfig } from '../../types';

export function createDiskUsageAlertType(): AlertTypeModel<MonitoringAlertTypeParams> {
export function createDiskUsageAlertType(
config: MonitoringConfig
): AlertTypeModel<MonitoringAlertTypeParams> {
return {
id: RULE_DISK_USAGE,
description: RULE_DETAILS[RULE_DISK_USAGE].description,
Expand All @@ -26,7 +29,11 @@ export function createDiskUsageAlertType(): AlertTypeModel<MonitoringAlertTypePa
return `${docLinks.links.monitoring.alertsKibanaDiskThreshold}`;
},
alertParamsExpression: (props: Props) => (
<Expression {...props} paramDetails={RULE_DETAILS[RULE_DISK_USAGE].paramDetails} />
<Expression
{...props}
config={config}
paramDetails={RULE_DETAILS[RULE_DISK_USAGE].paramDetails}
/>
),
validate,
defaultActionMessage: '{{context.internalFullMessage}}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
RULE_REQUIRES_APP_CONTEXT,
} from '../../../common/constants';
import { AlertTypeParams } from '../../../../alerting/common';
import { MonitoringConfig } from '../../types';

interface ValidateOptions extends AlertTypeParams {
indexPattern: string;
Expand All @@ -36,7 +37,9 @@ const validate = (inputValues: ValidateOptions): ValidationResult => {
return validationResult;
};

export function createLargeShardSizeAlertType(): AlertTypeModel<ValidateOptions> {
export function createLargeShardSizeAlertType(
config: MonitoringConfig
): AlertTypeModel<ValidateOptions> {
return {
id: RULE_LARGE_SHARD_SIZE,
description: RULE_DETAILS[RULE_LARGE_SHARD_SIZE].description,
Expand All @@ -45,7 +48,11 @@ export function createLargeShardSizeAlertType(): AlertTypeModel<ValidateOptions>
return `${docLinks.links.monitoring.alertsKibanaLargeShardSize}`;
},
alertParamsExpression: (props: Props) => (
<Expression {...props} paramDetails={RULE_DETAILS[RULE_LARGE_SHARD_SIZE].paramDetails} />
<Expression
{...props}
config={config}
paramDetails={RULE_DETAILS[RULE_LARGE_SHARD_SIZE].paramDetails}
/>
),
validate,
defaultActionMessage: '{{context.internalFullMessage}}',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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, { useCallback } from 'react';
import { debounce } from 'lodash';
import { EuiSpacer, EuiForm, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useDerivedIndexPattern } from '../components/param_details_form/use_derived_index_pattern';
import { convertKueryToElasticSearchQuery } from '../../lib/kuery';
import { KueryBar } from '../../components/kuery_bar';
import { Props } from '../components/param_details_form/expression';

const FILTER_TYPING_DEBOUNCE_MS = 500;

export const Expression = ({ alertParams, config, setAlertParams, data }: Props) => {
const { derivedIndexPattern } = useDerivedIndexPattern(data, config);
const onFilterChange = useCallback(
(filter: string) => {
setAlertParams('filterQueryText', filter);
setAlertParams(
'filterQuery',
convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || ''
);
},
[setAlertParams, derivedIndexPattern]
);

/* eslint-disable-next-line react-hooks/exhaustive-deps */
const debouncedOnFilterChange = useCallback(debounce(onFilterChange, FILTER_TYPING_DEBOUNCE_MS), [
onFilterChange,
]);
return (
<EuiForm component="form">
<EuiFormRow
fullWidth
label={i18n.translate('xpack.monitoring.alerts.filterLable', {
defaultMessage: 'Filter',
})}
helpText={i18n.translate('xpack.monitoring.alerts.filterHelpText', {
defaultMessage: 'Use a KQL expression to limit the scope of your alert trigger.',
})}
>
<KueryBar
value={alertParams.filterQueryText}
derivedIndexPattern={derivedIndexPattern}
onSubmit={onFilterChange}
onChange={debouncedOnFilterChange}
/>
</EuiFormRow>
<EuiSpacer />
</EuiForm>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
* 2.0.
*/

import React, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiTextColor, EuiSpacer } from '@elastic/eui';
import React from 'react';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
import {
LEGACY_RULES,
LEGACY_RULE_DETAILS,
RULE_REQUIRES_APP_CONTEXT,
} from '../../../common/constants';
import { MonitoringConfig } from '../../types';
import { Expression } from './expression';
import { Props } from '../components/param_details_form/expression';

export function createLegacyAlertTypes(): AlertTypeModel[] {
export function createLegacyAlertTypes(config: MonitoringConfig): AlertTypeModel[] {
return LEGACY_RULES.map((legacyAlert) => {
return {
id: legacyAlert,
Expand All @@ -25,17 +26,7 @@ export function createLegacyAlertTypes(): AlertTypeModel[] {
documentationUrl(docLinks) {
return `${docLinks.links.monitoring.alertsKibanaClusterAlerts}`;
},
alertParamsExpression: () => (
<Fragment>
<EuiSpacer />
<EuiTextColor color="subdued">
{i18n.translate('xpack.monitoring.alerts.legacyAlert.expressionText', {
defaultMessage: 'There is nothing to configure.',
})}
</EuiTextColor>
<EuiSpacer />
</Fragment>
),
alertParamsExpression: (props: Props) => <Expression {...props} config={config} />,
defaultActionMessage: '{{context.internalFullMessage}}',
validate: () => ({ errors: {} }),
requiresAppContext: RULE_REQUIRES_APP_CONTEXT,
Expand Down
Loading

0 comments on commit a29fdf5

Please sign in to comment.