mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Infra UI] Disable Infrastructure and Metrics alerts in Serverless (#167978)
Closes https://github.com/elastic/kibana/issues/164683 ## Summary This PR disables the infrastructure, metrics and logs alerts rule in Serverless: - Deletes the code responsible for the "Metric Anomaly" rule as it was [previously disabled](https://github.com/elastic/kibana/pull/93813) with plans to re-enable it as the previous PR describes but that never happened. - Adds feature flags for all three types of alert rules - Prevents rules registration in serverless based on the feature flags - Adds logic for showing/hiding items in the "Alerts and rules" dropdown - Disables custom threshold rule in the Infra UI by default in serverless as the rule needs to first be enabled by default by @elastic/actionable-observability team ([context](https://elastic.slack.com/archives/C023GDA0WMP/p1696853751040269)) **Dropdown**  **Host details**  ### How to test - Checkout locally Run in Serveless mode - Enable, Infra plugin, custom threshold in Infra, and custom threshold rule in general: ``` xpack.infra.enabled: true xpack.infra.featureFlags.customThresholdAlertsEnabled: true xpack.observability.unsafe.thresholdRule.enabled: true ``` - Go to `/app/metrics/hosts` and make sure there are no "Infrastructure" and "Metrics" items in the "Alerts and rules" dropdown - Click on "Manage rules" in the "Alerts and rules" dropdown, then "Create rule" to open the rule selection flyout - Make sure there are no rules for "Inventory", "Metrics" or "Logs" threshold - Run Kibana in traditional mode - Make sure the "Alerts and rules" dropdown looks as usual and you can create "Infrastructure" and "Metrics" alerts --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
b60517ac53
commit
3e8058bdf2
37 changed files with 238 additions and 1656 deletions
|
@ -268,25 +268,17 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.index_management.enableIndexStats (any)',
|
||||
'xpack.infra.sources.default.fields.message (array)',
|
||||
/**
|
||||
* xpack.infra.featureFlags.customThresholdAlertsEnabled is conditional based on traditional/serverless offering
|
||||
* and will resolve to (boolean)
|
||||
*/
|
||||
'xpack.infra.featureFlags.customThresholdAlertsEnabled (any)',
|
||||
/**
|
||||
* xpack.infra.featureFlags.logsUIEnabled is conditional based on traditional/serverless offering
|
||||
* and will resolve to (boolean)
|
||||
*/
|
||||
'xpack.infra.featureFlags.logsUIEnabled (any)',
|
||||
/**
|
||||
* xpack.infra.featureFlags.metricsExplorerEnabled is conditional based on traditional/serverless offering
|
||||
* and will resolve to (boolean)
|
||||
* Feature flags bellow are conditional based on traditional/serverless offering
|
||||
* and will all resolve to xpack.infra.featureFlags.* (boolean)
|
||||
*/
|
||||
'xpack.infra.featureFlags.metricsExplorerEnabled (any)',
|
||||
/**
|
||||
* xpack.infra.featureFlags.osqueryEnabled is conditional based on traditional/serverless offering
|
||||
* and will resolve to (boolean)
|
||||
*/
|
||||
'xpack.infra.featureFlags.customThresholdAlertsEnabled (any)',
|
||||
'xpack.infra.featureFlags.osqueryEnabled (any)',
|
||||
'xpack.infra.featureFlags.inventoryThresholdAlertRuleEnabled (any)',
|
||||
'xpack.infra.featureFlags.metricThresholdAlertRuleEnabled (any)',
|
||||
'xpack.infra.featureFlags.logThresholdAlertRuleEnabled (any)',
|
||||
'xpack.infra.featureFlags.logsUIEnabled (any)',
|
||||
|
||||
'xpack.license_management.ui.enabled (boolean)',
|
||||
'xpack.maps.preserveDrawingBuffer (boolean)',
|
||||
'xpack.maps.showMapsInspectorAdapter (boolean)',
|
||||
|
|
|
@ -37,7 +37,6 @@ describe('rule_type_registry_deprecated_consumers', () => {
|
|||
"siem.newTermsRule",
|
||||
"siem.notifications",
|
||||
"slo.rules.burnRate",
|
||||
"metrics.alert.anomaly",
|
||||
"logs.alert.document.count",
|
||||
"metrics.alert.inventory.threshold",
|
||||
"metrics.alert.threshold",
|
||||
|
|
|
@ -30,7 +30,6 @@ export const ruleTypeIdWithValidLegacyConsumers: Record<string, string[]> = {
|
|||
'siem.newTermsRule': [ALERTS_FEATURE_ID],
|
||||
'siem.notifications': [ALERTS_FEATURE_ID],
|
||||
'slo.rules.burnRate': [ALERTS_FEATURE_ID],
|
||||
'metrics.alert.anomaly': [ALERTS_FEATURE_ID],
|
||||
'logs.alert.document.count': [ALERTS_FEATURE_ID],
|
||||
'metrics.alert.inventory.threshold': [ALERTS_FEATURE_ID],
|
||||
'metrics.alert.threshold': [ALERTS_FEATURE_ID],
|
||||
|
|
|
@ -12,18 +12,15 @@ import { InventoryItemType, SnapshotMetricType } from '../../inventory_models/ty
|
|||
|
||||
export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold';
|
||||
export const METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.inventory.threshold';
|
||||
export const METRIC_ANOMALY_ALERT_TYPE_ID = 'metrics.alert.anomaly';
|
||||
|
||||
export enum InfraRuleType {
|
||||
MetricThreshold = 'metrics.alert.threshold',
|
||||
InventoryThreshold = 'metrics.alert.inventory.threshold',
|
||||
Anomaly = 'metrics.alert.anomaly',
|
||||
}
|
||||
|
||||
export interface InfraRuleTypeParams {
|
||||
[InfraRuleType.MetricThreshold]: MetricThresholdParams;
|
||||
[InfraRuleType.InventoryThreshold]: InventoryMetricConditions;
|
||||
[InfraRuleType.Anomaly]: MetricAnomalyParams;
|
||||
}
|
||||
|
||||
export enum Comparator {
|
||||
|
|
|
@ -30,6 +30,9 @@ export interface InfraConfig {
|
|||
logsUIEnabled: boolean;
|
||||
metricsExplorerEnabled: boolean;
|
||||
osqueryEnabled: boolean;
|
||||
inventoryThresholdAlertRuleEnabled: boolean;
|
||||
metricThresholdAlertRuleEnabled: boolean;
|
||||
logThresholdAlertRuleEnabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
EuiPopover,
|
||||
EuiHeaderLink,
|
||||
EuiContextMenu,
|
||||
import { EuiPopover, EuiHeaderLink, EuiContextMenu } from '@elastic/eui';
|
||||
import type {
|
||||
EuiContextMenuPanelDescriptor,
|
||||
EuiContextMenuPanelItemDescriptor,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
|
@ -23,6 +22,118 @@ import { InfraClientStartDeps } from '../../../types';
|
|||
|
||||
type VisibleFlyoutType = 'inventory' | 'metricThreshold' | 'customThreshold';
|
||||
|
||||
interface ContextMenuEntries {
|
||||
items: EuiContextMenuPanelItemDescriptor[];
|
||||
panels: EuiContextMenuPanelDescriptor[];
|
||||
}
|
||||
|
||||
function useInfrastructureMenu(
|
||||
onCreateRuleClick: (flyoutType: VisibleFlyoutType) => void
|
||||
): ContextMenuEntries {
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
return useMemo(() => {
|
||||
if (!featureFlags.inventoryThresholdAlertRuleEnabled) {
|
||||
return { items: [], panels: [] };
|
||||
}
|
||||
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'inventory-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.infrastructureDropdownMenu', {
|
||||
defaultMessage: 'Infrastructure',
|
||||
}),
|
||||
panel: 1,
|
||||
},
|
||||
],
|
||||
panels: [
|
||||
{
|
||||
id: 1,
|
||||
title: i18n.translate('xpack.infra.alerting.infrastructureDropdownTitle', {
|
||||
defaultMessage: 'Infrastructure rules',
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'inventory-alerts-create-rule',
|
||||
name: i18n.translate('xpack.infra.alerting.createInventoryRuleButton', {
|
||||
defaultMessage: 'Create inventory rule',
|
||||
}),
|
||||
onClick: () => onCreateRuleClick('inventory'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}, [featureFlags.inventoryThresholdAlertRuleEnabled, onCreateRuleClick]);
|
||||
}
|
||||
|
||||
function useMetricsMenu(
|
||||
onCreateRuleClick: (flyoutType: VisibleFlyoutType) => void
|
||||
): ContextMenuEntries {
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
return useMemo(() => {
|
||||
if (!featureFlags.metricThresholdAlertRuleEnabled) {
|
||||
return { items: [], panels: [] };
|
||||
}
|
||||
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'metrics-threshold-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.metricsDropdownMenu', {
|
||||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
panel: 2,
|
||||
},
|
||||
],
|
||||
panels: [
|
||||
{
|
||||
id: 2,
|
||||
title: i18n.translate('xpack.infra.alerting.metricsDropdownTitle', {
|
||||
defaultMessage: 'Metrics rules',
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'metrics-threshold-alerts-create-rule',
|
||||
name: i18n.translate('xpack.infra.alerting.createThresholdRuleButton', {
|
||||
defaultMessage: 'Create threshold rule',
|
||||
}),
|
||||
onClick: () => onCreateRuleClick('metricThreshold'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}, [featureFlags.metricThresholdAlertRuleEnabled, onCreateRuleClick]);
|
||||
}
|
||||
|
||||
function useCustomThresholdMenu(
|
||||
onCreateRuleClick: (flyoutType: VisibleFlyoutType) => void
|
||||
): ContextMenuEntries {
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
return useMemo(() => {
|
||||
if (!featureFlags.customThresholdAlertsEnabled) {
|
||||
return { items: [], panels: [] };
|
||||
}
|
||||
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'custom-threshold-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.customThresholdDropdownMenu', {
|
||||
defaultMessage: 'Create custom threshold rule',
|
||||
}),
|
||||
onClick: () => onCreateRuleClick('customThreshold'),
|
||||
},
|
||||
],
|
||||
panels: [],
|
||||
};
|
||||
}, [featureFlags.customThresholdAlertsEnabled, onCreateRuleClick]);
|
||||
}
|
||||
|
||||
export const MetricsAlertDropdown = () => {
|
||||
const [popoverOpen, setPopoverOpen] = useState(false);
|
||||
const [visibleFlyoutType, setVisibleFlyoutType] = useState<VisibleFlyoutType | null>(null);
|
||||
|
@ -34,8 +145,6 @@ export const MetricsAlertDropdown = () => {
|
|||
() => Boolean(uiCapabilities?.infrastructure?.save),
|
||||
[uiCapabilities]
|
||||
);
|
||||
const { featureFlags } = usePluginConfig();
|
||||
|
||||
const closeFlyout = useCallback(() => setVisibleFlyoutType(null), [setVisibleFlyoutType]);
|
||||
|
||||
const closePopover = useCallback(() => {
|
||||
|
@ -45,49 +154,18 @@ export const MetricsAlertDropdown = () => {
|
|||
const togglePopover = useCallback(() => {
|
||||
setPopoverOpen(!popoverOpen);
|
||||
}, [setPopoverOpen, popoverOpen]);
|
||||
const infrastructureAlertsPanel = useMemo(
|
||||
() => ({
|
||||
id: 1,
|
||||
title: i18n.translate('xpack.infra.alerting.infrastructureDropdownTitle', {
|
||||
defaultMessage: 'Infrastructure rules',
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'inventory-alerts-create-rule',
|
||||
name: i18n.translate('xpack.infra.alerting.createInventoryRuleButton', {
|
||||
defaultMessage: 'Create inventory rule',
|
||||
}),
|
||||
onClick: () => {
|
||||
closePopover();
|
||||
setVisibleFlyoutType('inventory');
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
[setVisibleFlyoutType, closePopover]
|
||||
|
||||
const onCreateRuleClick = useCallback(
|
||||
(flyoutType: VisibleFlyoutType) => {
|
||||
closePopover();
|
||||
setVisibleFlyoutType(flyoutType);
|
||||
},
|
||||
[closePopover]
|
||||
);
|
||||
|
||||
const metricsAlertsPanel = useMemo(
|
||||
() => ({
|
||||
id: 2,
|
||||
title: i18n.translate('xpack.infra.alerting.metricsDropdownTitle', {
|
||||
defaultMessage: 'Metrics rules',
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
'data-test-subj': 'metrics-threshold-alerts-create-rule',
|
||||
name: i18n.translate('xpack.infra.alerting.createThresholdRuleButton', {
|
||||
defaultMessage: 'Create threshold rule',
|
||||
}),
|
||||
onClick: () => {
|
||||
closePopover();
|
||||
setVisibleFlyoutType('metricThreshold');
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
[setVisibleFlyoutType, closePopover]
|
||||
);
|
||||
const infrastructureMenu = useInfrastructureMenu(onCreateRuleClick);
|
||||
const metricsMenu = useMetricsMenu(onCreateRuleClick);
|
||||
const customThresholdMenu = useCustomThresholdMenu(onCreateRuleClick);
|
||||
|
||||
const manageRulesLinkProps = observability.useRulesLink();
|
||||
|
||||
|
@ -102,56 +180,27 @@ export const MetricsAlertDropdown = () => {
|
|||
[manageRulesLinkProps]
|
||||
);
|
||||
|
||||
const firstPanelMenuItems: EuiContextMenuPanelDescriptor['items'] = useMemo(
|
||||
() =>
|
||||
canCreateAlerts
|
||||
? [
|
||||
{
|
||||
'data-test-subj': 'inventory-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.infrastructureDropdownMenu', {
|
||||
defaultMessage: 'Infrastructure',
|
||||
}),
|
||||
panel: 1,
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'metrics-threshold-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.metricsDropdownMenu', {
|
||||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
panel: 2,
|
||||
},
|
||||
...(featureFlags.customThresholdAlertsEnabled
|
||||
? [
|
||||
{
|
||||
'data-test-subj': 'custom-threshold-alerts-menu-option',
|
||||
name: i18n.translate('xpack.infra.alerting.customThresholdDropdownMenu', {
|
||||
defaultMessage: 'Create custom threshold rule',
|
||||
}),
|
||||
onClick: () => {
|
||||
closePopover();
|
||||
setVisibleFlyoutType('customThreshold');
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
manageAlertsMenuItem,
|
||||
]
|
||||
: [manageAlertsMenuItem],
|
||||
[canCreateAlerts, closePopover, featureFlags.customThresholdAlertsEnabled, manageAlertsMenuItem]
|
||||
);
|
||||
|
||||
const panels: EuiContextMenuPanelDescriptor[] = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
id: 0,
|
||||
title: i18n.translate('xpack.infra.alerting.alertDropdownTitle', {
|
||||
defaultMessage: 'Alerts and rules',
|
||||
}),
|
||||
items: firstPanelMenuItems,
|
||||
},
|
||||
].concat(canCreateAlerts ? [infrastructureAlertsPanel, metricsAlertsPanel] : []),
|
||||
[infrastructureAlertsPanel, metricsAlertsPanel, firstPanelMenuItems, canCreateAlerts]
|
||||
() => [
|
||||
{
|
||||
id: 0,
|
||||
title: i18n.translate('xpack.infra.alerting.alertDropdownTitle', {
|
||||
defaultMessage: 'Alerts and rules',
|
||||
}),
|
||||
items: canCreateAlerts
|
||||
? [
|
||||
...infrastructureMenu.items,
|
||||
...metricsMenu.items,
|
||||
...customThresholdMenu.items,
|
||||
manageAlertsMenuItem,
|
||||
]
|
||||
: [manageAlertsMenuItem],
|
||||
},
|
||||
...(canCreateAlerts
|
||||
? [...infrastructureMenu.panels, ...metricsMenu.panels, ...customThresholdMenu.panels]
|
||||
: []),
|
||||
],
|
||||
[canCreateAlerts, infrastructureMenu, metricsMenu, customThresholdMenu, manageAlertsMenuItem]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -27,6 +27,15 @@ export function AlertFlyout({ onClose }: Props) {
|
|||
onClose,
|
||||
canChangeTrigger: false,
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
metadata: {
|
||||
currentOptions: {
|
||||
/*
|
||||
Setting the groupBy is currently required in custom threshold
|
||||
rule for it to populate the rule with additional host context.
|
||||
*/
|
||||
groupBy: 'host.name',
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [onClose, triggersActionsUI]);
|
||||
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* 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, useContext, useMemo } from 'react';
|
||||
|
||||
import { TriggerActionsContext } from '../../../utils/triggers_actions_context';
|
||||
import { METRIC_ANOMALY_ALERT_TYPE_ID } from '../../../../common/alerting/metrics';
|
||||
import { InfraWaffleMapOptions } from '../../../lib/lib';
|
||||
import { InventoryItemType } from '../../../../common/inventory_models/types';
|
||||
import { useAlertPrefillContext } from '../../use_alert_prefill';
|
||||
|
||||
interface Props {
|
||||
visible?: boolean;
|
||||
metric?: InfraWaffleMapOptions['metric'];
|
||||
nodeType?: InventoryItemType;
|
||||
filter?: string;
|
||||
setVisible(val: boolean): void;
|
||||
}
|
||||
|
||||
export const AlertFlyout = ({ metric, nodeType, visible, setVisible }: Props) => {
|
||||
const { triggersActionsUI } = useContext(TriggerActionsContext);
|
||||
|
||||
const onCloseFlyout = useCallback(() => setVisible(false), [setVisible]);
|
||||
const AddAlertFlyout = useMemo(
|
||||
() =>
|
||||
triggersActionsUI &&
|
||||
triggersActionsUI.getAddRuleFlyout({
|
||||
consumer: 'infrastructure',
|
||||
onClose: onCloseFlyout,
|
||||
canChangeTrigger: false,
|
||||
ruleTypeId: METRIC_ANOMALY_ALERT_TYPE_ID,
|
||||
metadata: {
|
||||
metric,
|
||||
nodeType,
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[triggersActionsUI, visible]
|
||||
);
|
||||
|
||||
return <>{visible && AddAlertFlyout}</>;
|
||||
};
|
||||
|
||||
export const PrefilledAnomalyAlertFlyout = ({ onClose }: { onClose(): void }) => {
|
||||
const { inventoryPrefill } = useAlertPrefillContext();
|
||||
const { nodeType, metric } = inventoryPrefill;
|
||||
|
||||
return <AlertFlyout metric={metric} nodeType={nodeType} visible setVisible={onClose} />;
|
||||
};
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* 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 { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
|
||||
// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock`
|
||||
import { coreMock as mockCoreMock } from '@kbn/core/public/mocks';
|
||||
import React from 'react';
|
||||
import { Expression, AlertContextMeta } from './expression';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
|
||||
jest.mock('../../../containers/metrics_source/source', () => ({
|
||||
withSourceProvider: () => jest.fn,
|
||||
useSourceContext: () => ({
|
||||
source: { id: 'default' },
|
||||
createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../hooks/use_kibana', () => ({
|
||||
useKibanaContextForPlugin: () => ({
|
||||
services: mockCoreMock.createStart(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../hooks/use_kibana_space', () => ({
|
||||
useActiveKibanaSpace: () => ({
|
||||
space: { id: 'default' },
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../containers/ml/infra_ml_capabilities', () => ({
|
||||
useInfraMLCapabilities: () => ({
|
||||
isLoading: false,
|
||||
hasInfraMLCapabilities: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
const dataViewMock = dataViewPluginMocks.createStartContract();
|
||||
|
||||
describe('Expression', () => {
|
||||
async function setup(currentOptions: AlertContextMeta) {
|
||||
const ruleParams = {
|
||||
metric: undefined,
|
||||
nodeType: undefined,
|
||||
threshold: 50,
|
||||
};
|
||||
const wrapper = mountWithIntl(
|
||||
<Expression
|
||||
ruleInterval="1m"
|
||||
ruleThrottle="1m"
|
||||
alertNotifyWhen="onThrottleInterval"
|
||||
ruleParams={ruleParams as any}
|
||||
errors={{}}
|
||||
setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)}
|
||||
setRuleProperty={() => {}}
|
||||
metadata={currentOptions}
|
||||
dataViews={dataViewMock}
|
||||
/>
|
||||
);
|
||||
|
||||
const update = async () =>
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
await update();
|
||||
|
||||
return { wrapper, update, ruleParams };
|
||||
}
|
||||
|
||||
it('should prefill the alert using the context metadata', async () => {
|
||||
const currentOptions = {
|
||||
nodeType: 'pod',
|
||||
metric: { type: 'tx' },
|
||||
};
|
||||
const { ruleParams } = await setup(currentOptions as AlertContextMeta);
|
||||
expect(ruleParams.nodeType).toBe('k8s');
|
||||
expect(ruleParams.metric).toBe('network_out');
|
||||
});
|
||||
});
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiSkeletonText, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { euiStyled, EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import {
|
||||
RuleTypeParams,
|
||||
RuleTypeParamsExpressionProps,
|
||||
WhenExpression,
|
||||
} from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold';
|
||||
import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source';
|
||||
import { MetricAnomalyParams } from '../../../../common/alerting/metrics';
|
||||
import { findInventoryModel } from '../../../../common/inventory_models';
|
||||
import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types';
|
||||
import { SubscriptionSplashPrompt } from '../../../components/subscription_splash_content';
|
||||
import { useInfraMLCapabilities } from '../../../containers/ml/infra_ml_capabilities';
|
||||
import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space';
|
||||
import { InfraWaffleMapOptions } from '../../../lib/lib';
|
||||
import { InfluencerFilter } from './influencer_filter';
|
||||
import { NodeTypeExpression } from './node_type';
|
||||
import { SeverityThresholdExpression } from './severity_threshold';
|
||||
|
||||
export interface AlertContextMeta {
|
||||
metric?: InfraWaffleMapOptions['metric'];
|
||||
nodeType?: InventoryItemType;
|
||||
}
|
||||
|
||||
type AlertParams = RuleTypeParams &
|
||||
MetricAnomalyParams & { sourceId: string; spaceId: string; hasInfraMLCapabilities: boolean };
|
||||
|
||||
type Props = Omit<
|
||||
RuleTypeParamsExpressionProps<AlertParams, AlertContextMeta>,
|
||||
'defaultActionGroupId' | 'actionGroups' | 'charts' | 'data' | 'unifiedSearch' | 'onChangeMetaData'
|
||||
>;
|
||||
|
||||
export const defaultExpression = {
|
||||
metric: 'memory_usage' as MetricAnomalyParams['metric'],
|
||||
threshold: ML_ANOMALY_THRESHOLD.MAJOR as MetricAnomalyParams['threshold'],
|
||||
nodeType: 'hosts' as MetricAnomalyParams['nodeType'],
|
||||
influencerFilter: undefined,
|
||||
};
|
||||
|
||||
export const Expression: React.FC<Props> = (props) => {
|
||||
const { hasInfraMLCapabilities, isLoading: isLoadingMLCapabilities } = useInfraMLCapabilities();
|
||||
const { space } = useActiveKibanaSpace();
|
||||
|
||||
const { setRuleParams, ruleParams, ruleInterval, metadata } = props;
|
||||
const { source, createDerivedIndexPattern } = useSourceContext();
|
||||
|
||||
const derivedIndexPattern = useMemo(
|
||||
() => createDerivedIndexPattern(),
|
||||
[createDerivedIndexPattern]
|
||||
);
|
||||
|
||||
const [influencerFieldName, updateInfluencerFieldName] = useState(
|
||||
ruleParams.influencerFilter?.fieldName ?? 'host.name'
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setRuleParams('hasInfraMLCapabilities', hasInfraMLCapabilities);
|
||||
}, [setRuleParams, hasInfraMLCapabilities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ruleParams.influencerFilter) {
|
||||
setRuleParams('influencerFilter', {
|
||||
...ruleParams.influencerFilter,
|
||||
fieldName: influencerFieldName,
|
||||
});
|
||||
}
|
||||
}, [influencerFieldName, ruleParams, setRuleParams]);
|
||||
const updateInfluencerFieldValue = useCallback(
|
||||
(value: string) => {
|
||||
if (value) {
|
||||
setRuleParams('influencerFilter', {
|
||||
...ruleParams.influencerFilter,
|
||||
fieldValue: value,
|
||||
} as MetricAnomalyParams['influencerFilter']);
|
||||
} else {
|
||||
setRuleParams('influencerFilter', undefined);
|
||||
}
|
||||
},
|
||||
[setRuleParams, ruleParams]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setRuleParams('alertInterval', ruleInterval);
|
||||
}, [setRuleParams, ruleInterval]);
|
||||
|
||||
const updateNodeType = useCallback(
|
||||
(nt: any) => {
|
||||
setRuleParams('nodeType', nt);
|
||||
},
|
||||
[setRuleParams]
|
||||
);
|
||||
|
||||
const updateMetric = useCallback(
|
||||
(metric: string) => {
|
||||
setRuleParams('metric', metric as MetricAnomalyParams['metric']);
|
||||
},
|
||||
[setRuleParams]
|
||||
);
|
||||
|
||||
const updateSeverityThreshold = useCallback(
|
||||
(threshold: any) => {
|
||||
setRuleParams('threshold', threshold);
|
||||
},
|
||||
[setRuleParams]
|
||||
);
|
||||
|
||||
const prefillNodeType = useCallback(() => {
|
||||
const md = metadata;
|
||||
if (md && md.nodeType) {
|
||||
setRuleParams(
|
||||
'nodeType',
|
||||
getMLNodeTypeFromInventoryNodeType(md.nodeType) ?? defaultExpression.nodeType
|
||||
);
|
||||
} else {
|
||||
setRuleParams('nodeType', defaultExpression.nodeType);
|
||||
}
|
||||
}, [metadata, setRuleParams]);
|
||||
|
||||
const prefillMetric = useCallback(() => {
|
||||
const md = metadata;
|
||||
if (md && md.metric) {
|
||||
setRuleParams(
|
||||
'metric',
|
||||
getMLMetricFromInventoryMetric(md.metric.type) ?? defaultExpression.metric
|
||||
);
|
||||
} else {
|
||||
setRuleParams('metric', defaultExpression.metric);
|
||||
}
|
||||
}, [metadata, setRuleParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ruleParams.nodeType) {
|
||||
prefillNodeType();
|
||||
}
|
||||
|
||||
if (!ruleParams.threshold) {
|
||||
setRuleParams('threshold', defaultExpression.threshold);
|
||||
}
|
||||
|
||||
if (!ruleParams.metric) {
|
||||
prefillMetric();
|
||||
}
|
||||
|
||||
if (!ruleParams.sourceId) {
|
||||
setRuleParams('sourceId', source?.id || 'default');
|
||||
}
|
||||
|
||||
if (!ruleParams.spaceId) {
|
||||
setRuleParams('spaceId', space?.id || 'default');
|
||||
}
|
||||
}, [metadata, derivedIndexPattern, defaultExpression, source, space]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (isLoadingMLCapabilities) return <EuiSkeletonText lines={10} />;
|
||||
if (!hasInfraMLCapabilities) return <SubscriptionSplashPrompt />;
|
||||
|
||||
return (
|
||||
// https://github.com/elastic/kibana/issues/89506
|
||||
<EuiThemeProvider>
|
||||
<EuiText size="xs">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.conditions"
|
||||
defaultMessage="Conditions"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
<StyledExpression>
|
||||
<StyledExpressionRow>
|
||||
<NodeTypeExpression
|
||||
options={nodeTypes}
|
||||
value={ruleParams.nodeType ?? defaultExpression.nodeType}
|
||||
onChange={updateNodeType}
|
||||
/>
|
||||
</StyledExpressionRow>
|
||||
</StyledExpression>
|
||||
<EuiSpacer size={'xs'} />
|
||||
<StyledExpressionRow>
|
||||
<StyledExpression>
|
||||
<WhenExpression
|
||||
aggType={ruleParams.metric ?? defaultExpression.metric}
|
||||
onChangeSelectedAggType={updateMetric}
|
||||
customAggTypesOptions={{
|
||||
memory_usage: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.anomalyJobs.memoryUsage', {
|
||||
defaultMessage: 'Memory usage',
|
||||
}),
|
||||
fieldRequired: false,
|
||||
value: 'memory_usage',
|
||||
validNormalizedTypes: [],
|
||||
},
|
||||
network_in: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.anomalyJobs.networkIn', {
|
||||
defaultMessage: 'Network in',
|
||||
}),
|
||||
fieldRequired: false,
|
||||
validNormalizedTypes: [],
|
||||
value: 'network_in',
|
||||
},
|
||||
network_out: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.anomalyJobs.networkOut', {
|
||||
defaultMessage: 'Network out',
|
||||
}),
|
||||
fieldRequired: false,
|
||||
validNormalizedTypes: [],
|
||||
value: 'network_out',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</StyledExpression>
|
||||
<StyledExpression>
|
||||
<SeverityThresholdExpression
|
||||
value={ruleParams.threshold ?? ML_ANOMALY_THRESHOLD.CRITICAL}
|
||||
onChange={updateSeverityThreshold}
|
||||
/>
|
||||
</StyledExpression>
|
||||
</StyledExpressionRow>
|
||||
<EuiSpacer size={'m'} />
|
||||
<InfluencerFilter
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
nodeType={ruleParams.nodeType}
|
||||
fieldName={influencerFieldName}
|
||||
fieldValue={ruleParams.influencerFilter?.fieldValue ?? ''}
|
||||
onChangeFieldName={updateInfluencerFieldName}
|
||||
onChangeFieldValue={updateInfluencerFieldValue}
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// required for dynamic import
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default withSourceProvider<Props>(Expression)('default');
|
||||
|
||||
const StyledExpressionRow = euiStyled(EuiFlexGroup)`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -4px;
|
||||
`;
|
||||
|
||||
const StyledExpression = euiStyled.div`
|
||||
padding: 0 4px;
|
||||
`;
|
||||
|
||||
const getDisplayNameForType = (type: InventoryItemType) => {
|
||||
const inventoryModel = findInventoryModel(type);
|
||||
return inventoryModel.displayName;
|
||||
};
|
||||
|
||||
export const nodeTypes: { [key: string]: any } = {
|
||||
hosts: {
|
||||
text: getDisplayNameForType('host'),
|
||||
value: 'hosts',
|
||||
},
|
||||
k8s: {
|
||||
text: getDisplayNameForType('pod'),
|
||||
value: 'k8s',
|
||||
},
|
||||
};
|
||||
|
||||
const getMLMetricFromInventoryMetric: (
|
||||
metric: SnapshotMetricType
|
||||
) => MetricAnomalyParams['metric'] | null = (metric) => {
|
||||
switch (metric) {
|
||||
case 'memory':
|
||||
return 'memory_usage';
|
||||
case 'tx':
|
||||
return 'network_out';
|
||||
case 'rx':
|
||||
return 'network_in';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getMLNodeTypeFromInventoryNodeType: (
|
||||
nodeType: InventoryItemType
|
||||
) => MetricAnomalyParams['nodeType'] | null = (nodeType) => {
|
||||
switch (nodeType) {
|
||||
case 'host':
|
||||
return 'hosts';
|
||||
case 'pod':
|
||||
return 'k8s';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* 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 { debounce } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useCallback, useEffect, useMemo } from 'react';
|
||||
import { first } from 'lodash';
|
||||
import { EuiFlexGroup, EuiFormRow, EuiCheckbox, EuiFlexItem, EuiSelect } from '@elastic/eui';
|
||||
import {
|
||||
MetricsExplorerKueryBar,
|
||||
CurryLoadSuggestionsType,
|
||||
} from '../../../pages/metrics/metrics_explorer/components/kuery_bar';
|
||||
import { MetricAnomalyParams } from '../../../../common/alerting/metrics';
|
||||
|
||||
interface Props {
|
||||
fieldName: string;
|
||||
fieldValue: string;
|
||||
nodeType: MetricAnomalyParams['nodeType'];
|
||||
onChangeFieldName: (v: string) => void;
|
||||
onChangeFieldValue: (v: string) => void;
|
||||
derivedIndexPattern: Parameters<typeof MetricsExplorerKueryBar>[0]['derivedIndexPattern'];
|
||||
}
|
||||
|
||||
const FILTER_TYPING_DEBOUNCE_MS = 500;
|
||||
|
||||
export const InfluencerFilter = ({
|
||||
fieldName,
|
||||
fieldValue,
|
||||
nodeType,
|
||||
onChangeFieldName,
|
||||
onChangeFieldValue,
|
||||
derivedIndexPattern,
|
||||
}: Props) => {
|
||||
const fieldNameOptions = useMemo(
|
||||
() => (nodeType === 'k8s' ? k8sFieldNames : hostFieldNames),
|
||||
[nodeType]
|
||||
);
|
||||
|
||||
// If initial props contain a fieldValue, assume it was passed in from loaded alertParams,
|
||||
// and enable the UI element
|
||||
const [isEnabled, updateIsEnabled] = useState(fieldValue ? true : false);
|
||||
const [storedFieldValue, updateStoredFieldValue] = useState(fieldValue);
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
nodeType === 'k8s'
|
||||
? onChangeFieldName(first(k8sFieldNames)!.value)
|
||||
: onChangeFieldName(first(hostFieldNames)!.value),
|
||||
[nodeType, onChangeFieldName]
|
||||
);
|
||||
|
||||
const onSelectFieldName = useCallback(
|
||||
(e) => onChangeFieldName(e.target.value),
|
||||
[onChangeFieldName]
|
||||
);
|
||||
const onUpdateFieldValue = useCallback(
|
||||
(value) => {
|
||||
updateStoredFieldValue(value);
|
||||
onChangeFieldValue(value);
|
||||
},
|
||||
[onChangeFieldValue]
|
||||
);
|
||||
|
||||
const toggleEnabled = useCallback(() => {
|
||||
const nextState = !isEnabled;
|
||||
updateIsEnabled(nextState);
|
||||
if (!nextState) {
|
||||
onChangeFieldValue('');
|
||||
} else {
|
||||
onChangeFieldValue(storedFieldValue);
|
||||
}
|
||||
}, [isEnabled, updateIsEnabled, onChangeFieldValue, storedFieldValue]);
|
||||
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
const debouncedOnUpdateFieldValue = useCallback(
|
||||
debounce(onUpdateFieldValue, FILTER_TYPING_DEBOUNCE_MS),
|
||||
[onUpdateFieldValue]
|
||||
);
|
||||
|
||||
const affixFieldNameToQuery: CurryLoadSuggestionsType =
|
||||
(fn) => (expression, cursorPosition, maxSuggestions) => {
|
||||
// Add the field name to the front of the passed-in query
|
||||
const prefix = `${fieldName}:`;
|
||||
// Trim whitespace to prevent AND/OR suggestions
|
||||
const modifiedExpression = `${prefix}${expression}`.trim();
|
||||
// Move the cursor position forward by the length of the field name
|
||||
const modifiedPosition = cursorPosition + prefix.length;
|
||||
return fn(modifiedExpression, modifiedPosition, maxSuggestions, (suggestions) =>
|
||||
suggestions
|
||||
.map((s) => ({
|
||||
...s,
|
||||
// Remove quotes from suggestions
|
||||
text: s.text.replace(/\"/g, '').trim(),
|
||||
// Offset the returned suggestions' cursor positions so that they can be autocompleted accurately
|
||||
start: s.start - prefix.length,
|
||||
end: s.end - prefix.length,
|
||||
}))
|
||||
// Removing quotes can lead to an already-selected suggestion still coming up in the autocomplete list,
|
||||
// so filter these out
|
||||
.filter((s) => !expression.startsWith(s.text))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={
|
||||
<EuiCheckbox
|
||||
label={filterByNodeLabel}
|
||||
id="anomalyAlertFilterByNodeCheckbox"
|
||||
onChange={toggleEnabled}
|
||||
checked={isEnabled}
|
||||
/>
|
||||
}
|
||||
helpText={
|
||||
isEnabled ? (
|
||||
<>
|
||||
{i18n.translate('xpack.infra.metrics.alertFlyout.anomalyFilterHelpText', {
|
||||
defaultMessage:
|
||||
'Limit the scope of your alert trigger to anomalies influenced by certain node(s).',
|
||||
})}
|
||||
<br />
|
||||
{i18n.translate('xpack.infra.metrics.alertFlyout.anomalyFilterHelpTextExample', {
|
||||
defaultMessage: 'For example: "my-node-1" or "my-node-*"',
|
||||
})}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
fullWidth
|
||||
display="rowCompressed"
|
||||
>
|
||||
{isEnabled ? (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiSelect
|
||||
data-test-subj="infraInfluencerFilterSelect"
|
||||
id="selectInfluencerFieldName"
|
||||
value={fieldName}
|
||||
onChange={onSelectFieldName}
|
||||
options={fieldNameOptions}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MetricsExplorerKueryBar
|
||||
derivedIndexPattern={derivedIndexPattern}
|
||||
onChange={debouncedOnUpdateFieldValue}
|
||||
onSubmit={onUpdateFieldValue}
|
||||
value={storedFieldValue}
|
||||
curryLoadSuggestions={affixFieldNameToQuery}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.infra.metrics.alertFlyout.anomalyInfluencerFilterPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Everything',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
const hostFieldNames = [
|
||||
{
|
||||
value: 'host.name',
|
||||
text: 'host.name',
|
||||
},
|
||||
];
|
||||
|
||||
const k8sFieldNames = [
|
||||
{
|
||||
value: 'kubernetes.pod.uid',
|
||||
text: 'kubernetes.pod.uid',
|
||||
},
|
||||
{
|
||||
value: 'kubernetes.node.name',
|
||||
text: 'kubernetes.node.name',
|
||||
},
|
||||
{
|
||||
value: 'kubernetes.namespace',
|
||||
text: 'kubernetes.namespace',
|
||||
},
|
||||
];
|
||||
|
||||
const filterByNodeLabel = i18n.translate('xpack.infra.metrics.alertFlyout.filterByNodeLabel', {
|
||||
defaultMessage: 'Filter by node',
|
||||
});
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiExpression, EuiPopover, EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui';
|
||||
import { EuiPopoverTitle, EuiButtonIcon } from '@elastic/eui';
|
||||
import { MetricAnomalyParams } from '../../../../common/alerting/metrics';
|
||||
|
||||
type Node = MetricAnomalyParams['nodeType'];
|
||||
|
||||
interface WhenExpressionProps {
|
||||
value: Node;
|
||||
options: { [key: string]: { text: string; value: Node } };
|
||||
onChange: (value: Node) => void;
|
||||
popupPosition?:
|
||||
| 'upCenter'
|
||||
| 'upLeft'
|
||||
| 'upRight'
|
||||
| 'downCenter'
|
||||
| 'downLeft'
|
||||
| 'downRight'
|
||||
| 'leftCenter'
|
||||
| 'leftUp'
|
||||
| 'leftDown'
|
||||
| 'rightCenter'
|
||||
| 'rightUp'
|
||||
| 'rightDown';
|
||||
}
|
||||
|
||||
export const NodeTypeExpression = ({
|
||||
value,
|
||||
options,
|
||||
onChange,
|
||||
popupPosition,
|
||||
}: WhenExpressionProps) => {
|
||||
const [aggTypePopoverOpen, setAggTypePopoverOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiExpression
|
||||
data-test-subj="nodeTypeExpression"
|
||||
description={i18n.translate(
|
||||
'xpack.infra.metrics.alertFlyout.expression.for.descriptionLabel',
|
||||
{
|
||||
defaultMessage: 'For',
|
||||
}
|
||||
)}
|
||||
value={options[value].text}
|
||||
isActive={aggTypePopoverOpen}
|
||||
onClick={() => {
|
||||
setAggTypePopoverOpen(true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isOpen={aggTypePopoverOpen}
|
||||
closePopover={() => {
|
||||
setAggTypePopoverOpen(false);
|
||||
}}
|
||||
ownFocus
|
||||
anchorPosition={popupPosition ?? 'downLeft'}
|
||||
>
|
||||
<div>
|
||||
<ClosablePopoverTitle onClose={() => setAggTypePopoverOpen(false)}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.expression.for.popoverTitle"
|
||||
defaultMessage="Node Type"
|
||||
/>
|
||||
</ClosablePopoverTitle>
|
||||
<EuiSelect
|
||||
data-test-subj="forExpressionSelect"
|
||||
value={value}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value as Node);
|
||||
setAggTypePopoverOpen(false);
|
||||
}}
|
||||
options={Object.values(options).map((o) => o)}
|
||||
/>
|
||||
</div>
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
||||
interface ClosablePopoverTitleProps {
|
||||
children: JSX.Element;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const ClosablePopoverTitle = ({ children, onClose }: ClosablePopoverTitleProps) => {
|
||||
return (
|
||||
<EuiPopoverTitle>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem>{children}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="infraClosablePopoverTitleButton"
|
||||
iconType="cross"
|
||||
color="danger"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.infra.metrics.expressionItems.components.closablePopoverTitle.closeLabel',
|
||||
{
|
||||
defaultMessage: 'Close',
|
||||
}
|
||||
)}
|
||||
onClick={() => onClose()}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPopoverTitle>
|
||||
);
|
||||
};
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiExpression, EuiPopover, EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui';
|
||||
import { EuiPopoverTitle, EuiButtonIcon } from '@elastic/eui';
|
||||
import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold';
|
||||
|
||||
interface WhenExpressionProps {
|
||||
value: Exclude<ML_ANOMALY_THRESHOLD, ML_ANOMALY_THRESHOLD.LOW>;
|
||||
onChange: (value: ML_ANOMALY_THRESHOLD) => void;
|
||||
popupPosition?:
|
||||
| 'upCenter'
|
||||
| 'upLeft'
|
||||
| 'upRight'
|
||||
| 'downCenter'
|
||||
| 'downLeft'
|
||||
| 'downRight'
|
||||
| 'leftCenter'
|
||||
| 'leftUp'
|
||||
| 'leftDown'
|
||||
| 'rightCenter'
|
||||
| 'rightUp'
|
||||
| 'rightDown';
|
||||
}
|
||||
|
||||
const options = {
|
||||
[ML_ANOMALY_THRESHOLD.CRITICAL]: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.criticalLabel', {
|
||||
defaultMessage: 'Critical',
|
||||
}),
|
||||
value: ML_ANOMALY_THRESHOLD.CRITICAL,
|
||||
},
|
||||
[ML_ANOMALY_THRESHOLD.MAJOR]: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.majorLabel', {
|
||||
defaultMessage: 'Major',
|
||||
}),
|
||||
value: ML_ANOMALY_THRESHOLD.MAJOR,
|
||||
},
|
||||
[ML_ANOMALY_THRESHOLD.MINOR]: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.minorLabel', {
|
||||
defaultMessage: 'Minor',
|
||||
}),
|
||||
value: ML_ANOMALY_THRESHOLD.MINOR,
|
||||
},
|
||||
[ML_ANOMALY_THRESHOLD.WARNING]: {
|
||||
text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.warningLabel', {
|
||||
defaultMessage: 'Warning',
|
||||
}),
|
||||
value: ML_ANOMALY_THRESHOLD.WARNING,
|
||||
},
|
||||
};
|
||||
|
||||
export const SeverityThresholdExpression = ({
|
||||
value,
|
||||
onChange,
|
||||
popupPosition,
|
||||
}: WhenExpressionProps) => {
|
||||
const [aggTypePopoverOpen, setAggTypePopoverOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiExpression
|
||||
data-test-subj="nodeTypeExpression"
|
||||
description={i18n.translate(
|
||||
'xpack.infra.metrics.alertFlyout.expression.severityScore.descriptionLabel',
|
||||
{
|
||||
defaultMessage: 'Severity score is above',
|
||||
}
|
||||
)}
|
||||
value={options[value].text}
|
||||
isActive={aggTypePopoverOpen}
|
||||
onClick={() => {
|
||||
setAggTypePopoverOpen(true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isOpen={aggTypePopoverOpen}
|
||||
closePopover={() => {
|
||||
setAggTypePopoverOpen(false);
|
||||
}}
|
||||
ownFocus
|
||||
anchorPosition={popupPosition ?? 'downLeft'}
|
||||
>
|
||||
<div>
|
||||
<ClosablePopoverTitle onClose={() => setAggTypePopoverOpen(false)}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.expression.severityScore.popoverTitle"
|
||||
defaultMessage="Severity Score"
|
||||
/>
|
||||
</ClosablePopoverTitle>
|
||||
<EuiSelect
|
||||
data-test-subj="severityExpressionSelect"
|
||||
value={value}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
onChange(Number(e.target.value) as ML_ANOMALY_THRESHOLD);
|
||||
setAggTypePopoverOpen(false);
|
||||
}}
|
||||
options={Object.values(options).map((o) => o)}
|
||||
/>
|
||||
</div>
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
||||
interface ClosablePopoverTitleProps {
|
||||
children: JSX.Element;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const ClosablePopoverTitle = ({ children, onClose }: ClosablePopoverTitleProps) => {
|
||||
return (
|
||||
<EuiPopoverTitle>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem>{children}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="infraClosablePopoverTitleButton"
|
||||
iconType="cross"
|
||||
color="danger"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.infra.metrics.expressionItems.components.closablePopoverTitle.closeLabel',
|
||||
{
|
||||
defaultMessage: 'Close',
|
||||
}
|
||||
)}
|
||||
onClick={() => onClose()}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPopoverTitle>
|
||||
);
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import type { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
|
||||
export function validateMetricAnomaly({
|
||||
hasInfraMLCapabilities,
|
||||
}: {
|
||||
hasInfraMLCapabilities: boolean;
|
||||
}): ValidationResult {
|
||||
const validationResult = { errors: {} };
|
||||
const errors: {
|
||||
hasInfraMLCapabilities: string[];
|
||||
} = {
|
||||
hasInfraMLCapabilities: [],
|
||||
};
|
||||
|
||||
validationResult.errors = errors;
|
||||
|
||||
if (!hasInfraMLCapabilities) {
|
||||
errors.hasInfraMLCapabilities.push(
|
||||
i18n.translate('xpack.infra.metrics.alertFlyout.error.mlCapabilitiesRequired', {
|
||||
defaultMessage: 'Cannot create an anomaly alert when machine learning is disabled.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { RuleTypeParams } from '@kbn/alerting-plugin/common';
|
||||
import { METRIC_ANOMALY_ALERT_TYPE_ID } from '../../../common/alerting/metrics';
|
||||
import { validateMetricAnomaly } from './components/validation';
|
||||
|
||||
interface MetricAnomalyRuleTypeParams extends RuleTypeParams {
|
||||
hasInfraMLCapabilities: boolean;
|
||||
}
|
||||
|
||||
export function createMetricAnomalyRuleType(): RuleTypeModel<MetricAnomalyRuleTypeParams> {
|
||||
return {
|
||||
id: METRIC_ANOMALY_ALERT_TYPE_ID,
|
||||
description: i18n.translate('xpack.infra.metrics.anomaly.alertFlyout.alertDescription', {
|
||||
defaultMessage: 'Alert when the anomaly score exceeds a defined threshold.',
|
||||
}),
|
||||
iconClass: 'bell',
|
||||
documentationUrl(docLinks) {
|
||||
return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/observability/${docLinks.DOC_LINK_VERSION}/infrastructure-anomaly-alert.html`;
|
||||
},
|
||||
ruleParamsExpression: React.lazy(() => import('./components/expression')),
|
||||
validate: validateMetricAnomaly,
|
||||
defaultActionMessage: i18n.translate(
|
||||
'xpack.infra.metrics.alerting.anomaly.defaultActionMessage',
|
||||
{
|
||||
defaultMessage: `\\{\\{alertName\\}\\} is in a state of \\{\\{context.alertState\\}\\}
|
||||
|
||||
\\{\\{context.metric\\}\\} was \\{\\{context.summary\\}\\} than normal at \\{\\{context.timestamp\\}\\}
|
||||
|
||||
Typical value: \\{\\{context.typical\\}\\}
|
||||
Actual value: \\{\\{context.actual\\}\\}
|
||||
`,
|
||||
}
|
||||
),
|
||||
requiresAppContext: false,
|
||||
};
|
||||
}
|
|
@ -9,6 +9,7 @@ import React, { useMemo } from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { usePluginConfig } from '../../../../containers/plugin_config_context';
|
||||
import type { AlertsEsQuery } from '../../../../common/alerts/types';
|
||||
import type { InventoryItemType } from '../../../../../common/inventory_models/types';
|
||||
import { findInventoryFields } from '../../../../../common/inventory_models';
|
||||
|
@ -32,6 +33,7 @@ export const AlertsSummaryContent = ({
|
|||
assetType: InventoryItemType;
|
||||
dateRange: TimeRange;
|
||||
}) => {
|
||||
const { featureFlags } = usePluginConfig();
|
||||
const [isAlertFlyoutVisible, { toggle: toggleAlertFlyout }] = useBoolean(false);
|
||||
const { overrides } = useAssetDetailsRenderPropsContext();
|
||||
|
||||
|
@ -51,9 +53,11 @@ export const AlertsSummaryContent = ({
|
|||
<EuiFlexItem>
|
||||
<AlertsSectionTitle />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkToAlertsRule onClick={toggleAlertFlyout} />
|
||||
</EuiFlexItem>
|
||||
{featureFlags.inventoryThresholdAlertRuleEnabled && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkToAlertsRule onClick={toggleAlertFlyout} />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkToAlertsPage
|
||||
assetName={assetName}
|
||||
|
@ -64,13 +68,16 @@ export const AlertsSummaryContent = ({
|
|||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<MemoAlertSummaryWidget alertsQuery={alertsEsQueryByStatus} dateRange={dateRange} />
|
||||
<AlertFlyout
|
||||
filter={`${findInventoryFields(assetType).name}: "${assetName}"`}
|
||||
nodeType={assetType}
|
||||
setVisible={toggleAlertFlyout}
|
||||
visible={isAlertFlyoutVisible}
|
||||
options={overrides?.alertRule?.options}
|
||||
/>
|
||||
|
||||
{featureFlags.inventoryThresholdAlertRuleEnabled && (
|
||||
<AlertFlyout
|
||||
filter={`${findInventoryFields(assetType).name}: "${assetName}"`}
|
||||
nodeType={assetType}
|
||||
setVisible={toggleAlertFlyout}
|
||||
visible={isAlertFlyoutVisible}
|
||||
options={overrides?.alertRule?.options}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -24,6 +24,9 @@ describe('usePluginConfig()', () => {
|
|||
logsUIEnabled: false,
|
||||
metricsExplorerEnabled: false,
|
||||
osqueryEnabled: false,
|
||||
inventoryThresholdAlertRuleEnabled: true,
|
||||
metricThresholdAlertRuleEnabled: true,
|
||||
logThresholdAlertRuleEnabled: true,
|
||||
},
|
||||
};
|
||||
const { result } = renderHook(() => usePluginConfig(), {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
|||
import { GetViewInAppRelativeUrlFnOpts, PluginSetupContract } from '@kbn/alerting-plugin/server';
|
||||
import { observabilityPaths } from '@kbn/observability-plugin/common';
|
||||
import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration';
|
||||
import type { InfraConfig } from '../../../../common/plugin_config_types';
|
||||
import {
|
||||
Comparator,
|
||||
METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
|
||||
|
@ -81,10 +82,15 @@ const groupActionVariableDescription = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export async function registerMetricInventoryThresholdRuleType(
|
||||
export async function registerInventoryThresholdRuleType(
|
||||
alertingPlugin: PluginSetupContract,
|
||||
libs: InfraBackendLibs
|
||||
libs: InfraBackendLibs,
|
||||
{ featureFlags }: InfraConfig
|
||||
) {
|
||||
if (!featureFlags.inventoryThresholdAlertRuleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
alertingPlugin.registerType({
|
||||
id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
|
||||
name: i18n.translate('xpack.infra.metrics.inventory.alertName', {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { GetViewInAppRelativeUrlFnOpts, PluginSetupContract } from '@kbn/alerting-plugin/server';
|
||||
import { observabilityPaths } from '@kbn/observability-plugin/common';
|
||||
import type { InfraConfig } from '../../../../common/plugin_config_types';
|
||||
import { O11Y_AAD_FIELDS } from '../../../../common/constants';
|
||||
import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor';
|
||||
import { extractReferences, injectReferences } from './log_threshold_references_manager';
|
||||
|
@ -103,8 +104,13 @@ const viewInAppUrlActionVariableDescription = i18n.translate(
|
|||
|
||||
export async function registerLogThresholdRuleType(
|
||||
alertingPlugin: PluginSetupContract,
|
||||
libs: InfraBackendLibs
|
||||
libs: InfraBackendLibs,
|
||||
{ featureFlags }: InfraConfig
|
||||
) {
|
||||
if (!featureFlags.logThresholdAlertRuleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!alertingPlugin) {
|
||||
throw new Error(
|
||||
'Cannot register log threshold alert type. Both the actions and alerting plugins need to be enabled.'
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* 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 { MetricAnomalyParams } from '../../../../common/alerting/metrics';
|
||||
import { getMetricsHostsAnomalies, getMetricK8sAnomalies } from '../../infra_ml';
|
||||
import { MlSystem, MlAnomalyDetectors } from '../../../types';
|
||||
|
||||
type ConditionParams = Omit<MetricAnomalyParams, 'alertInterval'> & {
|
||||
spaceId: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
mlSystem: MlSystem;
|
||||
mlAnomalyDetectors: MlAnomalyDetectors;
|
||||
};
|
||||
|
||||
export const evaluateCondition = async ({
|
||||
nodeType,
|
||||
spaceId,
|
||||
sourceId,
|
||||
mlSystem,
|
||||
mlAnomalyDetectors,
|
||||
startTime,
|
||||
endTime,
|
||||
metric,
|
||||
threshold,
|
||||
influencerFilter,
|
||||
}: ConditionParams) => {
|
||||
const getAnomalies = nodeType === 'k8s' ? getMetricK8sAnomalies : getMetricsHostsAnomalies;
|
||||
|
||||
const result = await getAnomalies({
|
||||
context: {
|
||||
spaceId,
|
||||
mlSystem,
|
||||
mlAnomalyDetectors,
|
||||
},
|
||||
sourceId: sourceId ?? 'default',
|
||||
anomalyThreshold: threshold,
|
||||
startTime,
|
||||
endTime,
|
||||
metric,
|
||||
sort: { field: 'anomalyScore', direction: 'desc' },
|
||||
pagination: { pageSize: 100 },
|
||||
influencerFilter,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { first } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertInstanceContext as AlertContext,
|
||||
AlertInstanceState as AlertState,
|
||||
} from '@kbn/alerting-plugin/common';
|
||||
import { RuleExecutorOptions } from '@kbn/alerting-plugin/server';
|
||||
import { MlPluginSetup } from '@kbn/ml-plugin/server';
|
||||
import { AlertStates, MetricAnomalyParams } from '../../../../common/alerting/metrics';
|
||||
import { getIntervalInSeconds } from '../../../../common/utils/get_interval_in_seconds';
|
||||
import { MappedAnomalyHit } from '../../infra_ml';
|
||||
import { InfraBackendLibs } from '../../infra_types';
|
||||
import { stateToAlertMessage } from '../common/messages';
|
||||
import { evaluateCondition } from './evaluate_condition';
|
||||
import { MetricAnomalyAllowedActionGroups } from './register_metric_anomaly_rule_type';
|
||||
|
||||
export const createMetricAnomalyExecutor =
|
||||
(_libs: InfraBackendLibs, ml?: MlPluginSetup) =>
|
||||
async ({
|
||||
services,
|
||||
params,
|
||||
startedAt,
|
||||
}: RuleExecutorOptions<
|
||||
/**
|
||||
* TODO: Remove this use of `any` by utilizing a proper type
|
||||
*/
|
||||
Record<string, any>,
|
||||
Record<string, any>,
|
||||
AlertState,
|
||||
AlertContext,
|
||||
MetricAnomalyAllowedActionGroups
|
||||
>) => {
|
||||
if (!ml) {
|
||||
return { state: {} };
|
||||
}
|
||||
const request = {} as KibanaRequest;
|
||||
const mlSystem = ml.mlSystemProvider(request, services.savedObjectsClient);
|
||||
const mlAnomalyDetectors = ml.anomalyDetectorsProvider(request, services.savedObjectsClient);
|
||||
|
||||
const { metric, alertInterval, influencerFilter, sourceId, spaceId, nodeType, threshold } =
|
||||
params as MetricAnomalyParams;
|
||||
|
||||
const bucketInterval = getIntervalInSeconds('15m') * 1000;
|
||||
const alertIntervalInMs = getIntervalInSeconds(alertInterval ?? '1m') * 1000;
|
||||
|
||||
const endTime = startedAt.getTime();
|
||||
// Anomalies are bucketed at :00, :15, :30, :45 minutes every hour
|
||||
const previousBucketStartTime = endTime - (endTime % bucketInterval);
|
||||
|
||||
// If the alert interval is less than 15m, make sure that it actually queries an anomaly bucket
|
||||
const startTime = Math.min(endTime - alertIntervalInMs, previousBucketStartTime);
|
||||
|
||||
const { data } = await evaluateCondition({
|
||||
sourceId: sourceId ?? 'default',
|
||||
spaceId: spaceId ?? 'default',
|
||||
mlSystem,
|
||||
mlAnomalyDetectors,
|
||||
startTime,
|
||||
endTime,
|
||||
metric,
|
||||
threshold,
|
||||
nodeType,
|
||||
influencerFilter,
|
||||
});
|
||||
|
||||
const shouldAlertFire = data.length > 0;
|
||||
|
||||
if (shouldAlertFire) {
|
||||
const {
|
||||
startTime: anomalyStartTime,
|
||||
anomalyScore,
|
||||
actual,
|
||||
typical,
|
||||
influencers,
|
||||
} = first(data as MappedAnomalyHit[])!;
|
||||
const alert = services.alertFactory.create(`${nodeType}-${metric}`);
|
||||
|
||||
alert.scheduleActions(FIRED_ACTIONS_ID, {
|
||||
alertState: stateToAlertMessage[AlertStates.ALERT],
|
||||
timestamp: moment(anomalyStartTime).toISOString(),
|
||||
anomalyScore,
|
||||
actual,
|
||||
typical,
|
||||
metric: metricNameMap[metric],
|
||||
summary: generateSummaryMessage(actual, typical),
|
||||
influencers: influencers.join(', '),
|
||||
});
|
||||
}
|
||||
|
||||
return { state: {} };
|
||||
};
|
||||
|
||||
export const FIRED_ACTIONS_ID = 'metrics.anomaly.fired';
|
||||
export const FIRED_ACTIONS: ActionGroup<typeof FIRED_ACTIONS_ID> = {
|
||||
id: FIRED_ACTIONS_ID,
|
||||
name: i18n.translate('xpack.infra.metrics.alerting.anomaly.fired', {
|
||||
defaultMessage: 'Fired',
|
||||
}),
|
||||
};
|
||||
|
||||
const generateSummaryMessage = (actual: number, typical: number) => {
|
||||
const differential = (Math.max(actual, typical) / Math.min(actual, typical))
|
||||
.toFixed(1)
|
||||
.replace('.0', '');
|
||||
if (actual > typical) {
|
||||
return i18n.translate('xpack.infra.metrics.alerting.anomaly.summaryHigher', {
|
||||
defaultMessage: '{differential}x higher',
|
||||
values: {
|
||||
differential,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return i18n.translate('xpack.infra.metrics.alerting.anomaly.summaryLower', {
|
||||
defaultMessage: '{differential}x lower',
|
||||
values: {
|
||||
differential,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const metricNameMap = {
|
||||
memory_usage: i18n.translate('xpack.infra.metrics.alerting.anomaly.memoryUsage', {
|
||||
defaultMessage: 'Memory usage',
|
||||
}),
|
||||
network_in: i18n.translate('xpack.infra.metrics.alerting.anomaly.networkIn', {
|
||||
defaultMessage: 'Network in',
|
||||
}),
|
||||
network_out: i18n.translate('xpack.infra.metrics.alerting.anomaly.networkOut', {
|
||||
defaultMessage: 'Network out',
|
||||
}),
|
||||
};
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* 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 { Unit } from '@kbn/datemath';
|
||||
import { countBy } from 'lodash';
|
||||
import {
|
||||
isTooManyBucketsPreviewException,
|
||||
MetricAnomalyParams,
|
||||
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION,
|
||||
} from '../../../../common/alerting/metrics';
|
||||
import { getIntervalInSeconds } from '../../../../common/utils/get_interval_in_seconds';
|
||||
import { MlAnomalyDetectors, MlSystem } from '../../../types';
|
||||
import { MappedAnomalyHit } from '../../infra_ml';
|
||||
import { evaluateCondition } from './evaluate_condition';
|
||||
|
||||
interface PreviewMetricAnomalyAlertParams {
|
||||
mlSystem: MlSystem;
|
||||
mlAnomalyDetectors: MlAnomalyDetectors;
|
||||
spaceId: string;
|
||||
params: MetricAnomalyParams;
|
||||
sourceId: string;
|
||||
lookback: Unit;
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
alertOnNoData: boolean;
|
||||
alertNotifyWhen: string;
|
||||
}
|
||||
|
||||
export const previewMetricAnomalyAlert = async ({
|
||||
mlSystem,
|
||||
mlAnomalyDetectors,
|
||||
spaceId,
|
||||
params,
|
||||
sourceId,
|
||||
lookback,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertNotifyWhen,
|
||||
}: PreviewMetricAnomalyAlertParams) => {
|
||||
const { metric, threshold, influencerFilter, nodeType } = params as MetricAnomalyParams;
|
||||
|
||||
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||
const throttleIntervalInSeconds = getIntervalInSeconds(alertThrottle);
|
||||
|
||||
const lookbackInterval = `1${lookback}`;
|
||||
const lookbackIntervalInSeconds = getIntervalInSeconds(lookbackInterval);
|
||||
const endTime = Date.now();
|
||||
const startTime = endTime - lookbackIntervalInSeconds * 1000;
|
||||
|
||||
const numberOfExecutions = Math.floor(lookbackIntervalInSeconds / alertIntervalInSeconds);
|
||||
const bucketIntervalInSeconds = getIntervalInSeconds('15m');
|
||||
const bucketsPerExecution = Math.max(
|
||||
1,
|
||||
Math.floor(alertIntervalInSeconds / bucketIntervalInSeconds)
|
||||
);
|
||||
|
||||
try {
|
||||
let anomalies: MappedAnomalyHit[] = [];
|
||||
const { data } = await evaluateCondition({
|
||||
nodeType,
|
||||
spaceId,
|
||||
sourceId,
|
||||
mlSystem,
|
||||
mlAnomalyDetectors,
|
||||
startTime,
|
||||
endTime,
|
||||
metric,
|
||||
threshold,
|
||||
influencerFilter,
|
||||
});
|
||||
anomalies = [...anomalies, ...data];
|
||||
|
||||
const anomaliesByTime = countBy(anomalies, ({ startTime: anomStartTime }) => anomStartTime);
|
||||
|
||||
let numberOfTimesFired = 0;
|
||||
let numberOfNotifications = 0;
|
||||
let throttleTracker = 0;
|
||||
let previousActionGroup: string | null = null;
|
||||
const notifyWithThrottle = (actionGroup: string) => {
|
||||
if (alertNotifyWhen === 'onActionGroupChange') {
|
||||
if (previousActionGroup !== actionGroup) numberOfNotifications++;
|
||||
} else if (alertNotifyWhen === 'onThrottleInterval') {
|
||||
if (throttleTracker === 0) numberOfNotifications++;
|
||||
throttleTracker += alertIntervalInSeconds;
|
||||
} else {
|
||||
numberOfNotifications++;
|
||||
}
|
||||
previousActionGroup = actionGroup;
|
||||
};
|
||||
// Mock each alert evaluation
|
||||
for (let i = 0; i < numberOfExecutions; i++) {
|
||||
const executionTime = startTime + alertIntervalInSeconds * 1000 * i;
|
||||
// Get an array of bucket times this mock alert evaluation will be looking at
|
||||
// Anomalies are bucketed at :00, :15, :30, :45 minutes every hour,
|
||||
// so this is an array of how many of those times occurred between this evaluation
|
||||
// and the previous one
|
||||
const bucketsLookedAt = Array.from(Array(bucketsPerExecution), (_, idx) => {
|
||||
const previousBucketStartTime =
|
||||
executionTime -
|
||||
(executionTime % (bucketIntervalInSeconds * 1000)) -
|
||||
idx * bucketIntervalInSeconds * 1000;
|
||||
return previousBucketStartTime;
|
||||
});
|
||||
const anomaliesDetectedInBuckets = bucketsLookedAt.some((bucketTime) =>
|
||||
Reflect.has(anomaliesByTime, bucketTime)
|
||||
);
|
||||
|
||||
if (anomaliesDetectedInBuckets) {
|
||||
numberOfTimesFired++;
|
||||
notifyWithThrottle('fired');
|
||||
} else {
|
||||
previousActionGroup = 'recovered';
|
||||
if (throttleTracker > 0) {
|
||||
throttleTracker += alertIntervalInSeconds;
|
||||
}
|
||||
}
|
||||
if (throttleTracker >= throttleIntervalInSeconds) {
|
||||
throttleTracker = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return { fired: numberOfTimesFired, notifications: numberOfNotifications };
|
||||
} catch (e) {
|
||||
if (!isTooManyBucketsPreviewException(e)) throw e;
|
||||
const { maxBuckets } = e;
|
||||
throw new Error(`${TOO_MANY_BUCKETS_PREVIEW_EXCEPTION}:${maxBuckets}`);
|
||||
}
|
||||
};
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
* 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 { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { MlPluginSetup } from '@kbn/ml-plugin/server';
|
||||
import {
|
||||
RuleType,
|
||||
AlertInstanceState as AlertState,
|
||||
AlertInstanceContext as AlertContext,
|
||||
GetViewInAppRelativeUrlFnOpts,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { RecoveredActionGroupId } from '@kbn/alerting-plugin/common';
|
||||
import { observabilityPaths } from '@kbn/observability-plugin/common';
|
||||
import { O11Y_AAD_FIELDS } from '../../../../common/constants';
|
||||
import {
|
||||
createMetricAnomalyExecutor,
|
||||
FIRED_ACTIONS,
|
||||
FIRED_ACTIONS_ID,
|
||||
} from './metric_anomaly_executor';
|
||||
import { METRIC_ANOMALY_ALERT_TYPE_ID } from '../../../../common/alerting/metrics';
|
||||
import { InfraBackendLibs } from '../../infra_types';
|
||||
import { oneOfLiterals, validateIsStringElasticsearchJSONFilter } from '../common/utils';
|
||||
import { alertStateActionVariableDescription } from '../common/messages';
|
||||
|
||||
export type MetricAnomalyAllowedActionGroups = typeof FIRED_ACTIONS_ID;
|
||||
|
||||
export const registerMetricAnomalyRuleType = (
|
||||
libs: InfraBackendLibs,
|
||||
ml?: MlPluginSetup
|
||||
): RuleType<
|
||||
/**
|
||||
* TODO: Remove this use of `any` by utilizing a proper type
|
||||
*/
|
||||
Record<string, any>,
|
||||
never, // Only use if defining useSavedObjectReferences hook
|
||||
Record<string, any>,
|
||||
AlertState,
|
||||
AlertContext,
|
||||
MetricAnomalyAllowedActionGroups,
|
||||
RecoveredActionGroupId
|
||||
> => ({
|
||||
id: METRIC_ANOMALY_ALERT_TYPE_ID,
|
||||
name: i18n.translate('xpack.infra.metrics.anomaly.alertName', {
|
||||
defaultMessage: 'Infrastructure anomaly',
|
||||
}),
|
||||
validate: {
|
||||
params: schema.object(
|
||||
{
|
||||
nodeType: oneOfLiterals(['hosts', 'k8s']),
|
||||
alertInterval: schema.string(),
|
||||
metric: oneOfLiterals(['memory_usage', 'network_in', 'network_out']),
|
||||
threshold: schema.number(),
|
||||
filterQuery: schema.maybe(
|
||||
schema.string({ validate: validateIsStringElasticsearchJSONFilter })
|
||||
),
|
||||
sourceId: schema.string(),
|
||||
spaceId: schema.string(),
|
||||
},
|
||||
{ unknowns: 'allow' }
|
||||
),
|
||||
},
|
||||
defaultActionGroupId: FIRED_ACTIONS_ID,
|
||||
actionGroups: [FIRED_ACTIONS],
|
||||
category: DEFAULT_APP_CATEGORIES.observability.id,
|
||||
producer: 'infrastructure',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
executor: createMetricAnomalyExecutor(libs, ml),
|
||||
fieldsForAAD: O11Y_AAD_FIELDS,
|
||||
actionVariables: {
|
||||
context: [
|
||||
{ name: 'alertState', description: alertStateActionVariableDescription },
|
||||
{
|
||||
name: 'metric',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyMetricDescription', {
|
||||
defaultMessage: 'The metric name in the specified condition.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'timestamp',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyTimestampDescription', {
|
||||
defaultMessage: 'A timestamp of when the anomaly was detected.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'anomalyScore',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyScoreDescription', {
|
||||
defaultMessage: 'The exact severity score of the detected anomaly.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'actual',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyActualDescription', {
|
||||
defaultMessage: 'The actual value of the monitored metric at the time of the anomaly.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'typical',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyTypicalDescription', {
|
||||
defaultMessage: 'The typical value of the monitored metric at the time of the anomaly.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'summary',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalySummaryDescription', {
|
||||
defaultMessage: 'A description of the anomaly, e.g. "2x higher."',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'influencers',
|
||||
description: i18n.translate('xpack.infra.metrics.alerting.anomalyInfluencersDescription', {
|
||||
defaultMessage: 'A list of node names that influenced the anomaly.',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
getViewInAppRelativeUrl: ({ rule }: GetViewInAppRelativeUrlFnOpts<{}>) =>
|
||||
observabilityPaths.ruleDetails(rule.id),
|
||||
});
|
|
@ -1903,6 +1903,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
|
|||
logsUIEnabled: true,
|
||||
metricsExplorerEnabled: true,
|
||||
osqueryEnabled: true,
|
||||
inventoryThresholdAlertRuleEnabled: true,
|
||||
metricThresholdAlertRuleEnabled: true,
|
||||
logThresholdAlertRuleEnabled: true,
|
||||
},
|
||||
enabled: true,
|
||||
sources,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
RuleType,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { observabilityPaths } from '@kbn/observability-plugin/common';
|
||||
import type { InfraConfig } from '../../../../common/plugin_config_types';
|
||||
import { Comparator, METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics';
|
||||
import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api';
|
||||
import { InfraBackendLibs } from '../../infra_types';
|
||||
|
@ -56,8 +57,13 @@ export type MetricThresholdAlertType = Omit<RuleType, 'ActionGroupIdsOf'> & {
|
|||
|
||||
export async function registerMetricThresholdRuleType(
|
||||
alertingPlugin: PluginSetupContract,
|
||||
libs: InfraBackendLibs
|
||||
libs: InfraBackendLibs,
|
||||
{ featureFlags }: InfraConfig
|
||||
) {
|
||||
if (!featureFlags.metricThresholdAlertRuleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const baseCriterion = {
|
||||
threshold: schema.arrayOf(schema.number()),
|
||||
comparator: oneOfLiterals(Object.values(Comparator)),
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
|
||||
import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils';
|
||||
import { type IRuleTypeAlerts, PluginSetupContract } from '@kbn/alerting-plugin/server';
|
||||
import { MlPluginSetup } from '@kbn/ml-plugin/server';
|
||||
import { registerMetricThresholdRuleType } from './metric_threshold/register_metric_threshold_rule_type';
|
||||
import { registerMetricInventoryThresholdRuleType } from './inventory_metric_threshold/register_inventory_metric_threshold_rule_type';
|
||||
import { registerMetricAnomalyRuleType } from './metric_anomaly/register_metric_anomaly_rule_type';
|
||||
import { registerInventoryThresholdRuleType } from './inventory_metric_threshold/register_inventory_metric_threshold_rule_type';
|
||||
import { registerLogThresholdRuleType } from './log_threshold/register_log_threshold_rule_type';
|
||||
import { InfraBackendLibs } from '../infra_types';
|
||||
import type { InfraConfig } from '../../types';
|
||||
|
||||
export const LOGS_RULES_ALERT_CONTEXT = 'observability.logs';
|
||||
// Defines which alerts-as-data index logs rules will use
|
||||
|
@ -35,18 +34,16 @@ export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts = {
|
|||
const registerRuleTypes = (
|
||||
alertingPlugin: PluginSetupContract,
|
||||
libs: InfraBackendLibs,
|
||||
ml?: MlPluginSetup
|
||||
config: InfraConfig
|
||||
) => {
|
||||
if (alertingPlugin) {
|
||||
alertingPlugin.registerType(registerMetricAnomalyRuleType(libs, ml));
|
||||
|
||||
const registerFns = [
|
||||
registerLogThresholdRuleType,
|
||||
registerMetricInventoryThresholdRuleType,
|
||||
registerInventoryThresholdRuleType,
|
||||
registerMetricThresholdRuleType,
|
||||
];
|
||||
registerFns.forEach((fn) => {
|
||||
fn(alertingPlugin, libs);
|
||||
fn(alertingPlugin, libs, config);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -130,6 +130,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
|
|||
logsUIEnabled: true,
|
||||
metricsExplorerEnabled: true,
|
||||
osqueryEnabled: true,
|
||||
inventoryThresholdAlertRuleEnabled: true,
|
||||
metricThresholdAlertRuleEnabled: true,
|
||||
logThresholdAlertRuleEnabled: true,
|
||||
},
|
||||
sources,
|
||||
enabled: true,
|
||||
|
|
|
@ -83,7 +83,7 @@ export const config: PluginConfigDescriptor<InfraConfig> = {
|
|||
featureFlags: schema.object({
|
||||
customThresholdAlertsEnabled: offeringBasedSchema({
|
||||
traditional: schema.boolean({ defaultValue: false }),
|
||||
serverless: schema.boolean({ defaultValue: true }),
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
logsUIEnabled: offeringBasedSchema({
|
||||
traditional: schema.boolean({ defaultValue: true }),
|
||||
|
@ -97,6 +97,18 @@ export const config: PluginConfigDescriptor<InfraConfig> = {
|
|||
traditional: schema.boolean({ defaultValue: true }),
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
inventoryThresholdAlertRuleEnabled: offeringBasedSchema({
|
||||
traditional: schema.boolean({ defaultValue: true }),
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
metricThresholdAlertRuleEnabled: offeringBasedSchema({
|
||||
traditional: schema.boolean({ defaultValue: true }),
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
logThresholdAlertRuleEnabled: offeringBasedSchema({
|
||||
traditional: schema.boolean({ defaultValue: true }),
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
deprecations: configDeprecations,
|
||||
|
@ -238,7 +250,7 @@ export class InfraServerPlugin
|
|||
}
|
||||
|
||||
initInfraServer(this.libs);
|
||||
registerRuleTypes(plugins.alerting, this.libs, plugins.ml);
|
||||
registerRuleTypes(plugins.alerting, this.libs, this.config);
|
||||
|
||||
core.http.registerRouteHandlerContext<InfraPluginRequestHandlerContext, 'infra'>(
|
||||
'infra',
|
||||
|
|
|
@ -16,8 +16,7 @@ export const initGetLogAlertsChartPreviewDataRoute = ({
|
|||
framework,
|
||||
getStartServices,
|
||||
}: Pick<InfraBackendLibs, 'framework' | 'getStartServices'>) => {
|
||||
// Replace with the corresponding logs alert rule feature flag
|
||||
if (!framework.config.featureFlags.logsUIEnabled) {
|
||||
if (!framework.config.featureFlags.logThresholdAlertRuleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,6 @@ export enum MetricsExplorerChartType {
|
|||
export enum InfraRuleType {
|
||||
MetricThreshold = 'metrics.alert.threshold',
|
||||
InventoryThreshold = 'metrics.alert.inventory.threshold',
|
||||
Anomaly = 'metrics.alert.anomaly',
|
||||
}
|
||||
|
||||
export enum AlertStates {
|
||||
|
|
|
@ -14,7 +14,6 @@ import { TimeUnitChar } from '../../../../common';
|
|||
export enum InfraRuleType {
|
||||
MetricThreshold = 'metrics.alert.threshold',
|
||||
InventoryThreshold = 'metrics.alert.inventory.threshold',
|
||||
Anomaly = 'metrics.alert.anomaly',
|
||||
}
|
||||
|
||||
export enum AlertStates {
|
||||
|
|
|
@ -18469,8 +18469,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.customEquationEditor.fieldLabel": "Champ {name}",
|
||||
"xpack.infra.metrics.alertFlyout.customEquationEditor.filterLabel": "Filtre KQL {name}",
|
||||
"xpack.infra.metrics.alertFlyout.ofExpression.helpTextDetail": "Vous ne trouvez pas un indicateur ? {documentationLink}.",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryHigher": "{differential}x plus élevé",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryLower": "{differential}x plus bas",
|
||||
"xpack.infra.metrics.alerting.threshold.errorAlertReason": "Elasticsearch a échoué lors de l'interrogation des données pour {metric}",
|
||||
"xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric} est {currentValue} dans les dernières {duration}{group}. Alerte lorsque {comparator} {threshold}.",
|
||||
"xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric} n'a signalé aucune donnée dans les dernières {interval} {group}",
|
||||
|
@ -19051,12 +19049,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.alertOnGroupDisappear": "Me prévenir si un groupe cesse de signaler les données",
|
||||
"xpack.infra.metrics.alertFlyout.alertOnNoData": "M'alerter s'il n'y a aucune donnée",
|
||||
"xpack.infra.metrics.alertFlyout.alertPerRedundantFilterError.docsLink": "les documents",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpText": "Limitez la portée de votre déclenchement d'alerte aux anomalies influencées par certains nœuds.",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpTextExample": "Par exemple : \"my-node-1\" ou \"my-node-*\"",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyInfluencerFilterPlaceholder": "Tout",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.memoryUsage": "Utilisation mémoire",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkIn": "Entrée réseau",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkOut": "Sortie réseau",
|
||||
"xpack.infra.metrics.alertFlyout.conditions": "Conditions",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerHelpText": "Créer une alerte pour chaque valeur unique. Par exemple : \"host.id\" ou \"cloud.region\".",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerText": "Regrouper les alertes par (facultatif)",
|
||||
|
@ -19076,7 +19068,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.error.equation.invalidCharacters": "Le champ d'équation prend en charge uniquement les caractères suivants : A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, =",
|
||||
"xpack.infra.metrics.alertFlyout.error.invalidFilterQuery": "La requête de filtre n'est pas valide.",
|
||||
"xpack.infra.metrics.alertFlyout.error.metricRequired": "L'indicateur est requis.",
|
||||
"xpack.infra.metrics.alertFlyout.error.mlCapabilitiesRequired": "Impossible de créer une alerte d'anomalie lors que le Machine Learning est désactivé.",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdRequired": "Le seuil est requis.",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdTypeRequired": "Les seuils doivent contenir un nombre valide.",
|
||||
"xpack.infra.metrics.alertFlyout.error.timeRequred": "La taille de temps est requise.",
|
||||
|
@ -19086,13 +19077,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.expression.metric.popoverTitle": "Indicateur",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.selectFieldLabel": "Sélectionner un indicateur",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.whenLabel": "Quand",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.criticalLabel": "Critique",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.descriptionLabel": "Le score de sévérité est supérieur à",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.majorLabel": "Majeur",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.minorLabel": "Mineur",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.popoverTitle": "Score de sévérité",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.warningLabel": "Avertissement",
|
||||
"xpack.infra.metrics.alertFlyout.filterByNodeLabel": "Filtrer par nœud",
|
||||
"xpack.infra.metrics.alertFlyout.filterHelpText": "Utilisez une expression KQL pour limiter la portée de votre déclenchement d'alerte.",
|
||||
"xpack.infra.metrics.alertFlyout.filterLabel": "Filtre (facultatif)",
|
||||
"xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "Activez cette option pour déclencher l’action si un groupe précédemment détecté cesse de signaler des résultats. Ce n’est pas recommandé pour les infrastructures à montée en charge dynamique qui peuvent rapidement lancer ou stopper des nœuds automatiquement.",
|
||||
|
@ -19104,18 +19088,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.warningThreshold": "Avertissement",
|
||||
"xpack.infra.metrics.alerting.alertDetailUrlActionVariableDescription": "Lien vers l’affichage de résolution des problèmes d’alerte pour voir plus de contextes et de détails. La chaîne sera vide si server.publicBaseUrl n'est pas configuré.",
|
||||
"xpack.infra.metrics.alerting.alertStateActionVariableDescription": "État actuel de l'alerte",
|
||||
"xpack.infra.metrics.alerting.anomaly.defaultActionMessage": "\\{\\{alertName\\}\\} est à l'état \\{\\{context.alertState\\}\\}\n\n\\{\\{context.metric\\}\\} était \\{\\{context.summary\\}\\} que la normale à \\{\\{context.timestamp\\}\\}\n\nValeur typique : \\{\\{context.typical\\}\\}\nValeur réelle : \\{\\{context.actual\\}\\}\n",
|
||||
"xpack.infra.metrics.alerting.anomaly.fired": "Déclenché",
|
||||
"xpack.infra.metrics.alerting.anomaly.memoryUsage": "Utilisation mémoire",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkIn": "Entrée réseau",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkOut": "Sortie réseau",
|
||||
"xpack.infra.metrics.alerting.anomalyActualDescription": "Valeur réelle de l'indicateur monitoré au moment de l'anomalie.",
|
||||
"xpack.infra.metrics.alerting.anomalyInfluencersDescription": "Liste des noms de nœuds ayant influencé l'anomalie.",
|
||||
"xpack.infra.metrics.alerting.anomalyMetricDescription": "Nom de l'indicateur dans la condition spécifiée.",
|
||||
"xpack.infra.metrics.alerting.anomalyScoreDescription": "Score de sévérité exact de l'anomalie détectée.",
|
||||
"xpack.infra.metrics.alerting.anomalySummaryDescription": "Description de l'anomalie, par ex. \"2 x plus élevé.\"",
|
||||
"xpack.infra.metrics.alerting.anomalyTimestampDescription": "Horodatage du moment où l'anomalie a été détectée.",
|
||||
"xpack.infra.metrics.alerting.anomalyTypicalDescription": "Valeur typique de l'indicateur monitoré au moment de l'anomalie.",
|
||||
"xpack.infra.metrics.alerting.cloudActionVariableDescription": "Objet cloud défini par ECS s'il est disponible dans la source.",
|
||||
"xpack.infra.metrics.alerting.containerActionVariableDescription": "Objet conteneur défini par ECS s'il est disponible dans la source.",
|
||||
"xpack.infra.metrics.alerting.groupActionVariableDescription": "Nom des données de reporting des groupes. Pour accéder à chaque clé de groupe, utilisez context.groupByKeys.",
|
||||
|
@ -19154,8 +19126,6 @@
|
|||
"xpack.infra.metrics.alerting.valueActionVariableDescription": "Valeur de l'indicateur dans la condition spécifiée. Utilisation : (ctx.value.condition0, ctx.value.condition1, etc...).",
|
||||
"xpack.infra.metrics.alerting.viewInAppUrlActionVariableDescription": "Lien vers la source de l’alerte",
|
||||
"xpack.infra.metrics.alertName": "Seuil de l'indicateur",
|
||||
"xpack.infra.metrics.anomaly.alertFlyout.alertDescription": "Alerte lorsque le score d'anomalie dépasse un seuil défini.",
|
||||
"xpack.infra.metrics.anomaly.alertName": "Anomalie d'infrastructure",
|
||||
"xpack.infra.metrics.emptyViewDescription": "Essayez d'ajuster vos heures ou votre filtre.",
|
||||
"xpack.infra.metrics.emptyViewTitle": "Il n'y a aucune donnée à afficher.",
|
||||
"xpack.infra.metrics.expressionItems.components.closablePopoverTitle.closeLabel": "Fermer",
|
||||
|
|
|
@ -18483,8 +18483,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.customEquationEditor.fieldLabel": "フィールド{name}",
|
||||
"xpack.infra.metrics.alertFlyout.customEquationEditor.filterLabel": "KQLフィルター{name}",
|
||||
"xpack.infra.metrics.alertFlyout.ofExpression.helpTextDetail": "メトリックが見つからない場合は、{documentationLink}。",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryHigher": "{differential}x高い",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryLower": "{differential}x低い",
|
||||
"xpack.infra.metrics.alerting.threshold.errorAlertReason": "{metric}のデータのクエリを試行しているときに、Elasticsearchが失敗しました",
|
||||
"xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric}は最後の{duration}{group}の{currentValue}です。{comparator} {threshold}のときにアラートを通知します。",
|
||||
"xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric}は最後の{interval}{group}でデータがないことを報告しました",
|
||||
|
@ -19065,12 +19063,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.alertOnGroupDisappear": "グループがデータのレポートを停止する場合にアラートで通知する",
|
||||
"xpack.infra.metrics.alertFlyout.alertOnNoData": "データがない場合に通知する",
|
||||
"xpack.infra.metrics.alertFlyout.alertPerRedundantFilterError.docsLink": "ドキュメント",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpText": "アラートトリガーの範囲を、特定のノードの影響を受ける異常に制限します。",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpTextExample": "例:「my-node-1」または「my-node-*」",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyInfluencerFilterPlaceholder": "すべて",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.memoryUsage": "メモリー使用状況",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkIn": "内向きのネットワーク",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkOut": "外向きのネットワーク",
|
||||
"xpack.infra.metrics.alertFlyout.conditions": "条件",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerHelpText": "すべての一意の値についてアラートを作成します。例:「host.id」または「cloud.region」。",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerText": "アラートのグループ化条件(オプション)",
|
||||
|
@ -19090,7 +19082,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.error.equation.invalidCharacters": "等式フィールドでは次の文字のみを使用できます:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=",
|
||||
"xpack.infra.metrics.alertFlyout.error.invalidFilterQuery": "フィルタークエリは無効です。",
|
||||
"xpack.infra.metrics.alertFlyout.error.metricRequired": "メトリックが必要です。",
|
||||
"xpack.infra.metrics.alertFlyout.error.mlCapabilitiesRequired": "機械学習が無効なときには、異常アラートを作成できません。",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdRequired": "しきい値が必要です。",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdTypeRequired": "しきい値には有効な数値を含める必要があります。",
|
||||
"xpack.infra.metrics.alertFlyout.error.timeRequred": "ページサイズが必要です。",
|
||||
|
@ -19100,13 +19091,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.expression.metric.popoverTitle": "メトリック",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.selectFieldLabel": "メトリックを選択",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.whenLabel": "タイミング",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.criticalLabel": "重大",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.descriptionLabel": "重要度スコアが超えています",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.majorLabel": "高",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.minorLabel": "低",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.popoverTitle": "重要度スコア",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.warningLabel": "警告",
|
||||
"xpack.infra.metrics.alertFlyout.filterByNodeLabel": "ノードでフィルタリング",
|
||||
"xpack.infra.metrics.alertFlyout.filterHelpText": "KQL式を使用して、アラートトリガーの範囲を制限します。",
|
||||
"xpack.infra.metrics.alertFlyout.filterLabel": "フィルター(任意)",
|
||||
"xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "以前に検出されたグループが結果を報告しなくなった場合は、これを有効にすると、アクションがトリガーされます。自動的に急速にノードを開始および停止することがある動的に拡張するインフラストラクチャーでは、これは推奨されません。",
|
||||
|
@ -19118,18 +19102,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.warningThreshold": "警告",
|
||||
"xpack.infra.metrics.alerting.alertDetailUrlActionVariableDescription": "アラートトラブルシューティングビューにリンクして、さらに詳しい状況や詳細を確認できます。server.publicBaseUrlが構成されていない場合は、空の文字列になります。",
|
||||
"xpack.infra.metrics.alerting.alertStateActionVariableDescription": "現在のアラートの状態",
|
||||
"xpack.infra.metrics.alerting.anomaly.defaultActionMessage": "\\{\\{alertName\\}\\}は\\{\\{context.alertState\\}\\}の状態です\n\n\\{\\{context.metric\\}\\}は\\{\\{context.timestamp\\}\\}で標準を超える\\{\\{context.summary\\}\\}でした\n\n標準の値:\\{\\{context.typical\\}\\}\n実際の値:\\{\\{context.actual\\}\\}\n",
|
||||
"xpack.infra.metrics.alerting.anomaly.fired": "実行",
|
||||
"xpack.infra.metrics.alerting.anomaly.memoryUsage": "メモリー使用状況",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkIn": "内向きのネットワーク",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkOut": "外向きのネットワーク",
|
||||
"xpack.infra.metrics.alerting.anomalyActualDescription": "異常時に監視されたメトリックの実際の値。",
|
||||
"xpack.infra.metrics.alerting.anomalyInfluencersDescription": "異常に影響したノード名のリスト。",
|
||||
"xpack.infra.metrics.alerting.anomalyMetricDescription": "指定された条件のメトリック名。",
|
||||
"xpack.infra.metrics.alerting.anomalyScoreDescription": "検出された異常の正確な重要度スコア。",
|
||||
"xpack.infra.metrics.alerting.anomalySummaryDescription": "異常の説明。例:「2x高い」",
|
||||
"xpack.infra.metrics.alerting.anomalyTimestampDescription": "異常が検出された時点のタイムスタンプ。",
|
||||
"xpack.infra.metrics.alerting.anomalyTypicalDescription": "異常時に監視されたメトリックの標準の値。",
|
||||
"xpack.infra.metrics.alerting.cloudActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたクラウドオブジェクト。",
|
||||
"xpack.infra.metrics.alerting.containerActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたコンテナーオブジェクト。",
|
||||
"xpack.infra.metrics.alerting.groupActionVariableDescription": "データを報告するグループの名前。各グループキーにアクセスするには、context.groupByKeysを使用します。",
|
||||
|
@ -19168,8 +19140,6 @@
|
|||
"xpack.infra.metrics.alerting.valueActionVariableDescription": "指定された条件のメトリックの値。使用方法:(ctx.value.condition0、ctx.value.condition1など)。",
|
||||
"xpack.infra.metrics.alerting.viewInAppUrlActionVariableDescription": "アラートソースにリンク",
|
||||
"xpack.infra.metrics.alertName": "メトリックしきい値",
|
||||
"xpack.infra.metrics.anomaly.alertFlyout.alertDescription": "異常スコアが定義されたしきい値を超えたときにアラートを発行します。",
|
||||
"xpack.infra.metrics.anomaly.alertName": "インフラストラクチャーの異常",
|
||||
"xpack.infra.metrics.emptyViewDescription": "期間またはフィルターを調整してみてください。",
|
||||
"xpack.infra.metrics.emptyViewTitle": "表示するデータがありません。",
|
||||
"xpack.infra.metrics.expressionItems.components.closablePopoverTitle.closeLabel": "閉じる",
|
||||
|
|
|
@ -18483,8 +18483,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.customEquationEditor.fieldLabel": "字段 {name}",
|
||||
"xpack.infra.metrics.alertFlyout.customEquationEditor.filterLabel": "KQL 筛选 {name}",
|
||||
"xpack.infra.metrics.alertFlyout.ofExpression.helpTextDetail": "找不到指标?{documentationLink}。",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryHigher": "高 {differential} 倍",
|
||||
"xpack.infra.metrics.alerting.anomaly.summaryLower": "低 {differential} 倍",
|
||||
"xpack.infra.metrics.alerting.threshold.errorAlertReason": "Elasticsearch 尝试查询 {metric} 的数据时出现故障",
|
||||
"xpack.infra.metrics.alerting.threshold.firedAlertReason": "过去 {duration}{group},{metric} 为 {currentValue}。{comparator} {threshold} 时告警。",
|
||||
"xpack.infra.metrics.alerting.threshold.noDataAlertReason": "对于 {group},{metric} 在过去 {interval}中未报告数据",
|
||||
|
@ -19065,12 +19063,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.alertOnGroupDisappear": "组停止报告数据时提醒我",
|
||||
"xpack.infra.metrics.alertFlyout.alertOnNoData": "没数据时提醒我",
|
||||
"xpack.infra.metrics.alertFlyout.alertPerRedundantFilterError.docsLink": "文档",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpText": "将告警触发的范围限定在特定节点影响的异常。",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyFilterHelpTextExample": "例如:“my-node-1”或“my-node-*”",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyInfluencerFilterPlaceholder": "所有内容",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.memoryUsage": "内存使用",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkIn": "网络传入",
|
||||
"xpack.infra.metrics.alertFlyout.anomalyJobs.networkOut": "网络传出",
|
||||
"xpack.infra.metrics.alertFlyout.conditions": "条件",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerHelpText": "为每个唯一值创建告警。例如:“host.id”或“cloud.region”。",
|
||||
"xpack.infra.metrics.alertFlyout.createAlertPerText": "告警分组依据(可选)",
|
||||
|
@ -19090,7 +19082,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.error.equation.invalidCharacters": "方程字段仅支持以下字符:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=",
|
||||
"xpack.infra.metrics.alertFlyout.error.invalidFilterQuery": "筛选查询无效。",
|
||||
"xpack.infra.metrics.alertFlyout.error.metricRequired": "“指标”必填。",
|
||||
"xpack.infra.metrics.alertFlyout.error.mlCapabilitiesRequired": "禁用 Machine Learning 时,无法创建异常告警。",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdRequired": "“阈值”必填。",
|
||||
"xpack.infra.metrics.alertFlyout.error.thresholdTypeRequired": "阈值必须包含有效数字。",
|
||||
"xpack.infra.metrics.alertFlyout.error.timeRequred": "“时间大小”必填。",
|
||||
|
@ -19100,13 +19091,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.expression.metric.popoverTitle": "指标",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.selectFieldLabel": "选择指标",
|
||||
"xpack.infra.metrics.alertFlyout.expression.metric.whenLabel": "当",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.criticalLabel": "紧急",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.descriptionLabel": "严重性分数高于",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.majorLabel": "重大",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.minorLabel": "轻微",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.popoverTitle": "严重性分数",
|
||||
"xpack.infra.metrics.alertFlyout.expression.severityScore.warningLabel": "警告",
|
||||
"xpack.infra.metrics.alertFlyout.filterByNodeLabel": "按节点筛选",
|
||||
"xpack.infra.metrics.alertFlyout.filterHelpText": "使用 KQL 表达式限制告警触发的范围。",
|
||||
"xpack.infra.metrics.alertFlyout.filterLabel": "筛选(可选)",
|
||||
"xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "启用此选项可在之前检测的组开始不报告任何数据时触发操作。不建议将此选项用于可能会快速自动启动和停止节点的动态扩展基础架构。",
|
||||
|
@ -19118,18 +19102,6 @@
|
|||
"xpack.infra.metrics.alertFlyout.warningThreshold": "警告",
|
||||
"xpack.infra.metrics.alerting.alertDetailUrlActionVariableDescription": "链接到告警故障排除视图获取进一步的上下文和详情。如果未配置 server.publicBaseUrl,这将为空字符串。",
|
||||
"xpack.infra.metrics.alerting.alertStateActionVariableDescription": "告警的当前状态",
|
||||
"xpack.infra.metrics.alerting.anomaly.defaultActionMessage": "\\{\\{alertName\\}\\} 处于 \\{\\{context.alertState\\}\\} 状态\n\n\\{\\{context.metric\\}\\} 在 \\{\\{context.timestamp\\}\\}比正常\\{\\{context.summary\\}\\}\n\n典型值:\\{\\{context.typical\\}\\}\n实际值:\\{\\{context.actual\\}\\}\n",
|
||||
"xpack.infra.metrics.alerting.anomaly.fired": "已触发",
|
||||
"xpack.infra.metrics.alerting.anomaly.memoryUsage": "内存使用",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkIn": "网络传入",
|
||||
"xpack.infra.metrics.alerting.anomaly.networkOut": "网络传出",
|
||||
"xpack.infra.metrics.alerting.anomalyActualDescription": "在发生异常时受监测指标的实际值。",
|
||||
"xpack.infra.metrics.alerting.anomalyInfluencersDescription": "影响异常的节点名称列表。",
|
||||
"xpack.infra.metrics.alerting.anomalyMetricDescription": "指定条件中的指标名称。",
|
||||
"xpack.infra.metrics.alerting.anomalyScoreDescription": "检测到的异常的确切严重性分数。",
|
||||
"xpack.infra.metrics.alerting.anomalySummaryDescription": "异常的描述,例如“高 2 倍”。",
|
||||
"xpack.infra.metrics.alerting.anomalyTimestampDescription": "检测到异常时的时间戳。",
|
||||
"xpack.infra.metrics.alerting.anomalyTypicalDescription": "在发生异常时受监测指标的典型值。",
|
||||
"xpack.infra.metrics.alerting.cloudActionVariableDescription": "ECS 定义的云对象(如果在源中可用)。",
|
||||
"xpack.infra.metrics.alerting.containerActionVariableDescription": "ECS 定义的容器对象(如果在源中可用)。",
|
||||
"xpack.infra.metrics.alerting.groupActionVariableDescription": "报告数据的组名称。为了访问每个组密钥,请使用 context.groupByKeys。",
|
||||
|
@ -19168,8 +19140,6 @@
|
|||
"xpack.infra.metrics.alerting.valueActionVariableDescription": "指定条件中的指标值。用法:(ctx.value.condition0, ctx.value.condition1, 诸如此类)。",
|
||||
"xpack.infra.metrics.alerting.viewInAppUrlActionVariableDescription": "链接到告警源",
|
||||
"xpack.infra.metrics.alertName": "指标阈值",
|
||||
"xpack.infra.metrics.anomaly.alertFlyout.alertDescription": "当异常分数超过定义的阈值时告警。",
|
||||
"xpack.infra.metrics.anomaly.alertName": "基础架构异常",
|
||||
"xpack.infra.metrics.emptyViewDescription": "尝试调整您的时间或筛选。",
|
||||
"xpack.infra.metrics.emptyViewTitle": "没有可显示的数据。",
|
||||
"xpack.infra.metrics.expressionItems.components.closablePopoverTitle.closeLabel": "关闭",
|
||||
|
|
|
@ -46,7 +46,6 @@ export default function createRegisteredRuleTypeTests({ getService }: FtrProvide
|
|||
'siem.newTermsRule',
|
||||
'siem.notifications',
|
||||
'slo.rules.burnRate',
|
||||
'metrics.alert.anomaly',
|
||||
'logs.alert.document.count',
|
||||
'metrics.alert.inventory.threshold',
|
||||
'metrics.alert.threshold',
|
||||
|
|
|
@ -81,7 +81,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'alerting:apm.transaction_duration',
|
||||
'alerting:apm.transaction_error_rate',
|
||||
'alerting:logs.alert.document.count',
|
||||
'alerting:metrics.alert.anomaly',
|
||||
'alerting:metrics.alert.inventory.threshold',
|
||||
'alerting:metrics.alert.threshold',
|
||||
'alerting:monitoring_alert_cluster_health',
|
||||
|
|
|
@ -18,5 +18,9 @@ export default createTestConfig({
|
|||
// include settings from project controller
|
||||
// https://github.com/elastic/project-controller/blob/main/internal/project/observability/config/elasticsearch.yml
|
||||
esServerArgs: ['xpack.ml.dfa.enabled=false', 'xpack.ml.nlp.enabled=false'],
|
||||
kbnServerArgs: ['--xpack.infra.enabled=true'],
|
||||
kbnServerArgs: [
|
||||
'--xpack.infra.enabled=true',
|
||||
'--xpack.infra.featureFlags.customThresholdAlertsEnabled=true',
|
||||
'--xpack.observability.unsafe.thresholdRule.enabled=true',
|
||||
],
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue