mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[AO] Add evaluation values for metric threshold and inventory rules (#154255)
Closes #153877 ## Summary This PR adds a new field called `kibana.alert.evaluation.values` to the alert document for metric threshold and inventory rules. This is an array of numbers but depending on the result of the rule execution, the value might be `null` too.  We want to use this result in the metric threshold alert details page, so I checked whether this value can be retrieved correctly there or not:  **Note** I will add tests later, I would like to get feedback about the implementation first. ## 🧪 How to test - Add xpack.observability.unsafe.alertDetails.metrics.enabled: true to the Kibana config - Create a metric threshold and inventory rule that generates an alert - Check the alert document for the `kibana.alert.evaluation.values` field, it should be an array with the result of evaluation for the related criteria - If you are using metricbeat, stop it so the value of evaluation will be null - Go to the alert details page, you should be able to see the main chart even when the evaluation value is null - Check the alert document for the `kibana.alert.evaluation.values` field, it should be an array including a null value
This commit is contained in:
parent
fd26017162
commit
11721308fc
5 changed files with 51 additions and 14 deletions
|
@ -6,7 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_EVALUATION_VALUES,
|
||||
} from '@kbn/rule-data-utils';
|
||||
|
||||
export const legacyExperimentalFieldMap = {
|
||||
[ALERT_EVALUATION_THRESHOLD]: {
|
||||
|
@ -15,6 +19,12 @@ export const legacyExperimentalFieldMap = {
|
|||
required: false,
|
||||
},
|
||||
[ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false },
|
||||
[ALERT_EVALUATION_VALUES]: {
|
||||
type: 'scaled_float',
|
||||
scaling_factor: 100,
|
||||
required: false,
|
||||
array: true,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type ExperimentalRuleFieldMap = typeof legacyExperimentalFieldMap;
|
||||
|
|
|
@ -85,6 +85,7 @@ const EVENT_MODULE = 'event.module' as const;
|
|||
const ALERT_BUILDING_BLOCK_TYPE = `${ALERT_NAMESPACE}.building_block_type` as const;
|
||||
const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const;
|
||||
const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const;
|
||||
const ALERT_EVALUATION_VALUES = `${ALERT_NAMESPACE}.evaluation.values` as const;
|
||||
|
||||
// Fields pertaining to the rule associated with the alert
|
||||
const ALERT_RULE_EXCEPTIONS_LIST = `${ALERT_RULE_NAMESPACE}.exceptions_list` as const;
|
||||
|
@ -125,6 +126,7 @@ const fields = {
|
|||
ALERT_END,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_EVALUATION_VALUES,
|
||||
ALERT_FLAPPING,
|
||||
ALERT_MAINTENANCE_WINDOW_IDS,
|
||||
ALERT_INSTANCE_ID,
|
||||
|
@ -192,6 +194,7 @@ export {
|
|||
ALERT_BUILDING_BLOCK_TYPE,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_EVALUATION_VALUES,
|
||||
ALERT_RULE_EXCEPTIONS_LIST,
|
||||
ALERT_RULE_NAMESPACE_FIELD,
|
||||
ALERT_THREAT_FRAMEWORK,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_REASON, ALERT_ACTION_GROUP } from '@kbn/rule-data-utils';
|
||||
import { ALERT_REASON, ALERT_ACTION_GROUP, ALERT_EVALUATION_VALUES } from '@kbn/rule-data-utils';
|
||||
import { first, get } from 'lodash';
|
||||
import {
|
||||
ActionGroup,
|
||||
|
@ -65,8 +65,7 @@ type InventoryMetricThresholdAlertFactory = (
|
|||
reason: string,
|
||||
actionGroup: InventoryThrehsoldActionGroup,
|
||||
additionalContext?: AdditionalContext | null,
|
||||
threshold?: number | undefined,
|
||||
value?: number | undefined
|
||||
evaluationValues?: Array<number | null>
|
||||
) => InventoryMetricThresholdAlert;
|
||||
|
||||
export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||
|
@ -109,13 +108,15 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
id,
|
||||
reason,
|
||||
actionGroup,
|
||||
additionalContext
|
||||
additionalContext,
|
||||
evaluationValues
|
||||
) =>
|
||||
alertWithLifecycle({
|
||||
id,
|
||||
fields: {
|
||||
[ALERT_REASON]: reason,
|
||||
[ALERT_ACTION_GROUP]: actionGroup,
|
||||
[ALERT_EVALUATION_VALUES]: evaluationValues,
|
||||
...flattenAdditionalContext(additionalContext),
|
||||
},
|
||||
});
|
||||
|
@ -243,7 +244,18 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
new Set([...(additionalContext.tags ?? []), ...ruleTags])
|
||||
);
|
||||
|
||||
const alert = alertFactory(group, reason, actionGroupId, additionalContext);
|
||||
const evaluationValues = results.reduce((acc: Array<number | null>, result) => {
|
||||
acc.push(result[group].currentValue);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const alert = alertFactory(
|
||||
group,
|
||||
reason,
|
||||
actionGroupId,
|
||||
additionalContext,
|
||||
evaluationValues
|
||||
);
|
||||
const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString();
|
||||
const alertUuid = getAlertUuid(group);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_ACTION_GROUP, ALERT_REASON } from '@kbn/rule-data-utils';
|
||||
import { ALERT_ACTION_GROUP, ALERT_EVALUATION_VALUES, ALERT_REASON } from '@kbn/rule-data-utils';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
ActionGroupIdsOf,
|
||||
|
@ -51,8 +51,8 @@ export type MetricThresholdRuleTypeState = RuleTypeState & {
|
|||
groupBy?: string | string[];
|
||||
filterQuery?: string;
|
||||
};
|
||||
export type MetricThresholdAlertState = AlertState; // no specific instace state used
|
||||
export type MetricThresholdAlertContext = AlertContext; // no specific instace state used
|
||||
export type MetricThresholdAlertState = AlertState; // no specific instance state used
|
||||
export type MetricThresholdAlertContext = AlertContext; // no specific instance state used
|
||||
|
||||
export const FIRED_ACTIONS_ID = 'metrics.threshold.fired';
|
||||
export const WARNING_ACTIONS_ID = 'metrics.threshold.warning';
|
||||
|
@ -79,8 +79,7 @@ type MetricThresholdAlertFactory = (
|
|||
reason: string,
|
||||
actionGroup: MetricThresholdActionGroup,
|
||||
additionalContext?: AdditionalContext | null,
|
||||
threshold?: number | undefined,
|
||||
value?: number | undefined
|
||||
evaluationValues?: Array<number | null>
|
||||
) => MetricThresholdAlert;
|
||||
|
||||
export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||
|
@ -117,13 +116,15 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
|||
id,
|
||||
reason,
|
||||
actionGroup,
|
||||
additionalContext
|
||||
additionalContext,
|
||||
evaluationValues
|
||||
) =>
|
||||
alertWithLifecycle({
|
||||
id,
|
||||
fields: {
|
||||
[ALERT_REASON]: reason,
|
||||
[ALERT_ACTION_GROUP]: actionGroup,
|
||||
[ALERT_EVALUATION_VALUES]: evaluationValues,
|
||||
...flattenAdditionalContext(additionalContext),
|
||||
},
|
||||
});
|
||||
|
@ -295,7 +296,18 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
|||
new Set([...(additionalContext.tags ?? []), ...options.rule.tags])
|
||||
);
|
||||
|
||||
const alert = alertFactory(`${group}`, reason, actionGroupId, additionalContext);
|
||||
const evaluationValues = alertResults.reduce((acc: Array<number | null>, result) => {
|
||||
acc.push(result[group].currentValue);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const alert = alertFactory(
|
||||
`${group}`,
|
||||
reason,
|
||||
actionGroupId,
|
||||
additionalContext,
|
||||
evaluationValues
|
||||
);
|
||||
const alertUuid = getAlertUuid(group);
|
||||
scheduledActionsCount++;
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ type CastSingle<T extends t.Type<any>> = t.Type<
|
|||
>;
|
||||
|
||||
const createCastArrayRt = <T extends t.Type<any>>(type: T): CastArray<T> => {
|
||||
const union = t.union([type, t.array(type)]);
|
||||
const union = t.union([type, t.array(t.union([type, t.nullType]))]);
|
||||
|
||||
return new t.Type('castArray', union.is, union.validate, (a) => (Array.isArray(a) ? a : [a]));
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue