Skip to content

Commit

Permalink
Index the reason in the log threshold alert executor
Browse files Browse the repository at this point in the history
  • Loading branch information
weltenwort committed Jul 20, 2021
1 parent ef85e6b commit 3e675de
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
PartialAlertParams,
} from '../../../common/alerting/logs/log_threshold/types';
import { formatReason } from './rule_data_formatters';
import { formatRuleData } from './rule_data_formatters';
import { validateExpression } from './validation';

export function createLogThresholdAlertType(): ObservabilityRuleTypeModel<PartialAlertParams> {
Expand All @@ -34,6 +34,6 @@ export function createLogThresholdAlertType(): ObservabilityRuleTypeModel<Partia
}
),
requiresAppContext: false,
format: formatReason,
format: formatRuleData,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,12 @@
* 2.0.
*/

import { i18n } from '@kbn/i18n';
import {
ALERT_EVALUATION_THRESHOLD,
ALERT_EVALUATION_VALUE,
ALERT_ID,
ALERT_START,
} from '@kbn/rule-data-utils';
import { ALERT_REASON, ALERT_START } from '@kbn/rule-data-utils';
import { modifyUrl } from '@kbn/std';
import { fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { ObservabilityRuleTypeFormatter } from '../../../../observability/public';
import {
ComparatorToi18nMap,
logThresholdRuleDataRT,
logThresholdRuleDataSerializedParamsKey,
ratioAlertParamsRT,
} from '../../../common/alerting/logs/log_threshold';

export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => {
const reason = pipe(
logThresholdRuleDataRT.decode(fields),
fold(
() =>
i18n.translate('xpack.infra.logs.alerting.threshold.unknownReasonDescription', {
defaultMessage: 'unknown reason',
}),
(logThresholdRuleData) => {
const params = logThresholdRuleData[logThresholdRuleDataSerializedParamsKey][0];

const actualCount = fields[ALERT_EVALUATION_VALUE];
const groupName = fields[ALERT_ID];
const isGrouped = (params.groupBy?.length ?? 0) > 0;
const isRatio = ratioAlertParamsRT.is(params);
const thresholdCount = fields[ALERT_EVALUATION_THRESHOLD];
const translatedComparator = ComparatorToi18nMap[params.count.comparator];

if (isRatio) {
return i18n.translate('xpack.infra.logs.alerting.threshold.ratioAlertReasonDescription', {
defaultMessage:
'{isGrouped, select, true{{groupName}: } false{}}The log entries ratio is {actualCount} ({translatedComparator} {thresholdCount}).',
values: {
actualCount,
translatedComparator,
groupName,
isGrouped,
thresholdCount,
},
});
} else {
return i18n.translate('xpack.infra.logs.alerting.threshold.countAlertReasonDescription', {
defaultMessage:
'{isGrouped, select, true{{groupName}: } false{}}{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {thresholdCount}) match the configured conditions.',
values: {
actualCount,
translatedComparator,
groupName,
isGrouped,
thresholdCount,
},
});
}
}
)
);

export const formatRuleData: ObservabilityRuleTypeFormatter = ({ fields }) => {
const reason = fields[ALERT_REASON] ?? '';
const alertStartDate = fields[ALERT_START];
const timestamp = alertStartDate != null ? new Date(alertStartDate).valueOf() : null;
const link = modifyUrl('/app/logs/link-to/default/logs', ({ query, ...otherUrlParts }) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

import { estypes } from '@elastic/elasticsearch';
import { i18n } from '@kbn/i18n';
import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils';
import {
ALERT_EVALUATION_THRESHOLD,
ALERT_EVALUATION_VALUE,
ALERT_REASON,
} from '@kbn/rule-data-utils';
import { ElasticsearchClient } from 'kibana/server';
import {
ActionGroup,
Expand All @@ -17,10 +21,6 @@ import {
AlertInstanceState,
AlertTypeState,
} from '../../../../../alerting/server';
import {
logThresholdRuleDataRT,
logThresholdRuleDataSerializedParamsKey,
} from '../../../../common/alerting/logs/log_threshold';
import {
AlertParams,
alertParamsRT,
Expand All @@ -46,6 +46,12 @@ import { decodeOrThrow } from '../../../../common/runtime_types';
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
import { InfraBackendLibs } from '../../infra_types';
import { UNGROUPED_FACTORY_KEY } from '../common/utils';
import {
getReasonMessageForGroupedCountAlert,
getReasonMessageForGroupedRatioAlert,
getReasonMessageForUngroupedCountAlert,
getReasonMessageForUngroupedRatioAlert,
} from './reason_formatters';

export type LogThresholdActionGroups = ActionGroupIdsOf<typeof FIRED_ACTIONS>;
export type LogThresholdAlertTypeParams = AlertParams;
Expand All @@ -60,6 +66,7 @@ type LogThresholdAlertInstance = AlertInstance<
>;
type LogThresholdAlertInstanceFactory = (
id: string,
reason: string,
threshold: number,
value: number
) => LogThresholdAlertInstance;
Expand Down Expand Up @@ -89,15 +96,13 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
>(async ({ services, params }) => {
const { alertWithLifecycle, savedObjectsClient, scopedClusterClient } = services;
const { sources } = libs;
const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, threshold, value) =>
const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, reason, threshold, value) =>
alertWithLifecycle({
id,
fields: {
[ALERT_EVALUATION_THRESHOLD]: threshold,
[ALERT_EVALUATION_VALUE]: value,
...logThresholdRuleDataRT.encode({
[logThresholdRuleDataSerializedParamsKey]: [params],
}),
[ALERT_REASON]: reason,
},
});

Expand Down Expand Up @@ -243,7 +248,12 @@ export const processUngroupedResults = (
const documentCount = results.hits.total.value;

if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, documentCount);
const alertInstance = alertInstanceFactory(
UNGROUPED_FACTORY_KEY,
getReasonMessageForUngroupedCountAlert(documentCount, count.value, count.comparator),
count.value,
documentCount
);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
Expand Down Expand Up @@ -272,7 +282,12 @@ export const processUngroupedRatioResults = (
const ratio = getRatio(numeratorCount, denominatorCount);

if (ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value)) {
const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, ratio);
const alertInstance = alertInstanceFactory(
UNGROUPED_FACTORY_KEY,
getReasonMessageForUngroupedRatioAlert(ratio, count.value, count.comparator),
count.value,
ratio
);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
Expand Down Expand Up @@ -341,7 +356,17 @@ export const processGroupByResults = (
const documentCount = group.documentCount;

if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
const alertInstance = alertInstanceFactory(group.name, count.value, documentCount);
const alertInstance = alertInstanceFactory(
group.name,
getReasonMessageForGroupedCountAlert(
documentCount,
count.value,
count.comparator,
group.name
),
count.value,
documentCount
);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
Expand Down Expand Up @@ -382,7 +407,17 @@ export const processGroupByRatioResults = (
ratio !== undefined &&
checkValueAgainstComparatorMap[count.comparator](ratio, count.value)
) {
const alertInstance = alertInstanceFactory(numeratorGroup.name, count.value, ratio);
const alertInstance = alertInstanceFactory(
numeratorGroup.name,
getReasonMessageForGroupedRatioAlert(
ratio,
count.value,
count.comparator,
numeratorGroup.name
),
count.value,
ratio
);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';
import {
Comparator,
ComparatorToi18nMap,
} from '../../../../common/alerting/logs/log_threshold/types';

export const getReasonMessageForUngroupedCountAlert = (
actualCount: number,
expectedCount: number,
comparator: Comparator
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.ungroupedCountAlertReasonDescription', {
defaultMessage:
'{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions.',
values: {
actualCount,
expectedCount,
translatedComparator: ComparatorToi18nMap[comparator],
},
});

export const getReasonMessageForGroupedCountAlert = (
actualCount: number,
expectedCount: number,
comparator: Comparator,
groupName: string
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription', {
defaultMessage:
'{groupName}: {actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions.',
values: {
actualCount,
expectedCount,
groupName,
translatedComparator: ComparatorToi18nMap[comparator],
},
});

export const getReasonMessageForUngroupedRatioAlert = (
actualRatio: number,
expectedRatio: number,
comparator: Comparator
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.ungroupedRatioAlertReasonDescription', {
defaultMessage:
'The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}).',
values: {
actualRatio,
expectedRatio,
translatedComparator: ComparatorToi18nMap[comparator],
},
});

export const getReasonMessageForGroupedRatioAlert = (
actualRatio: number,
expectedRatio: number,
comparator: Comparator,
groupName: string
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription', {
defaultMessage:
'{groupName}: The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}).',
values: {
actualRatio,
expectedRatio,
groupName,
translatedComparator: ComparatorToi18nMap[comparator],
},
});

0 comments on commit 3e675de

Please sign in to comment.