From 0163b50cf6d7900d485a48262f05c317f1aa0941 Mon Sep 17 00:00:00 2001 From: igoristic Date: Fri, 30 Oct 2020 10:50:34 -0400 Subject: [PATCH] [Monitoring] Thread pool rejections alert (#79433) * Thread pool rejections first draft * Split search and write rejections to seperate alerts * Code review feedback * Optimized page loading and bundle size * Increased monitoring bundle limit * Removed server app import into the frontend * Fixed tests and bundle size Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> # Conflicts: # packages/kbn-optimizer/limits.yml --- packages/kbn-optimizer/limits.yml | 2 +- x-pack/plugins/monitoring/common/constants.ts | 177 +++++++++- x-pack/plugins/monitoring/common/enums.ts | 1 + x-pack/plugins/monitoring/common/types.ts | 53 --- .../types.d.ts => common/types/alerts.ts} | 77 ++++- .../monitoring/public/alerts/badge.tsx | 4 +- .../monitoring/public/alerts/callout.tsx | 4 +- .../alerts/components/duration/expression.tsx | 20 +- .../cpu_usage_alert/cpu_usage_alert.tsx | 9 +- .../public/alerts/disk_usage_alert/index.tsx | 10 +- .../public/alerts/filter_alert_states.ts | 2 +- .../alert_param_duration.tsx | 2 +- .../flyout_expressions/alert_param_number.tsx | 36 ++ .../alerts/legacy_alert/legacy_alert.tsx | 11 +- .../public/alerts/lib/replace_tokens.tsx | 2 +- .../alerts/lib/should_show_alert_badge.ts | 2 +- .../alerts/memory_usage_alert/index.tsx | 10 +- .../expression.tsx | 4 +- .../missing_monitoring_data_alert.tsx | 12 +- .../monitoring/public/alerts/panel.tsx | 3 +- .../monitoring/public/alerts/status.tsx | 3 +- .../thread_pool_rejections_alert/index.tsx | 60 ++++ .../public/angular/providers/private.js | 8 +- .../public/components/chart/chart_target.js | 12 +- .../chart/timeseries_visualization.js | 24 +- .../cluster/overview/elasticsearch_panel.js | 4 + .../components/elasticsearch/nodes/nodes.js | 8 +- .../shard_allocation/components/unassigned.js | 4 +- .../lib/has_primary_children.js | 4 +- .../shard_allocation/lib/vents.js | 6 +- .../transformers/indices_by_nodes.js | 17 +- .../transformers/nodes_by_indices.js | 30 +- .../plugins/monitoring/public/legacy_shims.ts | 1 - .../public/lib/calculate_shard_stats.js | 8 +- .../public/lib/get_cluster_from_clusters.js | 6 +- .../monitoring/public/lib/route_init.js | 3 +- x-pack/plugins/monitoring/public/plugin.ts | 61 +++- .../monitoring/public/services/features.js | 6 +- .../monitoring/public/services/title.js | 4 +- .../elasticsearch/node/advanced/index.js | 4 + .../public/views/elasticsearch/node/index.js | 4 + .../public/views/elasticsearch/nodes/index.js | 4 + .../{alerts_common.ts => alert_helpers.ts} | 2 +- .../server/alerts/alerts_factory.test.ts | 5 - .../server/alerts/alerts_factory.ts | 8 +- .../monitoring/server/alerts/base_alert.ts | 23 +- .../server/alerts/cluster_health_alert.ts | 12 +- .../server/alerts/cpu_usage_alert.ts | 47 +-- .../server/alerts/disk_usage_alert.ts | 42 +-- .../elasticsearch_version_mismatch_alert.ts | 16 +- .../plugins/monitoring/server/alerts/index.ts | 4 +- .../alerts/kibana_version_mismatch_alert.ts | 16 +- .../server/alerts/license_expiration_alert.ts | 11 +- .../alerts/logstash_version_mismatch_alert.ts | 16 +- .../server/alerts/memory_usage_alert.ts | 42 +-- .../alerts/missing_monitoring_data_alert.ts | 37 +-- .../server/alerts/nodes_changed_alert.ts | 12 +- .../thread_pool_rejections_alert_base.ts | 312 ++++++++++++++++++ .../thread_pool_search_rejections_alert.ts | 26 ++ .../thread_pool_write_rejections_alert.ts | 26 ++ .../server/lib/alerts/fetch_clusters.ts | 2 +- .../lib/alerts/fetch_cpu_usage_node_stats.ts | 2 +- .../lib/alerts/fetch_disk_usage_node_stats.ts | 2 +- .../server/lib/alerts/fetch_legacy_alerts.ts | 2 +- .../alerts/fetch_memory_usage_node_stats.ts | 2 +- .../alerts/fetch_missing_monitoring_data.ts | 2 +- .../server/lib/alerts/fetch_status.test.ts | 2 +- .../server/lib/alerts/fetch_status.ts | 8 +- .../fetch_thread_pool_rejections_stats.ts | 141 ++++++++ .../server/routes/api/v1/alerts/status.ts | 2 +- 70 files changed, 1149 insertions(+), 395 deletions(-) delete mode 100644 x-pack/plugins/monitoring/common/types.ts rename x-pack/plugins/monitoring/{server/alerts/types.d.ts => common/types/alerts.ts} (66%) create mode 100644 x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_number.tsx create mode 100644 x-pack/plugins/monitoring/public/alerts/thread_pool_rejections_alert/index.tsx rename x-pack/plugins/monitoring/server/alerts/{alerts_common.ts => alert_helpers.ts} (97%) create mode 100644 x-pack/plugins/monitoring/server/alerts/thread_pool_rejections_alert_base.ts create mode 100644 x-pack/plugins/monitoring/server/alerts/thread_pool_search_rejections_alert.ts create mode 100644 x-pack/plugins/monitoring/server/alerts/thread_pool_write_rejections_alert.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index f39972e527128a..c8bd0b01189e8c 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -54,7 +54,7 @@ pageLoadAssetSize: mapsLegacy: 116961 mapsLegacyLicensing: 20214 ml: 82187 - monitoring: 268758 + monitoring: 50000 navigation: 37413 newsfeed: 42228 observability: 89709 diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts index 13b5b52fc88be3..2d649e9e1ff3e8 100644 --- a/x-pack/plugins/monitoring/common/constants.ts +++ b/x-pack/plugins/monitoring/common/constants.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; +import { CommonAlertParamDetail } from './types/alerts'; +import { AlertParamType } from './enums'; + /** * Helper string to add as a tag in every logging call */ @@ -215,15 +219,6 @@ export const REPORTING_SYSTEM_ID = 'reporting'; */ export const TELEMETRY_COLLECTION_INTERVAL = 86400000; -/** - * We want to slowly rollout the migration from watcher-based cluster alerts to - * kibana alerts and we only want to enable the kibana alerts once all - * watcher-based cluster alerts have been migrated so this flag will serve - * as the only way to see the new UI and actually run Kibana alerts. It will - * be false until all alerts have been migrated, then it will be removed - */ -export const KIBANA_CLUSTER_ALERTS_ENABLED = false; - /** * The prefix for all alert types used by monitoring */ @@ -238,6 +233,168 @@ export const ALERT_KIBANA_VERSION_MISMATCH = `${ALERT_PREFIX}alert_kibana_versio export const ALERT_LOGSTASH_VERSION_MISMATCH = `${ALERT_PREFIX}alert_logstash_version_mismatch`; export const ALERT_MEMORY_USAGE = `${ALERT_PREFIX}alert_jvm_memory_usage`; export const ALERT_MISSING_MONITORING_DATA = `${ALERT_PREFIX}alert_missing_monitoring_data`; +export const ALERT_THREAD_POOL_SEARCH_REJECTIONS = `${ALERT_PREFIX}alert_thread_pool_search_rejections`; +export const ALERT_THREAD_POOL_WRITE_REJECTIONS = `${ALERT_PREFIX}alert_thread_pool_write_rejections`; + +/** + * Legacy alerts details/label for server and public use + */ +export const LEGACY_ALERT_DETAILS = { + [ALERT_CLUSTER_HEALTH]: { + label: i18n.translate('xpack.monitoring.alerts.clusterHealth.label', { + defaultMessage: 'Cluster health', + }), + }, + [ALERT_ELASTICSEARCH_VERSION_MISMATCH]: { + label: i18n.translate('xpack.monitoring.alerts.elasticsearchVersionMismatch.label', { + defaultMessage: 'Elasticsearch version mismatch', + }), + }, + [ALERT_KIBANA_VERSION_MISMATCH]: { + label: i18n.translate('xpack.monitoring.alerts.kibanaVersionMismatch.label', { + defaultMessage: 'Kibana version mismatch', + }), + }, + [ALERT_LICENSE_EXPIRATION]: { + label: i18n.translate('xpack.monitoring.alerts.licenseExpiration.label', { + defaultMessage: 'License expiration', + }), + }, + [ALERT_LOGSTASH_VERSION_MISMATCH]: { + label: i18n.translate('xpack.monitoring.alerts.logstashVersionMismatch.label', { + defaultMessage: 'Logstash version mismatch', + }), + }, + [ALERT_NODES_CHANGED]: { + label: i18n.translate('xpack.monitoring.alerts.nodesChanged.label', { + defaultMessage: 'Nodes changed', + }), + }, +}; + +/** + * Alerts details/label for server and public use + */ +export const ALERT_DETAILS = { + [ALERT_CPU_USAGE]: { + label: i18n.translate('xpack.monitoring.alerts.cpuUsage.label', { + defaultMessage: 'CPU Usage', + }), + paramDetails: { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.cpuUsage.paramDetails.threshold.label', { + defaultMessage: `Notify when CPU is over`, + }), + type: AlertParamType.Percentage, + } as CommonAlertParamDetail, + duration: { + label: i18n.translate('xpack.monitoring.alerts.cpuUsage.paramDetails.duration.label', { + defaultMessage: `Look at the average over`, + }), + type: AlertParamType.Duration, + } as CommonAlertParamDetail, + }, + }, + [ALERT_DISK_USAGE]: { + paramDetails: { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.threshold.label', { + defaultMessage: `Notify when disk capacity is over`, + }), + type: AlertParamType.Percentage, + }, + duration: { + label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.duration.label', { + defaultMessage: `Look at the average over`, + }), + type: AlertParamType.Duration, + }, + }, + label: i18n.translate('xpack.monitoring.alerts.diskUsage.label', { + defaultMessage: 'Disk Usage', + }), + }, + [ALERT_MEMORY_USAGE]: { + paramDetails: { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.memoryUsage.paramDetails.threshold.label', { + defaultMessage: `Notify when memory usage is over`, + }), + type: AlertParamType.Percentage, + }, + duration: { + label: i18n.translate('xpack.monitoring.alerts.memoryUsage.paramDetails.duration.label', { + defaultMessage: `Look at the average over`, + }), + type: AlertParamType.Duration, + }, + }, + label: i18n.translate('xpack.monitoring.alerts.memoryUsage.label', { + defaultMessage: 'Memory Usage (JVM)', + }), + }, + [ALERT_MISSING_MONITORING_DATA]: { + paramDetails: { + duration: { + label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.duration.label', { + defaultMessage: `Notify if monitoring data is missing for the last`, + }), + type: AlertParamType.Duration, + } as CommonAlertParamDetail, + limit: { + label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.limit.label', { + defaultMessage: `looking back`, + }), + type: AlertParamType.Duration, + } as CommonAlertParamDetail, + }, + label: i18n.translate('xpack.monitoring.alerts.missingData.label', { + defaultMessage: 'Missing monitoring data', + }), + }, + [ALERT_THREAD_POOL_SEARCH_REJECTIONS]: { + paramDetails: { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.rejection.paramDetails.threshold.label', { + defaultMessage: `Notify when {type} rejection count is over`, + values: { type: 'search' }, + }), + type: AlertParamType.Number, + }, + duration: { + label: i18n.translate('xpack.monitoring.alerts.rejection.paramDetails.duration.label', { + defaultMessage: `In the last`, + }), + type: AlertParamType.Duration, + }, + }, + label: i18n.translate('xpack.monitoring.alerts.threadPoolRejections.label', { + defaultMessage: 'Thread pool {type} rejections', + values: { type: 'search' }, + }), + }, + [ALERT_THREAD_POOL_WRITE_REJECTIONS]: { + paramDetails: { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.rejection.paramDetails.threshold.label', { + defaultMessage: `Notify when {type} rejection count is over`, + values: { type: 'write' }, + }), + type: AlertParamType.Number, + }, + duration: { + label: i18n.translate('xpack.monitoring.alerts.rejection.paramDetails.duration.label', { + defaultMessage: `In the last`, + }), + type: AlertParamType.Duration, + }, + }, + label: i18n.translate('xpack.monitoring.alerts.threadPoolRejections.label', { + defaultMessage: 'Thread pool {type} rejections', + values: { type: 'write' }, + }), + }, +}; /** * A listing of all alert types @@ -253,6 +410,8 @@ export const ALERTS = [ ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_MEMORY_USAGE, ALERT_MISSING_MONITORING_DATA, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ]; /** diff --git a/x-pack/plugins/monitoring/common/enums.ts b/x-pack/plugins/monitoring/common/enums.ts index d4058e9de801ed..b373428bb279b6 100644 --- a/x-pack/plugins/monitoring/common/enums.ts +++ b/x-pack/plugins/monitoring/common/enums.ts @@ -25,6 +25,7 @@ export enum AlertMessageTokenType { export enum AlertParamType { Duration = 'duration', Percentage = 'percentage', + Number = 'number', } export enum SetupModeFeature { diff --git a/x-pack/plugins/monitoring/common/types.ts b/x-pack/plugins/monitoring/common/types.ts deleted file mode 100644 index 825d2e454b3bba..00000000000000 --- a/x-pack/plugins/monitoring/common/types.ts +++ /dev/null @@ -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; - * you may not use this file except in compliance with the Elastic License. - */ -import { Alert } from '../../alerts/common'; -import { AlertParamType } from './enums'; - -export interface CommonBaseAlert { - type: string; - label: string; - paramDetails: CommonAlertParamDetails; - rawAlert: Alert; - isLegacy: boolean; -} - -export interface CommonAlertStatus { - exists: boolean; - enabled: boolean; - states: CommonAlertState[]; - alert: CommonBaseAlert; -} - -export interface CommonAlertState { - firing: boolean; - state: any; - meta: any; -} - -export interface CommonAlertFilter { - nodeUuid?: string; -} - -export interface CommonAlertNodeUuidFilter extends CommonAlertFilter { - nodeUuid: string; -} - -export interface CommonAlertStackProductFilter extends CommonAlertFilter { - stackProduct: string; -} - -export interface CommonAlertParamDetail { - label: string; - type: AlertParamType; -} - -export interface CommonAlertParamDetails { - [name: string]: CommonAlertParamDetail; -} - -export interface CommonAlertParams { - [name: string]: string | number; -} diff --git a/x-pack/plugins/monitoring/server/alerts/types.d.ts b/x-pack/plugins/monitoring/common/types/alerts.ts similarity index 66% rename from x-pack/plugins/monitoring/server/alerts/types.d.ts rename to x-pack/plugins/monitoring/common/types/alerts.ts index 0b346e770a2990..f7a27a1b1a2b00 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -3,8 +3,60 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; -import { AlertInstanceState as BaseAlertInstanceState } from '../../../alerts/server'; + +import { Alert } from '../../../alerts/common'; +import { AlertParamType, AlertMessageTokenType, AlertSeverity } from '../enums'; + +export interface CommonBaseAlert { + type: string; + label: string; + paramDetails: CommonAlertParamDetails; + rawAlert: Alert; + isLegacy: boolean; +} + +export interface CommonAlertStatus { + exists: boolean; + enabled: boolean; + states: CommonAlertState[]; + alert: CommonBaseAlert; +} + +export interface CommonAlertState { + firing: boolean; + state: any; + meta: any; +} + +export interface CommonAlertFilter { + nodeUuid?: string; +} + +export interface CommonAlertNodeUuidFilter extends CommonAlertFilter { + nodeUuid: string; +} + +export interface CommonAlertStackProductFilter extends CommonAlertFilter { + stackProduct: string; +} + +export interface CommonAlertParamDetail { + label: string; + type?: AlertParamType; +} + +export interface CommonAlertParamDetails { + [name: string]: CommonAlertParamDetail | undefined; +} + +export interface CommonAlertParams { + [name: string]: string | number; +} + +export interface ThreadPoolRejectionsAlertParams { + threshold: number; + duration: string; +} export interface AlertEnableAction { id: string; @@ -12,7 +64,9 @@ export interface AlertEnableAction { } export interface AlertInstanceState { - alertStates: Array; + alertStates: Array< + AlertState | AlertCpuUsageState | AlertDiskUsageState | AlertThreadPoolRejectionsState + >; [x: string]: unknown; } @@ -46,6 +100,13 @@ export interface AlertMemoryUsageState extends AlertNodeState { memoryUsage: number; } +export interface AlertThreadPoolRejectionsState extends AlertState { + rejectionCount: number; + type: string; + nodeId: string; + nodeName?: string; +} + export interface AlertUiState { isFiring: boolean; severity: AlertSeverity; @@ -100,6 +161,14 @@ export interface AlertCpuUsageNodeStats extends AlertNodeStats { containerQuota: number; } +export interface AlertThreadPoolRejectionsStats { + clusterUuid: string; + nodeId: string; + nodeName: string; + rejectionCount: number; + ccs?: string; +} + export interface AlertDiskUsageNodeStats extends AlertNodeStats { diskUsage: number; } @@ -121,7 +190,7 @@ export interface AlertData { instanceKey: string; clusterUuid: string; ccs?: string; - shouldFire: boolean; + shouldFire?: boolean; severity: AlertSeverity; meta: any; } diff --git a/x-pack/plugins/monitoring/public/alerts/badge.tsx b/x-pack/plugins/monitoring/public/alerts/badge.tsx index d4e823a194f8ed..4bfecf4380d4be 100644 --- a/x-pack/plugins/monitoring/public/alerts/badge.tsx +++ b/x-pack/plugins/monitoring/public/alerts/badge.tsx @@ -14,11 +14,11 @@ import { EuiFlexItem, EuiText, } from '@elastic/eui'; -import { CommonAlertStatus, CommonAlertState } from '../../common/types'; +import { CommonAlertStatus, CommonAlertState } from '../../common/types/alerts'; import { AlertSeverity } from '../../common/enums'; // @ts-ignore import { formatDateTimeLocal } from '../../common/formatting'; -import { AlertMessage, AlertState } from '../../server/alerts/types'; +import { AlertMessage, AlertState } from '../../common/types/alerts'; import { AlertPanel } from './panel'; import { Legacy } from '../legacy_shims'; import { isInSetupMode } from '../lib/setup_mode'; diff --git a/x-pack/plugins/monitoring/public/alerts/callout.tsx b/x-pack/plugins/monitoring/public/alerts/callout.tsx index 1ddd41c2684565..769d4dc7b256d5 100644 --- a/x-pack/plugins/monitoring/public/alerts/callout.tsx +++ b/x-pack/plugins/monitoring/public/alerts/callout.tsx @@ -7,10 +7,10 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { CommonAlertStatus } from '../../common/types'; +import { CommonAlertStatus } from '../../common/types/alerts'; import { AlertSeverity } from '../../common/enums'; import { replaceTokens } from './lib/replace_tokens'; -import { AlertMessage, AlertState } from '../../server/alerts/types'; +import { AlertMessage, AlertState } from '../../common/types/alerts'; const TYPES = [ { diff --git a/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx index 2df7169efc675f..26593fdd6e7b0d 100644 --- a/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx @@ -6,10 +6,11 @@ import React, { Fragment } from 'react'; import { EuiForm, EuiSpacer } from '@elastic/eui'; -import { CommonAlertParamDetails } from '../../../../common/types'; +import { CommonAlertParamDetails } from '../../../../common/types/alerts'; import { AlertParamDuration } from '../../flyout_expressions/alert_param_duration'; import { AlertParamType } from '../../../../common/enums'; import { AlertParamPercentage } from '../../flyout_expressions/alert_param_percentage'; +import { AlertParamNumber } from '../../flyout_expressions/alert_param_number'; export interface Props { alertParams: { [property: string]: any }; @@ -26,14 +27,14 @@ export const Expression: React.FC = (props) => { const details = paramDetails[alertParamName]; const value = alertParams[alertParamName]; - switch (details.type) { + switch (details?.type) { case AlertParamType.Duration: return ( @@ -43,12 +44,23 @@ export const Expression: React.FC = (props) => { ); + case AlertParamType.Number: + return ( + + ); } }); diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index fb4ecacf57fd69..d15fe6344ec0f2 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -6,20 +6,17 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; -import { ALERT_CPU_USAGE } from '../../../common/constants'; +import { ALERT_CPU_USAGE, ALERT_DETAILS } from '../../../common/constants'; import { validate } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { CpuUsageAlert } from '../../../server/alerts'; export function createCpuUsageAlertType(): AlertTypeModel { - const alert = new CpuUsageAlert(); return { id: ALERT_CPU_USAGE, - name: alert.label, + name: ALERT_DETAILS[ALERT_CPU_USAGE].label, iconClass: 'bell', alertParamsExpression: (props: Props) => ( - + ), validate, defaultActionMessage: '{{context.internalFullMessage}}', diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx index c2abb35612b387..589b374cae32cb 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -10,17 +10,15 @@ import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { DiskUsageAlert } from '../../../server/alerts'; +import { ALERT_DISK_USAGE, ALERT_DETAILS } from '../../../common/constants'; export function createDiskUsageAlertType(): AlertTypeModel { return { - id: DiskUsageAlert.TYPE, - name: DiskUsageAlert.LABEL, + id: ALERT_DISK_USAGE, + name: ALERT_DETAILS[ALERT_DISK_USAGE].label, iconClass: 'bell', alertParamsExpression: (props: Props) => ( - + ), validate, defaultActionMessage: '{{context.internalFullMessage}}', diff --git a/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts b/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts index 63714a6921e3f1..e13ea7de0e2269 100644 --- a/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts +++ b/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CommonAlertState, CommonAlertStatus } from '../../common/types'; +import { CommonAlertState, CommonAlertStatus } from '../../common/types/alerts'; export function filterAlertStates( alerts: { [type: string]: CommonAlertStatus }, diff --git a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx index 862f32efd7361c..4ece1b0c818270 100644 --- a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx +++ b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx @@ -69,7 +69,7 @@ export const AlertParamDuration: React.FC = (props: Props) => { }, [unit, value]); return ( - 0}> + 0}> void; +} +export const AlertParamNumber: React.FC = (props: Props) => { + const { name, label, setAlertParams, errors } = props; + const [value, setValue] = useState(props.value); + return ( + 0}> + { + let newValue = Number(e.target.value); + if (isNaN(newValue)) { + newValue = 0; + } + setValue(newValue); + setAlertParams(name, newValue); + }} + /> + + ); +}; diff --git a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx index f6223d41ab30e9..83201b0512dbba 100644 --- a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx @@ -3,24 +3,21 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiTextColor, EuiSpacer } from '@elastic/eui'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; -import { LEGACY_ALERTS } from '../../../common/constants'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { BY_TYPE } from '../../../server/alerts'; +import { LEGACY_ALERTS, LEGACY_ALERT_DETAILS } from '../../../common/constants'; export function createLegacyAlertTypes(): AlertTypeModel[] { return LEGACY_ALERTS.map((legacyAlert) => { - const alertCls = BY_TYPE[legacyAlert]; - const alert = new alertCls(); return { id: legacyAlert, - name: alert.label, + name: LEGACY_ALERT_DETAILS[legacyAlert].label, iconClass: 'bell', - alertParamsExpression: (props: any) => ( + alertParamsExpression: () => ( diff --git a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx index 02f5703f663820..b8ac69cbae68a4 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx +++ b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx @@ -11,7 +11,7 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertMessageDocLinkToken, -} from '../../../server/alerts/types'; +} from '../../../common/types/alerts'; // @ts-ignore import { formatTimestampToDuration } from '../../../common'; import { CALCULATE_DURATION_UNTIL } from '../../../common/constants'; diff --git a/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts b/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts index 0b95592d92c842..2ec5d1ba8f94a1 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts +++ b/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { isInSetupMode } from '../../lib/setup_mode'; -import { CommonAlertStatus } from '../../../common/types'; +import { CommonAlertStatus } from '../../../common/types/alerts'; import { ISetupModeContext } from '../../components/setup_mode/setup_mode_context'; export function shouldShowAlertBadge( diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index dd60967a3458bf..d3d48d907d02e0 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -10,17 +10,15 @@ import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MemoryUsageAlert } from '../../../server/alerts'; +import { ALERT_MEMORY_USAGE, ALERT_DETAILS } from '../../../common/constants'; export function createMemoryUsageAlertType(): AlertTypeModel { return { - id: MemoryUsageAlert.TYPE, - name: MemoryUsageAlert.LABEL, + id: ALERT_MEMORY_USAGE, + name: ALERT_DETAILS[ALERT_MEMORY_USAGE].label, iconClass: 'bell', alertParamsExpression: (props: Props) => ( - + ), validate, defaultActionMessage: '{{context.internalFullMessage}}', diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx index 7dc6155de529ee..ac30a02173a5c2 100644 --- a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { EuiForm, EuiSpacer } from '@elastic/eui'; -import { CommonAlertParamDetails } from '../../../common/types'; +import { CommonAlertParamDetails } from '../../../common/types/alerts'; import { AlertParamDuration } from '../flyout_expressions/alert_param_duration'; import { AlertParamType } from '../../../common/enums'; import { AlertParamPercentage } from '../flyout_expressions/alert_param_percentage'; @@ -26,7 +26,7 @@ export const Expression: React.FC = (props) => { const details = paramDetails[alertParamName]; const value = alertParams[alertParamName]; - switch (details.type) { + switch (details?.type) { case AlertParamType.Duration: return ( ( - + ), validate, defaultActionMessage: '{{context.internalFullMessage}}', diff --git a/x-pack/plugins/monitoring/public/alerts/panel.tsx b/x-pack/plugins/monitoring/public/alerts/panel.tsx index eb3b6ff9da1be1..99db6c8b3c945f 100644 --- a/x-pack/plugins/monitoring/public/alerts/panel.tsx +++ b/x-pack/plugins/monitoring/public/alerts/panel.tsx @@ -18,8 +18,7 @@ import { EuiListGroupItem, } from '@elastic/eui'; -import { CommonAlertStatus, CommonAlertState } from '../../common/types'; -import { AlertMessage } from '../../server/alerts/types'; +import { CommonAlertStatus, CommonAlertState, AlertMessage } from '../../common/types/alerts'; import { Legacy } from '../legacy_shims'; import { replaceTokens } from './lib/replace_tokens'; import { AlertsContextProvider } from '../../../triggers_actions_ui/public'; diff --git a/x-pack/plugins/monitoring/public/alerts/status.tsx b/x-pack/plugins/monitoring/public/alerts/status.tsx index c1ad41fc8d763c..53918807a4272f 100644 --- a/x-pack/plugins/monitoring/public/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/alerts/status.tsx @@ -7,9 +7,8 @@ import React from 'react'; import { EuiToolTip, EuiHealth } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { CommonAlertStatus } from '../../common/types'; +import { CommonAlertStatus, AlertMessage, AlertState } from '../../common/types/alerts'; import { AlertSeverity } from '../../common/enums'; -import { AlertMessage, AlertState } from '../../server/alerts/types'; import { AlertsBadge } from './badge'; import { isInSetupMode } from '../lib/setup_mode'; import { SetupModeContext } from '../components/setup_mode/setup_mode_context'; diff --git a/x-pack/plugins/monitoring/public/alerts/thread_pool_rejections_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/thread_pool_rejections_alert/index.tsx new file mode 100644 index 00000000000000..5e8e6764482185 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/thread_pool_rejections_alert/index.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer } from '@elastic/eui'; +import { Expression, Props } from '../components/duration/expression'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { CommonAlertParamDetails } from '../../../common/types/alerts'; + +interface ThreadPoolTypes { + [key: string]: unknown; +} + +interface ThreadPoolRejectionAlertDetails { + label: string; + paramDetails: CommonAlertParamDetails; +} + +export function createThreadPoolRejectionsAlertType( + alertType: string, + threadPoolAlertDetails: ThreadPoolRejectionAlertDetails +): AlertTypeModel { + return { + id: alertType, + name: threadPoolAlertDetails.label, + iconClass: 'bell', + alertParamsExpression: (props: Props) => ( + <> + + + + ), + validate: (inputValues: ThreadPoolTypes) => { + const errors: { [key: string]: string[] } = {}; + const value = inputValues.threshold as number; + if (value < 0) { + const errStr = i18n.translate('xpack.monitoring.alerts.validation.lessThanZero', { + defaultMessage: 'This value can not be less than zero', + }); + errors.threshold = [errStr]; + } + + if (!inputValues.duration) { + const errStr = i18n.translate('xpack.monitoring.alerts.validation.duration', { + defaultMessage: 'A valid duration is required.', + }); + errors.duration = [errStr]; + } + + return { errors }; + }, + defaultActionMessage: '{{context.internalFullMessage}}', + requiresAppContext: true, + }; +} diff --git a/x-pack/plugins/monitoring/public/angular/providers/private.js b/x-pack/plugins/monitoring/public/angular/providers/private.js index 3a667037b29191..7709865432fe6f 100644 --- a/x-pack/plugins/monitoring/public/angular/providers/private.js +++ b/x-pack/plugins/monitoring/public/angular/providers/private.js @@ -81,9 +81,9 @@ * * @param {[type]} prov [description] */ -import _ from 'lodash'; +import { partial, uniqueId, isObject } from 'lodash'; -const nextId = _.partial(_.uniqueId, 'privateProvider#'); +const nextId = partial(uniqueId, 'privateProvider#'); function name(fn) { return fn.name || fn.toString().split('\n').shift(); @@ -141,7 +141,7 @@ export function PrivateProvider() { const context = {}; let instance = $injector.invoke(prov, context, locals); - if (!_.isObject(instance)) instance = context; + if (!isObject(instance)) instance = context; privPath.pop(); return instance; @@ -155,7 +155,7 @@ export function PrivateProvider() { if ($delegateId != null && $delegateProv != null) { instance = instantiate(prov, { - $decorate: _.partial(get, $delegateId, $delegateProv), + $decorate: partial(get, $delegateId, $delegateProv), }); } else { instance = instantiate(prov); diff --git a/x-pack/plugins/monitoring/public/components/chart/chart_target.js b/x-pack/plugins/monitoring/public/components/chart/chart_target.js index 9a590d803bb193..519964e4d59141 100644 --- a/x-pack/plugins/monitoring/public/components/chart/chart_target.js +++ b/x-pack/plugins/monitoring/public/components/chart/chart_target.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { get, isEqual, filter } from 'lodash'; import $ from 'jquery'; import React from 'react'; import { eventBus } from './event_bus'; @@ -50,12 +50,12 @@ export class ChartTarget extends React.Component { } UNSAFE_componentWillReceiveProps(newProps) { - if (this.plot && !_.isEqual(newProps, this.props)) { + if (this.plot && !isEqual(newProps, this.props)) { const { series, timeRange } = newProps; const xaxisOptions = this.plot.getAxes().xaxis.options; - xaxisOptions.min = _.get(timeRange, 'min'); - xaxisOptions.max = _.get(timeRange, 'max'); + xaxisOptions.min = get(timeRange, 'min'); + xaxisOptions.max = get(timeRange, 'max'); this.plot.setData(this.filterData(series, newProps.seriesToShow)); this.plot.setupGrid(); @@ -73,7 +73,7 @@ export class ChartTarget extends React.Component { } filterData(data, seriesToShow) { - return _(data).filter(this.filterByShow(seriesToShow)).value(); + return filter(data, this.filterByShow(seriesToShow)); } async getOptions() { @@ -128,7 +128,7 @@ export class ChartTarget extends React.Component { this.handleThorPlotHover = (_event, pos, item, originalPlot) => { if (this.plot !== originalPlot) { // the crosshair is set for the original chart already - this.plot.setCrosshair({ x: _.get(pos, 'x') }); + this.plot.setCrosshair({ x: get(pos, 'x') }); } this.props.updateLegend(pos, item); }; diff --git a/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js index eb32ee108e7b3e..829994791f7690 100644 --- a/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js +++ b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { debounce, keys, has, includes, isFunction, difference, assign } from 'lodash'; import React from 'react'; import { getLastValue } from './get_last_value'; import { TimeseriesContainer } from './timeseries_container'; @@ -17,7 +17,7 @@ export class TimeseriesVisualization extends React.Component { constructor(props) { super(props); - this.debouncedUpdateLegend = _.debounce(this.updateLegend, DEBOUNCE_SLOW_MS); + this.debouncedUpdateLegend = debounce(this.updateLegend, DEBOUNCE_SLOW_MS); this.debouncedUpdateLegend = this.debouncedUpdateLegend.bind(this); this.toggleFilter = this.toggleFilter.bind(this); @@ -26,18 +26,18 @@ export class TimeseriesVisualization extends React.Component { this.state = { values: {}, - seriesToShow: _.keys(values), + seriesToShow: keys(values), ignoreVisibilityUpdates: false, }; } filterLegend(id) { - if (!_.has(this.state.values, id)) { + if (!has(this.state.values, id)) { return []; } - const notAllShown = _.keys(this.state.values).length !== this.state.seriesToShow.length; - const isCurrentlyShown = _.includes(this.state.seriesToShow, id); + const notAllShown = keys(this.state.values).length !== this.state.seriesToShow.length; + const isCurrentlyShown = includes(this.state.seriesToShow, id); const seriesToShow = []; if (notAllShown && isCurrentlyShown) { @@ -59,7 +59,7 @@ export class TimeseriesVisualization extends React.Component { toggleFilter(_event, id) { const seriesToShow = this.filterLegend(id); - if (_.isFunction(this.props.onFilter)) { + if (isFunction(this.props.onFilter)) { this.props.onFilter(seriesToShow); } } @@ -94,7 +94,7 @@ export class TimeseriesVisualization extends React.Component { getValuesByX(this.props.series, pos.x, setValueCallback); } } else { - _.assign(values, this.getLastValues()); + assign(values, this.getLastValues()); } this.setState({ values }); @@ -102,13 +102,13 @@ export class TimeseriesVisualization extends React.Component { UNSAFE_componentWillReceiveProps(props) { const values = this.getLastValues(props); - const currentKeys = _.keys(this.state.values); - const keys = _.keys(values); - const diff = _.difference(keys, currentKeys); + const currentKeys = keys(this.state.values); + const valueKeys = keys(values); + const diff = difference(valueKeys, currentKeys); const nextState = { values: values }; if (diff.length && !this.state.ignoreVisibilityUpdates) { - nextState.seriesToShow = keys; + nextState.seriesToShow = valueKeys; } this.setState(nextState); diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js index 0fe434afa2c887..7e85d62c4bbd68 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js @@ -41,6 +41,8 @@ import { ALERT_CLUSTER_HEALTH, ALERT_CPU_USAGE, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, @@ -162,6 +164,8 @@ const OVERVIEW_PANEL_ALERTS = [ALERT_CLUSTER_HEALTH, ALERT_LICENSE_EXPIRATION]; const NODES_PANEL_ALERTS = [ ALERT_CPU_USAGE, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index 215bb8999eccd5..356f49b4d3b7d7 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -26,7 +26,7 @@ import { EuiHealth, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; +import { get } from 'lodash'; import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; import { ListingCallOut } from '../../setup_mode/listing_callout'; import { AlertsStatus } from '../../../alerts/status'; @@ -56,7 +56,7 @@ const getNodeTooltip = (node) => { return null; }; -const getSortHandler = (type) => (item) => _.get(item, [type, 'summary', 'lastVal']); +const getSortHandler = (type) => (item) => get(item, [type, 'summary', 'lastVal']); const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, alerts) => { const cols = []; @@ -85,7 +85,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler let setupModeStatus = null; if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - const list = _.get(setupMode, 'data.byUuid', {}); + const list = get(setupMode, 'data.byUuid', {}); const status = list[node.resolver] || {}; const instance = { uuid: node.resolver, @@ -394,7 +394,7 @@ export function ElasticsearchNodes({ clusterStatus, showCgroupMetricsElasticsear setupMode.data.totalUniqueInstanceCount ) { const finishMigrationAction = - _.get(setupMode.meta, 'liveClusterUuid') === clusterUuid + get(setupMode.meta, 'liveClusterUuid') === clusterUuid ? setupMode.shortcutToFinishMigration : setupMode.openFlyout; diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js index 2c66d14a406051..5c8dca54894b4a 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { sortBy } from 'lodash'; import React from 'react'; import { Shard } from './shard'; import { i18n } from '@kbn/i18n'; @@ -36,7 +36,7 @@ export class Unassigned extends React.Component { }; render() { - const shards = _.sortBy(this.props.shards, 'shard').map(this.createShard); + const shards = sortBy(this.props.shards, 'shard').map(this.createShard); return ( diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js index 47739b8fe31e81..a371f3e5ff40cd 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { some } from 'lodash'; export function hasPrimaryChildren(item) { - return _.some(item.children, { primary: true }); + return some(item.children, { primary: true }); } diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js index db9b7bacc3cdf1..335c3d29a5b9ec 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { each, isArray } from 'lodash'; export const _vents = {}; export const vents = { vents: _vents, on: function (id, cb) { - if (!_.isArray(_vents[id])) { + if (!isArray(_vents[id])) { _vents[id] = []; } _vents[id].push(cb); @@ -22,7 +22,7 @@ export const vents = { const args = Array.prototype.slice.call(arguments); const id = args.shift(); if (_vents[id]) { - _.each(_vents[id], function (cb) { + each(_vents[id], function (cb) { cb.apply(null, args); }); } diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js index a9808ebc4c6adb..a04e2bcd1786e5 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { find, reduce, values, sortBy } from 'lodash'; import { decorateShards } from '../lib/decorate_shards'; export function indicesByNodes() { @@ -39,7 +39,7 @@ export function indicesByNodes() { return obj; } - let nodeObj = _.find(obj[index].children, { id: node }); + let nodeObj = find(obj[index].children, { id: node }); if (!nodeObj) { nodeObj = { id: node, @@ -55,7 +55,7 @@ export function indicesByNodes() { return obj; } - const data = _.reduce( + const data = reduce( decorateShards(shards, nodes), function (obj, shard) { obj = createIndex(obj, shard); @@ -64,10 +64,11 @@ export function indicesByNodes() { }, {} ); - - return _(data) - .values() - .sortBy((index) => [!index.unassignedPrimaries, /^\./.test(index.name), index.name]) - .value(); + const dataValues = values(data); + return sortBy(dataValues, (index) => [ + !index.unassignedPrimaries, + /^\./.test(index.name), + index.name, + ]); }; } diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js index 353e1c23d4bc15..f8dd5b6cb8e8df 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { find, some, reduce, values, sortBy } from 'lodash'; import { hasPrimaryChildren } from '../lib/has_primary_children'; import { decorateShards } from '../lib/decorate_shards'; @@ -32,7 +32,7 @@ export function nodesByIndices() { if (!obj[node]) { createNode(obj, nodes[node], node); } - let indexObj = _.find(obj[node].children, { id: index }); + let indexObj = find(obj[node].children, { id: index }); if (!indexObj) { indexObj = { id: index, @@ -51,7 +51,7 @@ export function nodesByIndices() { } let data = {}; - if (_.some(shards, isUnassigned)) { + if (some(shards, isUnassigned)) { data.unassigned = { name: 'Unassigned', master: false, @@ -60,19 +60,15 @@ export function nodesByIndices() { }; } - data = _.reduce(decorateShards(shards, nodes), createIndexAddShard, data); - - return _(data) - .values() - .sortBy(function (node) { - return [node.name !== 'Unassigned', !node.master, node.name]; - }) - .map(function (node) { - if (node.name === 'Unassigned') { - node.unassignedPrimaries = node.children.some(hasPrimaryChildren); - } - return node; - }) - .value(); + data = reduce(decorateShards(shards, nodes), createIndexAddShard, data); + const dataValues = values(data); + return sortBy(dataValues, function (node) { + return [node.name !== 'Unassigned', !node.master, node.name]; + }).map(function (node) { + if (node.name === 'Unassigned') { + node.unassignedPrimaries = node.children.some(hasPrimaryChildren); + } + return node; + }); }; } diff --git a/x-pack/plugins/monitoring/public/legacy_shims.ts b/x-pack/plugins/monitoring/public/legacy_shims.ts index bb9f73b5e9ddba..c3c903dab38e95 100644 --- a/x-pack/plugins/monitoring/public/legacy_shims.ts +++ b/x-pack/plugins/monitoring/public/legacy_shims.ts @@ -5,7 +5,6 @@ */ import { CoreStart, HttpSetup, IUiSettingsClient } from 'kibana/public'; -import angular from 'angular'; import { Observable } from 'rxjs'; import { HttpRequestInit } from '../../../../src/core/public'; import { MonitoringStartPluginDependencies } from './types'; diff --git a/x-pack/plugins/monitoring/public/lib/calculate_shard_stats.js b/x-pack/plugins/monitoring/public/lib/calculate_shard_stats.js index 6aee89a9817d54..cd504374da2e45 100644 --- a/x-pack/plugins/monitoring/public/lib/calculate_shard_stats.js +++ b/x-pack/plugins/monitoring/public/lib/calculate_shard_stats.js @@ -5,10 +5,10 @@ */ import { set } from '@elastic/safer-lodash-set'; -import _ from 'lodash'; +import { get, each } from 'lodash'; function addOne(obj, key) { - let value = _.get(obj, key); + let value = get(obj, key); set(obj, key, ++value); } @@ -34,8 +34,8 @@ export function calculateShardStats(state) { data[shard.index] = metrics; }; if (state) { - const shards = _.get(state, 'cluster_state.shards'); - _.each(shards, processShards); + const shards = get(state, 'cluster_state.shards'); + each(shards, processShards); } return data; } diff --git a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js index 73422219add956..6e05c02ac73387 100644 --- a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js +++ b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { find, first } from 'lodash'; export function getClusterFromClusters(clusters, globalState, unsetGlobalState = false) { const cluster = (() => { - const existingCurrent = _.find(clusters, { cluster_uuid: globalState.cluster_uuid }); + const existingCurrent = find(clusters, { cluster_uuid: globalState.cluster_uuid }); if (existingCurrent) { return existingCurrent; } - const firstCluster = _.first(clusters); + const firstCluster = first(clusters); if (firstCluster && firstCluster.cluster_uuid) { return firstCluster; } diff --git a/x-pack/plugins/monitoring/public/lib/route_init.js b/x-pack/plugins/monitoring/public/lib/route_init.js index eebdfa8692f1a4..97ff621ee31644 100644 --- a/x-pack/plugins/monitoring/public/lib/route_init.js +++ b/x-pack/plugins/monitoring/public/lib/route_init.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; import { ajaxErrorHandlersProvider } from './ajax_error_handler'; import { isInSetupMode } from './setup_mode'; import { getClusterFromClusters } from './get_cluster_from_clusters'; @@ -13,7 +12,7 @@ export function routeInitProvider(Private, monitoringClusters, globalState, lice const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); function isOnPage(hash) { - return _.includes(window.location.hash, hash); + return window.location.hash.includes(hash); } /* diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index 4c50abb40dd3dc..a228c540761b85 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -22,11 +22,11 @@ import { UI_SETTINGS } from '../../../../src/plugins/data/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { MonitoringStartPluginDependencies, MonitoringConfig } from './types'; import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; -import { createCpuUsageAlertType } from './alerts/cpu_usage_alert'; -import { createMissingMonitoringDataAlertType } from './alerts/missing_monitoring_data_alert'; -import { createLegacyAlertTypes } from './alerts/legacy_alert'; -import { createDiskUsageAlertType } from './alerts/disk_usage_alert'; -import { createMemoryUsageAlertType } from './alerts/memory_usage_alert'; +import { + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, + ALERT_DETAILS, +} from '../common/constants'; interface MonitoringSetupPluginDependencies { home?: HomePublicPluginSetup; @@ -40,7 +40,7 @@ export class MonitoringPlugin Plugin { constructor(private initializerContext: PluginInitializerContext) {} - public setup( + public async setup( core: CoreSetup, plugins: MonitoringSetupPluginDependencies ) { @@ -73,16 +73,7 @@ export class MonitoringPlugin }); } - const { alertTypeRegistry } = plugins.triggersActionsUi; - alertTypeRegistry.register(createCpuUsageAlertType()); - alertTypeRegistry.register(createDiskUsageAlertType()); - alertTypeRegistry.register(createMemoryUsageAlertType()); - alertTypeRegistry.register(createMissingMonitoringDataAlertType()); - - const legacyAlertTypes = createLegacyAlertTypes(); - for (const legacyAlertType of legacyAlertTypes) { - alertTypeRegistry.register(legacyAlertType); - } + await this.registerAlertsAsync(plugins); const app: App = { id, @@ -106,7 +97,6 @@ export class MonitoringPlugin usageCollection: plugins.usageCollection, }; - pluginsStart.kibanaLegacy.loadFontAwesome(); this.setInitialTimefilter(deps); const monitoringApp = new AngularApp(deps); @@ -154,4 +144,41 @@ export class MonitoringPlugin ['showCgroupMetricsLogstash', monitoring.ui.container.logstash.enabled], ]; } + + private registerAlertsAsync = async (plugins: MonitoringSetupPluginDependencies) => { + const { createCpuUsageAlertType } = await import('./alerts/cpu_usage_alert'); + const { createMissingMonitoringDataAlertType } = await import( + './alerts/missing_monitoring_data_alert' + ); + const { createLegacyAlertTypes } = await import('./alerts/legacy_alert'); + const { createDiskUsageAlertType } = await import('./alerts/disk_usage_alert'); + const { createThreadPoolRejectionsAlertType } = await import( + './alerts/thread_pool_rejections_alert' + ); + const { createMemoryUsageAlertType } = await import('./alerts/memory_usage_alert'); + + const { + triggersActionsUi: { alertTypeRegistry }, + } = plugins; + alertTypeRegistry.register(createCpuUsageAlertType()); + alertTypeRegistry.register(createDiskUsageAlertType()); + alertTypeRegistry.register(createMemoryUsageAlertType()); + alertTypeRegistry.register(createMissingMonitoringDataAlertType()); + alertTypeRegistry.register( + createThreadPoolRejectionsAlertType( + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_DETAILS[ALERT_THREAD_POOL_SEARCH_REJECTIONS] + ) + ); + alertTypeRegistry.register( + createThreadPoolRejectionsAlertType( + ALERT_THREAD_POOL_WRITE_REJECTIONS, + ALERT_DETAILS[ALERT_THREAD_POOL_WRITE_REJECTIONS] + ) + ); + const legacyAlertTypes = createLegacyAlertTypes(); + for (const legacyAlertType of legacyAlertTypes) { + alertTypeRegistry.register(legacyAlertType); + } + }; } diff --git a/x-pack/plugins/monitoring/public/services/features.js b/x-pack/plugins/monitoring/public/services/features.js index f98af10f8dfb49..5e29353e497d12 100644 --- a/x-pack/plugins/monitoring/public/services/features.js +++ b/x-pack/plugins/monitoring/public/services/features.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { has, isUndefined } from 'lodash'; export function featuresProvider($window) { function getData() { @@ -28,11 +28,11 @@ export function featuresProvider($window) { function isEnabled(featureName, defaultSetting) { const monitoringDataObj = getData(); - if (_.has(monitoringDataObj, featureName)) { + if (has(monitoringDataObj, featureName)) { return monitoringDataObj[featureName]; } - if (_.isUndefined(defaultSetting)) { + if (isUndefined(defaultSetting)) { return false; } diff --git a/x-pack/plugins/monitoring/public/services/title.js b/x-pack/plugins/monitoring/public/services/title.js index 0715f4dc9e0b6b..91ef4c32f3b98e 100644 --- a/x-pack/plugins/monitoring/public/services/title.js +++ b/x-pack/plugins/monitoring/public/services/title.js @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { Legacy } from '../legacy_shims'; export function titleProvider($rootScope) { return function changeTitle(cluster, suffix) { - let clusterName = _.get(cluster, 'cluster_name'); + let clusterName = get(cluster, 'cluster_name'); clusterName = clusterName ? `- ${clusterName}` : ''; suffix = suffix ? `- ${suffix}` : ''; $rootScope.$applyAsync(() => { diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js index 8021ae7e5f63c4..7e78170d1117fe 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js @@ -20,6 +20,8 @@ import { MonitoringViewBaseController } from '../../../base_controller'; import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, ALERT_MEMORY_USAGE, @@ -76,6 +78,8 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', { alertTypeIds: [ ALERT_CPU_USAGE, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_MISSING_MONITORING_DATA, ], diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js index 5164e93c266ca3..586261eecb2508 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js @@ -21,6 +21,8 @@ import { MonitoringViewBaseController } from '../../base_controller'; import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, ALERT_MEMORY_USAGE, @@ -60,6 +62,8 @@ uiRoutes.when('/elasticsearch/nodes/:node', { alertTypeIds: [ ALERT_CPU_USAGE, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_MISSING_MONITORING_DATA, ], diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js index e69d572f9560be..3ec9c6235867ba 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js @@ -19,6 +19,8 @@ import { ELASTICSEARCH_SYSTEM_ID, CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, ALERT_MEMORY_USAGE, @@ -93,6 +95,8 @@ uiRoutes.when('/elasticsearch/nodes', { alertTypeIds: [ ALERT_CPU_USAGE, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_MISSING_MONITORING_DATA, ], diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alert_helpers.ts similarity index 97% rename from x-pack/plugins/monitoring/server/alerts/alerts_common.ts rename to x-pack/plugins/monitoring/server/alerts/alert_helpers.ts index 41c8bba17df0ad..984746e59f06bc 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alert_helpers.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { AlertMessageDocLinkToken } from './types'; +import { AlertMessageDocLinkToken } from '../../common/types/alerts'; import { AlertMessageTokenType } from '../../common/enums'; export class AlertingDefaults { diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts index f486061109b391..cc0423051f2aa5 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts @@ -60,9 +60,4 @@ describe('AlertsFactory', () => { expect(alert).not.toBeNull(); expect(alert?.type).toBe(ALERT_CPU_USAGE); }); - - it('should get all', () => { - const alerts = AlertsFactory.getAll(); - expect(alerts.length).toBe(10); - }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts index 22c41c9c600385..efd3d7d5e3b30d 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts @@ -8,6 +8,8 @@ import { CpuUsageAlert, MissingMonitoringDataAlert, DiskUsageAlert, + ThreadPoolSearchRejectionsAlert, + ThreadPoolWriteRejectionsAlert, MemoryUsageAlert, NodesChangedAlert, ClusterHealthAlert, @@ -23,6 +25,8 @@ import { ALERT_CPU_USAGE, ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, + ALERT_THREAD_POOL_SEARCH_REJECTIONS, + ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_MEMORY_USAGE, ALERT_NODES_CHANGED, ALERT_LOGSTASH_VERSION_MISMATCH, @@ -31,12 +35,14 @@ import { } from '../../common/constants'; import { AlertsClient } from '../../../alerts/server'; -export const BY_TYPE = { +const BY_TYPE = { [ALERT_CLUSTER_HEALTH]: ClusterHealthAlert, [ALERT_LICENSE_EXPIRATION]: LicenseExpirationAlert, [ALERT_CPU_USAGE]: CpuUsageAlert, [ALERT_MISSING_MONITORING_DATA]: MissingMonitoringDataAlert, [ALERT_DISK_USAGE]: DiskUsageAlert, + [ALERT_THREAD_POOL_SEARCH_REJECTIONS]: ThreadPoolSearchRejectionsAlert, + [ALERT_THREAD_POOL_WRITE_REJECTIONS]: ThreadPoolWriteRejectionsAlert, [ALERT_MEMORY_USAGE]: MemoryUsageAlert, [ALERT_NODES_CHANGED]: NodesChangedAlert, [ALERT_LOGSTASH_VERSION_MISMATCH]: LogstashVersionMismatchAlert, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index c92291cf72093a..48b783a4508077 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -28,14 +28,16 @@ import { AlertData, AlertInstanceState, AlertEnableAction, -} from './types'; + CommonAlertFilter, + CommonAlertParams, + CommonBaseAlert, +} from '../../common/types/alerts'; import { fetchAvailableCcs } from '../lib/alerts/fetch_available_ccs'; import { fetchClusters } from '../lib/alerts/fetch_clusters'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; import { MonitoringConfig } from '../config'; import { AlertSeverity } from '../../common/enums'; -import { CommonAlertFilter, CommonAlertParams, CommonBaseAlert } from '../../common/types'; import { MonitoringLicenseService } from '../types'; import { mbSafeQuery } from '../lib/mb_safe_query'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; @@ -269,18 +271,18 @@ export class BaseAlert { } protected async fetchData( - params: CommonAlertParams, + params: CommonAlertParams | unknown, callCluster: any, clusters: AlertCluster[], uiSettings: IUiSettingsClient, availableCcs: string[] - ): Promise { + ): Promise> { // Child should implement throw new Error('Child classes must implement `fetchData`'); } protected async processData( - data: AlertData[], + data: Array, clusters: AlertCluster[], services: AlertServices, logger: Logger, @@ -365,15 +367,18 @@ export class BaseAlert { }; } - protected getUiMessage(alertState: AlertState, item: AlertData): AlertMessage { + protected getUiMessage( + alertState: AlertState | unknown, + item: AlertData | unknown + ): AlertMessage { throw new Error('Child classes must implement `getUiMessage`'); } protected executeActions( instance: AlertInstance, - instanceState: AlertInstanceState, - item: AlertData, - cluster: AlertCluster + instanceState: AlertInstanceState | unknown, + item: AlertData | unknown, + cluster?: AlertCluster | unknown ) { throw new Error('Child classes must implement `executeActions`'); } diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts index 427dd2f86de00f..1d3d36413ebc28 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts @@ -14,15 +14,15 @@ import { AlertMessageLinkToken, AlertInstanceState, LegacyAlert, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; -import { INDEX_ALERTS, ALERT_CLUSTER_HEALTH } from '../../common/constants'; +import { INDEX_ALERTS, ALERT_CLUSTER_HEALTH, LEGACY_ALERT_DETAILS } from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertMessageTokenType, AlertClusterHealthType } from '../../common/enums'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; -import { CommonAlertParams } from '../../common/types'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const RED_STATUS_MESSAGE = i18n.translate('xpack.monitoring.alerts.clusterHealth.redMessage', { defaultMessage: 'Allocate missing primary and replica shards', @@ -39,9 +39,7 @@ const WATCH_NAME = 'elasticsearch_cluster_status'; export class ClusterHealthAlert extends BaseAlert { public type = ALERT_CLUSTER_HEALTH; - public label = i18n.translate('xpack.monitoring.alerts.clusterHealth.label', { - defaultMessage: 'Cluster health', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_CLUSTER_HEALTH].label; public isLegacy = true; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index 09133dadca1627..55931e2996cbf3 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -16,55 +16,36 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertInstanceState, -} from './types'; + CommonAlertFilter, + CommonAlertNodeUuidFilter, + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance, AlertServices } from '../../../alerts/server'; -import { INDEX_PATTERN_ELASTICSEARCH, ALERT_CPU_USAGE } from '../../common/constants'; +import { + INDEX_PATTERN_ELASTICSEARCH, + ALERT_CPU_USAGE, + ALERT_DETAILS, +} from '../../common/constants'; import { fetchCpuUsageNodeStats } from '../lib/alerts/fetch_cpu_usage_node_stats'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; -import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { RawAlertInstance } from '../../../alerts/common'; import { parseDuration } from '../../../alerts/common/parse_duration'; -import { - CommonAlertFilter, - CommonAlertNodeUuidFilter, - CommonAlertParams, - CommonAlertParamDetail, -} from '../../common/types'; -import { AlertingDefaults, createLink } from './alerts_common'; +import { AlertingDefaults, createLink } from './alert_helpers'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; -const DEFAULT_THRESHOLD = 85; -const DEFAULT_DURATION = '5m'; - interface CpuUsageParams { threshold: number; duration: string; } export class CpuUsageAlert extends BaseAlert { - public static paramDetails = { - threshold: { - label: i18n.translate('xpack.monitoring.alerts.cpuUsage.paramDetails.threshold.label', { - defaultMessage: `Notify when CPU is over`, - }), - type: AlertParamType.Percentage, - } as CommonAlertParamDetail, - duration: { - label: i18n.translate('xpack.monitoring.alerts.cpuUsage.paramDetails.duration.label', { - defaultMessage: `Look at the average over`, - }), - type: AlertParamType.Duration, - } as CommonAlertParamDetail, - }; - public type = ALERT_CPU_USAGE; - public label = i18n.translate('xpack.monitoring.alerts.cpuUsage.label', { - defaultMessage: 'CPU Usage', - }); + public label = ALERT_DETAILS[ALERT_CPU_USAGE].label; protected defaultParams: CpuUsageParams = { - threshold: DEFAULT_THRESHOLD, - duration: DEFAULT_DURATION, + threshold: 85, + duration: '5m', }; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 34c640de79625b..e54e7367243571 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -15,43 +15,25 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertInstanceState, -} from './types'; + CommonAlertFilter, + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance, AlertServices } from '../../../alerts/server'; -import { INDEX_PATTERN_ELASTICSEARCH, ALERT_DISK_USAGE } from '../../common/constants'; +import { + INDEX_PATTERN_ELASTICSEARCH, + ALERT_DISK_USAGE, + ALERT_DETAILS, +} from '../../common/constants'; import { fetchDiskUsageNodeStats } from '../lib/alerts/fetch_disk_usage_node_stats'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; -import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { RawAlertInstance } from '../../../alerts/common'; -import { CommonAlertFilter, CommonAlertParams, CommonAlertParamDetail } from '../../common/types'; -import { AlertingDefaults, createLink } from './alerts_common'; +import { AlertingDefaults, createLink } from './alert_helpers'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; -interface ParamDetails { - [key: string]: CommonAlertParamDetail; -} - export class DiskUsageAlert extends BaseAlert { - public static readonly PARAM_DETAILS: ParamDetails = { - threshold: { - label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.threshold.label', { - defaultMessage: `Notify when disk capacity is over`, - }), - type: AlertParamType.Percentage, - }, - duration: { - label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.duration.label', { - defaultMessage: `Look at the average over`, - }), - type: AlertParamType.Duration, - }, - }; - public static paramDetails = DiskUsageAlert.PARAM_DETAILS; - public static readonly TYPE = ALERT_DISK_USAGE; - public static readonly LABEL = i18n.translate('xpack.monitoring.alerts.diskUsage.label', { - defaultMessage: 'Disk Usage', - }); - public type = DiskUsageAlert.TYPE; - public label = DiskUsageAlert.LABEL; + public type = ALERT_DISK_USAGE; + public label = ALERT_DETAILS[ALERT_DISK_USAGE].label; protected defaultParams = { threshold: 80, diff --git a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts index f26b21f0c64c51..6412dcfde54bd6 100644 --- a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts @@ -13,22 +13,24 @@ import { AlertMessage, AlertInstanceState, LegacyAlert, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; -import { INDEX_ALERTS, ALERT_ELASTICSEARCH_VERSION_MISMATCH } from '../../common/constants'; +import { + INDEX_ALERTS, + ALERT_ELASTICSEARCH_VERSION_MISMATCH, + LEGACY_ALERT_DETAILS, +} from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; -import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const WATCH_NAME = 'elasticsearch_version_mismatch'; export class ElasticsearchVersionMismatchAlert extends BaseAlert { public type = ALERT_ELASTICSEARCH_VERSION_MISMATCH; - public label = i18n.translate('xpack.monitoring.alerts.elasticsearchVersionMismatch.label', { - defaultMessage: 'Elasticsearch version mismatch', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_ELASTICSEARCH_VERSION_MISMATCH].label; public isLegacy = true; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/index.ts b/x-pack/plugins/monitoring/server/alerts/index.ts index 48254f2dec3269..5fa718dfb34cda 100644 --- a/x-pack/plugins/monitoring/server/alerts/index.ts +++ b/x-pack/plugins/monitoring/server/alerts/index.ts @@ -8,6 +8,8 @@ export { BaseAlert } from './base_alert'; export { CpuUsageAlert } from './cpu_usage_alert'; export { MissingMonitoringDataAlert } from './missing_monitoring_data_alert'; export { DiskUsageAlert } from './disk_usage_alert'; +export { ThreadPoolSearchRejectionsAlert } from './thread_pool_search_rejections_alert'; +export { ThreadPoolWriteRejectionsAlert } from './thread_pool_write_rejections_alert'; export { MemoryUsageAlert } from './memory_usage_alert'; export { ClusterHealthAlert } from './cluster_health_alert'; export { LicenseExpirationAlert } from './license_expiration_alert'; @@ -15,4 +17,4 @@ export { NodesChangedAlert } from './nodes_changed_alert'; export { ElasticsearchVersionMismatchAlert } from './elasticsearch_version_mismatch_alert'; export { KibanaVersionMismatchAlert } from './kibana_version_mismatch_alert'; export { LogstashVersionMismatchAlert } from './logstash_version_mismatch_alert'; -export { AlertsFactory, BY_TYPE } from './alerts_factory'; +export { AlertsFactory } from './alerts_factory'; diff --git a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts index 316f305603964b..851a4016357924 100644 --- a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts @@ -13,22 +13,24 @@ import { AlertMessage, AlertInstanceState, LegacyAlert, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; -import { INDEX_ALERTS, ALERT_KIBANA_VERSION_MISMATCH } from '../../common/constants'; +import { + INDEX_ALERTS, + ALERT_KIBANA_VERSION_MISMATCH, + LEGACY_ALERT_DETAILS, +} from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; -import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const WATCH_NAME = 'kibana_version_mismatch'; export class KibanaVersionMismatchAlert extends BaseAlert { public type = ALERT_KIBANA_VERSION_MISMATCH; - public label = i18n.translate('xpack.monitoring.alerts.kibanaVersionMismatch.label', { - defaultMessage: 'Kibana version mismatch', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_KIBANA_VERSION_MISMATCH].label; public isLegacy = true; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts index f1412ff0fc91af..e0396ee6673e8b 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts @@ -16,27 +16,26 @@ import { AlertMessageLinkToken, AlertInstanceState, LegacyAlert, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; import { INDEX_ALERTS, ALERT_LICENSE_EXPIRATION, FORMAT_DURATION_TEMPLATE_SHORT, + LEGACY_ALERT_DETAILS, } from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertMessageTokenType } from '../../common/enums'; -import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const WATCH_NAME = 'xpack_license_expiration'; export class LicenseExpirationAlert extends BaseAlert { public type = ALERT_LICENSE_EXPIRATION; - public label = i18n.translate('xpack.monitoring.alerts.licenseExpiration.label', { - defaultMessage: 'License expiration', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_LICENSE_EXPIRATION].label; public isLegacy = true; protected actionVariables = [ { diff --git a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts index 37515e32e591a1..7f5c0ea40e36aa 100644 --- a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts @@ -13,22 +13,24 @@ import { AlertMessage, AlertInstanceState, LegacyAlert, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; -import { INDEX_ALERTS, ALERT_LOGSTASH_VERSION_MISMATCH } from '../../common/constants'; +import { + INDEX_ALERTS, + ALERT_LOGSTASH_VERSION_MISMATCH, + LEGACY_ALERT_DETAILS, +} from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; -import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const WATCH_NAME = 'logstash_version_mismatch'; export class LogstashVersionMismatchAlert extends BaseAlert { public type = ALERT_LOGSTASH_VERSION_MISMATCH; - public label = i18n.translate('xpack.monitoring.alerts.logstashVersionMismatch.label', { - defaultMessage: 'Logstash version mismatch', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_LOGSTASH_VERSION_MISMATCH].label; public isLegacy = true; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/memory_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/memory_usage_alert.ts index 8dc707afab1e11..c37176764c0205 100644 --- a/x-pack/plugins/monitoring/server/alerts/memory_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/memory_usage_alert.ts @@ -15,44 +15,26 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertInstanceState, -} from './types'; + CommonAlertFilter, + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance, AlertServices } from '../../../alerts/server'; -import { INDEX_PATTERN_ELASTICSEARCH, ALERT_MEMORY_USAGE } from '../../common/constants'; +import { + INDEX_PATTERN_ELASTICSEARCH, + ALERT_MEMORY_USAGE, + ALERT_DETAILS, +} from '../../common/constants'; import { fetchMemoryUsageNodeStats } from '../lib/alerts/fetch_memory_usage_node_stats'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; -import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { RawAlertInstance } from '../../../alerts/common'; -import { CommonAlertFilter, CommonAlertParams, CommonAlertParamDetail } from '../../common/types'; -import { AlertingDefaults, createLink } from './alerts_common'; +import { AlertingDefaults, createLink } from './alert_helpers'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; import { parseDuration } from '../../../alerts/common/parse_duration'; -interface ParamDetails { - [key: string]: CommonAlertParamDetail; -} - export class MemoryUsageAlert extends BaseAlert { - public static readonly PARAM_DETAILS: ParamDetails = { - threshold: { - label: i18n.translate('xpack.monitoring.alerts.memoryUsage.paramDetails.threshold.label', { - defaultMessage: `Notify when memory usage is over`, - }), - type: AlertParamType.Percentage, - }, - duration: { - label: i18n.translate('xpack.monitoring.alerts.memoryUsage.paramDetails.duration.label', { - defaultMessage: `Look at the average over`, - }), - type: AlertParamType.Duration, - }, - }; - public static paramDetails = MemoryUsageAlert.PARAM_DETAILS; - public static readonly TYPE = ALERT_MEMORY_USAGE; - public static readonly LABEL = i18n.translate('xpack.monitoring.alerts.memoryUsage.label', { - defaultMessage: 'Memory Usage (JVM)', - }); - public type = MemoryUsageAlert.TYPE; - public label = MemoryUsageAlert.LABEL; + public type = ALERT_MEMORY_USAGE; + public label = ALERT_DETAILS[ALERT_MEMORY_USAGE].label; protected defaultParams = { threshold: 85, diff --git a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts index 5b4542a4439ca1..456ad92855f65b 100644 --- a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts @@ -16,24 +16,22 @@ import { AlertMissingData, AlertMessageTimeToken, AlertInstanceState, -} from './types'; + CommonAlertFilter, + CommonAlertParams, + CommonAlertStackProductFilter, + CommonAlertNodeUuidFilter, +} from '../../common/types/alerts'; import { AlertInstance, AlertServices } from '../../../alerts/server'; import { INDEX_PATTERN, ALERT_MISSING_MONITORING_DATA, INDEX_PATTERN_ELASTICSEARCH, + ALERT_DETAILS, } from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; -import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { RawAlertInstance } from '../../../alerts/common'; import { parseDuration } from '../../../alerts/common/parse_duration'; -import { - CommonAlertFilter, - CommonAlertParams, - CommonAlertParamDetail, - CommonAlertStackProductFilter, - CommonAlertNodeUuidFilter, -} from '../../common/types'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; import { fetchMissingMonitoringData } from '../lib/alerts/fetch_missing_monitoring_data'; import { getTypeLabelForStackProduct } from '../lib/alerts/get_type_label_for_stack_product'; @@ -41,7 +39,7 @@ import { getListingLinkForStackProduct } from '../lib/alerts/get_listing_link_fo import { getStackProductLabel } from '../lib/alerts/get_stack_product_label'; import { fetchClusters } from '../lib/alerts/fetch_clusters'; import { fetchAvailableCcs } from '../lib/alerts/fetch_available_ccs'; -import { AlertingDefaults, createLink } from './alerts_common'; +import { AlertingDefaults, createLink } from './alert_helpers'; const RESOLVED = i18n.translate('xpack.monitoring.alerts.missingData.resolved', { defaultMessage: 'resolved', @@ -62,27 +60,10 @@ interface MissingDataParams { } export class MissingMonitoringDataAlert extends BaseAlert { - public static paramDetails = { - duration: { - label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.duration.label', { - defaultMessage: `Notify if monitoring data is missing for the last`, - }), - type: AlertParamType.Duration, - } as CommonAlertParamDetail, - limit: { - label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.limit.label', { - defaultMessage: `looking back`, - }), - type: AlertParamType.Duration, - } as CommonAlertParamDetail, - }; - public defaultThrottle: string = '6h'; public type = ALERT_MISSING_MONITORING_DATA; - public label = i18n.translate('xpack.monitoring.alerts.missingData.label', { - defaultMessage: 'Missing monitoring data', - }); + public label = ALERT_DETAILS[ALERT_MISSING_MONITORING_DATA].label; protected defaultParams: MissingDataParams = { duration: DEFAULT_DURATION, diff --git a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts index e03e6ea53ab4e6..7b54ef629cba6d 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -14,22 +14,20 @@ import { AlertInstanceState, LegacyAlert, LegacyAlertNodesChangedList, -} from './types'; + CommonAlertParams, +} from '../../common/types/alerts'; import { AlertInstance } from '../../../alerts/server'; -import { INDEX_ALERTS, ALERT_NODES_CHANGED } from '../../common/constants'; +import { INDEX_ALERTS, ALERT_NODES_CHANGED, LEGACY_ALERT_DETAILS } from '../../common/constants'; import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; -import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults } from './alert_helpers'; const WATCH_NAME = 'elasticsearch_nodes'; export class NodesChangedAlert extends BaseAlert { public type = ALERT_NODES_CHANGED; - public label = i18n.translate('xpack.monitoring.alerts.nodesChanged.label', { - defaultMessage: 'Nodes changed', - }); + public label = LEGACY_ALERT_DETAILS[ALERT_NODES_CHANGED].label; public isLegacy = true; protected actionVariables = [ diff --git a/x-pack/plugins/monitoring/server/alerts/thread_pool_rejections_alert_base.ts b/x-pack/plugins/monitoring/server/alerts/thread_pool_rejections_alert_base.ts new file mode 100644 index 00000000000000..4905ae73b0545d --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/thread_pool_rejections_alert_base.ts @@ -0,0 +1,312 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IUiSettingsClient, Logger } from 'kibana/server'; +import { i18n } from '@kbn/i18n'; +import { BaseAlert } from './base_alert'; +import { + AlertData, + AlertCluster, + AlertMessage, + AlertThreadPoolRejectionsState, + AlertMessageTimeToken, + AlertMessageLinkToken, + CommonAlertFilter, + ThreadPoolRejectionsAlertParams, +} from '../../common/types/alerts'; +import { AlertInstance, AlertServices } from '../../../alerts/server'; +import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; +import { fetchThreadPoolRejectionStats } from '../lib/alerts/fetch_thread_pool_rejections_stats'; +import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; +import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; +import { Alert, RawAlertInstance } from '../../../alerts/common'; +import { AlertingDefaults, createLink } from './alert_helpers'; +import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; + +type ActionVariables = Array<{ name: string; description: string }>; + +export class ThreadPoolRejectionsAlertBase extends BaseAlert { + protected static createActionVariables(type: string) { + return [ + { + name: 'count', + description: i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.actionVariables.count', + { + defaultMessage: 'The number of nodes reporting high thread pool {type} rejections.', + values: { type }, + } + ), + }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), + ]; + } + + protected defaultParams: ThreadPoolRejectionsAlertParams = { + threshold: 300, + duration: '5m', + }; + + constructor( + rawAlert: Alert | undefined = undefined, + public readonly type: string, + public readonly threadPoolType: string, + public readonly label: string, + public readonly actionVariables: ActionVariables + ) { + super(rawAlert); + } + + protected async fetchData( + params: ThreadPoolRejectionsAlertParams, + callCluster: any, + clusters: AlertCluster[], + uiSettings: IUiSettingsClient, + availableCcs: string[] + ): Promise { + let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); + if (availableCcs) { + esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); + } + + const { threshold, duration } = params; + + const stats = await fetchThreadPoolRejectionStats( + callCluster, + clusters, + esIndexPattern, + this.config.ui.max_bucket_size, + this.threadPoolType, + duration + ); + + return stats.map((stat) => { + const { clusterUuid, nodeId, rejectionCount, ccs } = stat; + + return { + instanceKey: `${clusterUuid}:${nodeId}`, + shouldFire: rejectionCount > threshold, + rejectionCount, + severity: AlertSeverity.Danger, + meta: stat, + clusterUuid, + ccs, + }; + }); + } + + protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) { + const alertInstanceStates = alertInstance.state + ?.alertStates as AlertThreadPoolRejectionsState[]; + const nodeUuid = filters?.find((filter) => filter.nodeUuid)?.nodeUuid; + + if (!alertInstanceStates?.length || !nodeUuid) { + return true; + } + + const nodeAlerts = alertInstanceStates.filter(({ nodeId }) => nodeId === nodeUuid); + return Boolean(nodeAlerts.length); + } + + protected getUiMessage( + alertState: AlertThreadPoolRejectionsState, + rejectionCount: number + ): AlertMessage { + const { nodeName, nodeId } = alertState; + return { + text: i18n.translate('xpack.monitoring.alerts.threadPoolRejections.ui.firingMessage', { + defaultMessage: `Node #start_link{nodeName}#end_link is reporting {rejectionCount} {type} rejections at #absolute`, + values: { + nodeName, + type: this.threadPoolType, + rejectionCount, + }, + }), + nextSteps: [ + createLink( + i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.ui.nextSteps.monitorThisNode', + { + defaultMessage: `#start_linkMonitor this node#end_link`, + } + ), + `elasticsearch/nodes/${nodeId}/advanced`, + AlertMessageTokenType.Link + ), + createLink( + i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.ui.nextSteps.optimizeQueries', + { + defaultMessage: '#start_linkOptimize complex queries#end_link', + } + ), + `{elasticWebsiteUrl}blog/advanced-tuning-finding-and-fixing-slow-elasticsearch-queries` + ), + createLink( + i18n.translate('xpack.monitoring.alerts.threadPoolRejections.ui.nextSteps.addMoreNodes', { + defaultMessage: '#start_linkAdd more nodes#end_link', + }), + `{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/add-elasticsearch-nodes.html` + ), + createLink( + i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.ui.nextSteps.resizeYourDeployment', + { + defaultMessage: '#start_linkResize your deployment (ECE)#end_link', + } + ), + `{elasticWebsiteUrl}guide/en/cloud-enterprise/current/ece-resize-deployment.html` + ), + createLink( + i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.ui.nextSteps.threadPoolSettings', + { + defaultMessage: '#start_linkThread pool settings#end_link', + } + ), + `{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/modules-threadpool.html` + ), + ], + tokens: [ + { + startToken: '#absolute', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.triggeredMS, + } as AlertMessageTimeToken, + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertMessageTokenType.Link, + url: `elasticsearch/nodes/${nodeId}`, + } as AlertMessageLinkToken, + ], + }; + } + + protected executeActions( + instance: AlertInstance, + alertStates: AlertThreadPoolRejectionsState[], + cluster: AlertCluster + ) { + const type = this.threadPoolType; + const count = alertStates.length; + const { clusterName: clusterKnownName, clusterUuid } = cluster; + const clusterName = clusterKnownName || clusterUuid; + const shortActionText = i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.shortAction', + { + defaultMessage: 'Verify thread pool {type} rejections across affected nodes.', + values: { + type, + }, + } + ); + + const fullActionText = i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.fullAction', + { + defaultMessage: 'View nodes', + } + ); + + const ccs = alertStates.find((state) => state.ccs)?.ccs; + const globalStateLink = this.createGlobalStateLink('elasticsearch/nodes', clusterUuid, ccs); + + const action = `[${fullActionText}](${globalStateLink})`; + const internalShortMessage = i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.firing.internalShortMessage', + { + defaultMessage: `Thread pool {type} rejections alert is firing for {count} node(s) in cluster: {clusterName}. {shortActionText}`, + values: { + count, + clusterName, + shortActionText, + type, + }, + } + ); + const internalFullMessage = i18n.translate( + 'xpack.monitoring.alerts.threadPoolRejections.firing.internalFullMessage', + { + defaultMessage: `Thread pool {type} rejections alert is firing for {count} node(s) in cluster: {clusterName}. {action}`, + values: { + count, + clusterName, + action, + type, + }, + } + ); + + instance.scheduleActions('default', { + internalShortMessage, + internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, + threadPoolType: type, + state: AlertingDefaults.ALERT_STATE.firing, + count, + clusterName, + action, + actionPlain: shortActionText, + }); + } + + protected async processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices, + logger: Logger, + state: { lastChecked?: number } + ) { + const currentUTC = +new Date(); + for (const cluster of clusters) { + const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); + if (!nodes.length) { + continue; + } + + const firingNodeUuids = nodes.filter((node) => node.shouldFire); + + if (!firingNodeUuids.length) { + continue; + } + + const instanceSuffix = firingNodeUuids.map((node) => node.meta.nodeId); + + const instancePrefix = `${this.type}:${cluster.clusterUuid}:`; + const alertInstanceId = `${instancePrefix}:${instanceSuffix}`; + const alertInstance = services.alertInstanceFactory(alertInstanceId); + const newAlertStates: AlertThreadPoolRejectionsState[] = []; + + for (const node of nodes) { + if (!node.shouldFire) { + continue; + } + const stat = node.meta as AlertThreadPoolRejectionsState; + const nodeState = this.getDefaultAlertState( + cluster, + node + ) as AlertThreadPoolRejectionsState; + const { nodeId, nodeName, rejectionCount } = stat; + nodeState.nodeId = nodeId; + nodeState.nodeName = nodeName; + nodeState.ui.triggeredMS = currentUTC; + nodeState.ui.isFiring = true; + nodeState.ui.severity = node.severity; + nodeState.ui.message = this.getUiMessage(nodeState, rejectionCount); + newAlertStates.push(nodeState); + } + + alertInstance.replaceState({ alertStates: newAlertStates }); + if (newAlertStates.length) { + this.executeActions(alertInstance, newAlertStates, cluster); + } + } + + state.lastChecked = currentUTC; + return state; + } +} diff --git a/x-pack/plugins/monitoring/server/alerts/thread_pool_search_rejections_alert.ts b/x-pack/plugins/monitoring/server/alerts/thread_pool_search_rejections_alert.ts new file mode 100644 index 00000000000000..10df95c05ba3f2 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/thread_pool_search_rejections_alert.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ThreadPoolRejectionsAlertBase } from './thread_pool_rejections_alert_base'; +import { ALERT_THREAD_POOL_SEARCH_REJECTIONS, ALERT_DETAILS } from '../../common/constants'; +import { Alert } from '../../../alerts/common'; + +export class ThreadPoolSearchRejectionsAlert extends ThreadPoolRejectionsAlertBase { + private static TYPE = ALERT_THREAD_POOL_SEARCH_REJECTIONS; + private static THREAD_POOL_TYPE = 'search'; + private static readonly LABEL = ALERT_DETAILS[ALERT_THREAD_POOL_SEARCH_REJECTIONS].label; + constructor(rawAlert?: Alert) { + super( + rawAlert, + ThreadPoolSearchRejectionsAlert.TYPE, + ThreadPoolSearchRejectionsAlert.THREAD_POOL_TYPE, + ThreadPoolSearchRejectionsAlert.LABEL, + ThreadPoolRejectionsAlertBase.createActionVariables( + ThreadPoolSearchRejectionsAlert.THREAD_POOL_TYPE + ) + ); + } +} diff --git a/x-pack/plugins/monitoring/server/alerts/thread_pool_write_rejections_alert.ts b/x-pack/plugins/monitoring/server/alerts/thread_pool_write_rejections_alert.ts new file mode 100644 index 00000000000000..d415515315b376 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/thread_pool_write_rejections_alert.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ThreadPoolRejectionsAlertBase } from './thread_pool_rejections_alert_base'; +import { ALERT_THREAD_POOL_WRITE_REJECTIONS, ALERT_DETAILS } from '../../common/constants'; +import { Alert } from '../../../alerts/common'; + +export class ThreadPoolWriteRejectionsAlert extends ThreadPoolRejectionsAlertBase { + private static TYPE = ALERT_THREAD_POOL_WRITE_REJECTIONS; + private static THREAD_POOL_TYPE = 'write'; + private static readonly LABEL = ALERT_DETAILS[ALERT_THREAD_POOL_WRITE_REJECTIONS].label; + constructor(rawAlert?: Alert) { + super( + rawAlert, + ThreadPoolWriteRejectionsAlert.TYPE, + ThreadPoolWriteRejectionsAlert.THREAD_POOL_TYPE, + ThreadPoolWriteRejectionsAlert.LABEL, + ThreadPoolRejectionsAlertBase.createActionVariables( + ThreadPoolWriteRejectionsAlert.THREAD_POOL_TYPE + ) + ); + } +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts index d474338bce9229..368a909279b8c0 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { AlertCluster } from '../../alerts/types'; +import { AlertCluster } from '../../../common/types/alerts'; interface RangeFilter { [field: string]: { diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts index ecd324c083a8c4..b38a32164223ee 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts @@ -6,7 +6,7 @@ import { get } from 'lodash'; import moment from 'moment'; import { NORMALIZED_DERIVATIVE_UNIT } from '../../../common/constants'; -import { AlertCluster, AlertCpuUsageNodeStats } from '../../alerts/types'; +import { AlertCluster, AlertCpuUsageNodeStats } from '../../../common/types/alerts'; interface NodeBucketESResponse { key: string; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts index 6201204ebebe0f..f00c42d708b160 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { AlertCluster, AlertDiskUsageNodeStats } from '../../alerts/types'; +import { AlertCluster, AlertDiskUsageNodeStats } from '../../../common/types/alerts'; export async function fetchDiskUsageNodeStats( callCluster: any, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts index fe01a1b921c2eb..fbf7608a737ba6 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { LegacyAlert, AlertCluster, LegacyAlertMetadata } from '../../alerts/types'; +import { LegacyAlert, AlertCluster, LegacyAlertMetadata } from '../../../common/types/alerts'; export async function fetchLegacyAlerts( callCluster: any, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts index c6843c3ed5f12f..9a68b3afc7758a 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { AlertCluster, AlertMemoryUsageNodeStats } from '../../alerts/types'; +import { AlertCluster, AlertMemoryUsageNodeStats } from '../../../common/types/alerts'; export async function fetchMemoryUsageNodeStats( callCluster: any, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts index 91fc05137a8c18..49307764e9f015 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { AlertCluster, AlertMissingData } from '../../alerts/types'; +import { AlertCluster, AlertMissingData } from '../../../common/types/alerts'; import { KIBANA_SYSTEM_ID, BEATS_SYSTEM_ID, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts index 824eeab7245b44..c31ab91866b1d5 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts @@ -5,7 +5,7 @@ */ import { fetchStatus } from './fetch_status'; -import { AlertUiState, AlertState } from '../../alerts/types'; +import { AlertUiState, AlertState } from '../../../common/types/alerts'; import { AlertSeverity } from '../../../common/enums'; import { ALERT_CPU_USAGE, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts index ed49f42e4908cf..ed860ee21344d7 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts @@ -4,10 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ import moment from 'moment'; -import { AlertInstanceState } from '../../alerts/types'; +import { AlertInstanceState } from '../../../common/types/alerts'; import { AlertsClient } from '../../../../alerts/server'; import { AlertsFactory } from '../../alerts'; -import { CommonAlertStatus, CommonAlertState, CommonAlertFilter } from '../../../common/types'; +import { + CommonAlertStatus, + CommonAlertState, + CommonAlertFilter, +} from '../../../common/types/alerts'; import { ALERTS } from '../../../common/constants'; import { MonitoringLicenseService } from '../../types'; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts new file mode 100644 index 00000000000000..664ceb1d9411b3 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { get } from 'lodash'; +import { AlertCluster, AlertThreadPoolRejectionsStats } from '../../../common/types/alerts'; + +const invalidNumberValue = (value: number) => { + return isNaN(value) || value === undefined || value === null; +}; + +const getTopHits = (threadType: string, order: string) => ({ + top_hits: { + sort: [ + { + timestamp: { + order, + unmapped_type: 'long', + }, + }, + ], + _source: { + includes: [`node_stats.thread_pool.${threadType}.rejected`, 'source_node.name'], + }, + size: 1, + }, +}); + +export async function fetchThreadPoolRejectionStats( + callCluster: any, + clusters: AlertCluster[], + index: string, + size: number, + threadType: string, + duration: string +): Promise { + const clustersIds = clusters.map((cluster) => cluster.clusterUuid); + const params = { + index, + filterPath: ['aggregations'], + body: { + size: 0, + query: { + bool: { + filter: [ + { + terms: { + cluster_uuid: clustersIds, + }, + }, + { + term: { + type: 'node_stats', + }, + }, + { + range: { + timestamp: { + gte: `now-${duration}`, + }, + }, + }, + ], + }, + }, + aggs: { + clusters: { + terms: { + field: 'cluster_uuid', + size, + }, + aggs: { + nodes: { + terms: { + field: 'source_node.uuid', + size, + }, + aggs: { + most_recent: { + ...getTopHits(threadType, 'desc'), + }, + least_recent: { + ...getTopHits(threadType, 'asc'), + }, + }, + }, + }, + }, + }, + }, + }; + + const response = await callCluster('search', params); + const stats: AlertThreadPoolRejectionsStats[] = []; + const { buckets: clusterBuckets = [] } = response.aggregations.clusters; + + if (!clusterBuckets.length) { + return stats; + } + + for (const clusterBucket of clusterBuckets) { + for (const node of clusterBucket.nodes.buckets) { + const mostRecentDoc = get(node, 'most_recent.hits.hits[0]'); + mostRecentDoc.timestamp = mostRecentDoc.sort[0]; + + const leastRecentDoc = get(node, 'least_recent.hits.hits[0]'); + leastRecentDoc.timestamp = leastRecentDoc.sort[0]; + + if (!mostRecentDoc || mostRecentDoc.timestamp === leastRecentDoc.timestamp) { + continue; + } + + const rejectedPath = `_source.node_stats.thread_pool.${threadType}.rejected`; + const newRejectionCount = Number(get(mostRecentDoc, rejectedPath)); + const oldRejectionCount = Number(get(leastRecentDoc, rejectedPath)); + + if (invalidNumberValue(newRejectionCount) || invalidNumberValue(oldRejectionCount)) { + continue; + } + + const rejectionCount = + oldRejectionCount > newRejectionCount + ? newRejectionCount + : newRejectionCount - oldRejectionCount; + const indexName = mostRecentDoc._index; + const nodeName = get(mostRecentDoc, '_source.source_node.name') || node.key; + const nodeStat = { + rejectionCount, + type: threadType, + clusterUuid: clusterBucket.key, + nodeId: node.key, + nodeName, + ccs: indexName.includes(':') ? indexName.split(':')[0] : null, + }; + stats.push(nodeStat); + } + } + return stats; +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts index d97bc34c2adb05..29a27ac3d05e74 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { handleError } from '../../../../lib/errors'; import { RouteDependencies } from '../../../../types'; import { fetchStatus } from '../../../../lib/alerts/fetch_status'; -import { CommonAlertFilter } from '../../../../../common/types'; +import { CommonAlertFilter } from '../../../../../common/types/alerts'; export function alertStatusRoute(server: any, npRoute: RouteDependencies) { npRoute.router.post(