[Logs UI] Remove UUID from Alert Instances (#71340)
* [Logs UI] Remove UUID from Alert Instances * Fix bad template string Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
5ef8d3f509
commit
6c4fc9ca20
|
@ -29,3 +29,5 @@ export const validateIsStringElasticsearchJSONFilter = (value: string) => {
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UNGROUPED_FACTORY_KEY = '*';
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { parseFilterQuery } from '../../../utils/serialized_query';
|
||||||
import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types';
|
import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types';
|
||||||
import { InfraTimerangeInput } from '../../../../common/http_api/snapshot_api';
|
import { InfraTimerangeInput } from '../../../../common/http_api/snapshot_api';
|
||||||
import { InfraSourceConfiguration } from '../../sources';
|
import { InfraSourceConfiguration } from '../../sources';
|
||||||
|
import { UNGROUPED_FACTORY_KEY } from '../common/utils';
|
||||||
|
|
||||||
type ConditionResult = InventoryMetricConditions & {
|
type ConditionResult = InventoryMetricConditions & {
|
||||||
shouldFire: boolean | boolean[];
|
shouldFire: boolean | boolean[];
|
||||||
|
@ -129,14 +130,14 @@ const getData = async (
|
||||||
const causedByType = e.body?.error?.caused_by?.type;
|
const causedByType = e.body?.error?.caused_by?.type;
|
||||||
if (causedByType === 'too_many_buckets_exception') {
|
if (causedByType === 'too_many_buckets_exception') {
|
||||||
return {
|
return {
|
||||||
'*': {
|
[UNGROUPED_FACTORY_KEY]: {
|
||||||
[TOO_MANY_BUCKETS_PREVIEW_EXCEPTION]: true,
|
[TOO_MANY_BUCKETS_PREVIEW_EXCEPTION]: true,
|
||||||
maxBuckets: e.body.error.caused_by.max_buckets,
|
maxBuckets: e.body.error.caused_by.max_buckets,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { '*': undefined };
|
return { [UNGROUPED_FACTORY_KEY]: undefined };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -54,19 +54,19 @@ services.alertInstanceFactory.mockImplementation((instanceId: string) => {
|
||||||
/*
|
/*
|
||||||
* Helper functions
|
* Helper functions
|
||||||
*/
|
*/
|
||||||
function getAlertState(instanceId: string): AlertStates {
|
function getAlertState(): AlertStates {
|
||||||
const alert = alertInstances.get(`${instanceId}-*`);
|
const alert = alertInstances.get('*');
|
||||||
if (alert) {
|
if (alert) {
|
||||||
return alert.state.alertState;
|
return alert.state.alertState;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Could not find alert instance `' + instanceId + '`');
|
throw new Error('Could not find alert instance');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Executor instance (our test subject)
|
* Executor instance (our test subject)
|
||||||
*/
|
*/
|
||||||
const executor = (createLogThresholdExecutor('test', libsMock) as unknown) as (opts: {
|
const executor = (createLogThresholdExecutor(libsMock) as unknown) as (opts: {
|
||||||
params: LogDocumentCountAlertParams;
|
params: LogDocumentCountAlertParams;
|
||||||
services: { callCluster: AlertExecutorOptions['params']['callCluster'] };
|
services: { callCluster: AlertExecutorOptions['params']['callCluster'] };
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
|
@ -109,30 +109,30 @@ describe('Ungrouped alerts', () => {
|
||||||
describe('Comparators trigger alerts correctly', () => {
|
describe('Comparators trigger alerts correctly', () => {
|
||||||
it('does not alert when counts do not reach the threshold', async () => {
|
it('does not alert when counts do not reach the threshold', async () => {
|
||||||
await callExecutor([0, Comparator.GT, 1]);
|
await callExecutor([0, Comparator.GT, 1]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.OK);
|
expect(getAlertState()).toBe(AlertStates.OK);
|
||||||
|
|
||||||
await callExecutor([0, Comparator.GT_OR_EQ, 1]);
|
await callExecutor([0, Comparator.GT_OR_EQ, 1]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.OK);
|
expect(getAlertState()).toBe(AlertStates.OK);
|
||||||
|
|
||||||
await callExecutor([1, Comparator.LT, 0]);
|
await callExecutor([1, Comparator.LT, 0]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.OK);
|
expect(getAlertState()).toBe(AlertStates.OK);
|
||||||
|
|
||||||
await callExecutor([1, Comparator.LT_OR_EQ, 0]);
|
await callExecutor([1, Comparator.LT_OR_EQ, 0]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.OK);
|
expect(getAlertState()).toBe(AlertStates.OK);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('alerts when counts reach the threshold', async () => {
|
it('alerts when counts reach the threshold', async () => {
|
||||||
await callExecutor([2, Comparator.GT, 1]);
|
await callExecutor([2, Comparator.GT, 1]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.ALERT);
|
expect(getAlertState()).toBe(AlertStates.ALERT);
|
||||||
|
|
||||||
await callExecutor([1, Comparator.GT_OR_EQ, 1]);
|
await callExecutor([1, Comparator.GT_OR_EQ, 1]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.ALERT);
|
expect(getAlertState()).toBe(AlertStates.ALERT);
|
||||||
|
|
||||||
await callExecutor([1, Comparator.LT, 2]);
|
await callExecutor([1, Comparator.LT, 2]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.ALERT);
|
expect(getAlertState()).toBe(AlertStates.ALERT);
|
||||||
|
|
||||||
await callExecutor([2, Comparator.LT_OR_EQ, 2]);
|
await callExecutor([2, Comparator.LT_OR_EQ, 2]);
|
||||||
expect(getAlertState('test')).toBe(AlertStates.ALERT);
|
expect(getAlertState()).toBe(AlertStates.ALERT);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ import { InfraBackendLibs } from '../../infra_types';
|
||||||
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
|
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
|
||||||
import { InfraSource } from '../../../../common/http_api/source_api';
|
import { InfraSource } from '../../../../common/http_api/source_api';
|
||||||
import { decodeOrThrow } from '../../../../common/runtime_types';
|
import { decodeOrThrow } from '../../../../common/runtime_types';
|
||||||
|
import { UNGROUPED_FACTORY_KEY } from '../common/utils';
|
||||||
|
|
||||||
const UNGROUPED_FACTORY_KEY = '*';
|
|
||||||
const COMPOSITE_GROUP_SIZE = 40;
|
const COMPOSITE_GROUP_SIZE = 40;
|
||||||
|
|
||||||
const checkValueAgainstComparatorMap: {
|
const checkValueAgainstComparatorMap: {
|
||||||
|
@ -34,7 +34,7 @@ const checkValueAgainstComparatorMap: {
|
||||||
[Comparator.LT_OR_EQ]: (a: number, b: number) => a <= b,
|
[Comparator.LT_OR_EQ]: (a: number, b: number) => a <= b,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createLogThresholdExecutor = (alertId: string, libs: InfraBackendLibs) =>
|
export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||||
async function ({ services, params }: AlertExecutorOptions) {
|
async function ({ services, params }: AlertExecutorOptions) {
|
||||||
const { alertInstanceFactory, savedObjectsClient, callCluster } = services;
|
const { alertInstanceFactory, savedObjectsClient, callCluster } = services;
|
||||||
const { sources } = libs;
|
const { sources } = libs;
|
||||||
|
@ -42,7 +42,7 @@ export const createLogThresholdExecutor = (alertId: string, libs: InfraBackendLi
|
||||||
|
|
||||||
const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default');
|
const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default');
|
||||||
const indexPattern = sourceConfiguration.configuration.logAlias;
|
const indexPattern = sourceConfiguration.configuration.logAlias;
|
||||||
const alertInstance = alertInstanceFactory(alertId);
|
const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const validatedParams = decodeOrThrow(LogDocumentCountAlertParamsRT)(params);
|
const validatedParams = decodeOrThrow(LogDocumentCountAlertParamsRT)(params);
|
||||||
|
@ -60,15 +60,13 @@ export const createLogThresholdExecutor = (alertId: string, libs: InfraBackendLi
|
||||||
processGroupByResults(
|
processGroupByResults(
|
||||||
await getGroupedResults(query, callCluster),
|
await getGroupedResults(query, callCluster),
|
||||||
validatedParams,
|
validatedParams,
|
||||||
alertInstanceFactory,
|
alertInstanceFactory
|
||||||
alertId
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
processUngroupedResults(
|
processUngroupedResults(
|
||||||
await getUngroupedResults(query, callCluster),
|
await getUngroupedResults(query, callCluster),
|
||||||
validatedParams,
|
validatedParams,
|
||||||
alertInstanceFactory,
|
alertInstanceFactory
|
||||||
alertId
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -83,12 +81,11 @@ export const createLogThresholdExecutor = (alertId: string, libs: InfraBackendLi
|
||||||
const processUngroupedResults = (
|
const processUngroupedResults = (
|
||||||
results: UngroupedSearchQueryResponse,
|
results: UngroupedSearchQueryResponse,
|
||||||
params: LogDocumentCountAlertParams,
|
params: LogDocumentCountAlertParams,
|
||||||
alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'],
|
alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory']
|
||||||
alertId: string
|
|
||||||
) => {
|
) => {
|
||||||
const { count, criteria } = params;
|
const { count, criteria } = params;
|
||||||
|
|
||||||
const alertInstance = alertInstanceFactory(`${alertId}-${UNGROUPED_FACTORY_KEY}`);
|
const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY);
|
||||||
const documentCount = results.hits.total.value;
|
const documentCount = results.hits.total.value;
|
||||||
|
|
||||||
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
|
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
|
||||||
|
@ -116,8 +113,7 @@ interface ReducedGroupByResults {
|
||||||
const processGroupByResults = (
|
const processGroupByResults = (
|
||||||
results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'],
|
results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'],
|
||||||
params: LogDocumentCountAlertParams,
|
params: LogDocumentCountAlertParams,
|
||||||
alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'],
|
alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory']
|
||||||
alertId: string
|
|
||||||
) => {
|
) => {
|
||||||
const { count, criteria } = params;
|
const { count, criteria } = params;
|
||||||
|
|
||||||
|
@ -128,7 +124,7 @@ const processGroupByResults = (
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
groupResults.forEach((group) => {
|
groupResults.forEach((group) => {
|
||||||
const alertInstance = alertInstanceFactory(`${alertId}-${group.name}`);
|
const alertInstance = alertInstanceFactory(group.name);
|
||||||
const documentCount = group.documentCount;
|
const documentCount = group.documentCount;
|
||||||
|
|
||||||
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
|
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import uuid from 'uuid';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
import { PluginSetupContract } from '../../../../../alerts/server';
|
import { PluginSetupContract } from '../../../../../alerts/server';
|
||||||
|
@ -71,8 +70,6 @@ export async function registerLogThresholdAlertType(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alertUUID = uuid.v4();
|
|
||||||
|
|
||||||
alertingPlugin.registerType({
|
alertingPlugin.registerType({
|
||||||
id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
|
id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
|
||||||
name: 'Log threshold',
|
name: 'Log threshold',
|
||||||
|
@ -87,7 +84,7 @@ export async function registerLogThresholdAlertType(
|
||||||
},
|
},
|
||||||
defaultActionGroupId: FIRED_ACTIONS.id,
|
defaultActionGroupId: FIRED_ACTIONS.id,
|
||||||
actionGroups: [FIRED_ACTIONS],
|
actionGroups: [FIRED_ACTIONS],
|
||||||
executor: createLogThresholdExecutor(alertUUID, libs),
|
executor: createLogThresholdExecutor(libs),
|
||||||
actionVariables: {
|
actionVariables: {
|
||||||
context: [
|
context: [
|
||||||
{ name: 'matchingDocuments', description: documentCountActionVariableDescription },
|
{ name: 'matchingDocuments', description: documentCountActionVariableDescription },
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler
|
||||||
import { AlertServices, AlertExecutorOptions } from '../../../../../../alerts/server';
|
import { AlertServices, AlertExecutorOptions } from '../../../../../../alerts/server';
|
||||||
import { getAllCompositeData } from '../../../../utils/get_all_composite_data';
|
import { getAllCompositeData } from '../../../../utils/get_all_composite_data';
|
||||||
import { DOCUMENT_COUNT_I18N } from '../../common/messages';
|
import { DOCUMENT_COUNT_I18N } from '../../common/messages';
|
||||||
|
import { UNGROUPED_FACTORY_KEY } from '../../common/utils';
|
||||||
import { MetricExpressionParams, Comparator, Aggregators } from '../types';
|
import { MetricExpressionParams, Comparator, Aggregators } from '../types';
|
||||||
import { getElasticsearchMetricQuery } from './metric_query';
|
import { getElasticsearchMetricQuery } from './metric_query';
|
||||||
|
|
||||||
|
@ -133,21 +134,21 @@ const getMetric: (
|
||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { '*': getValuesFromAggregations(result.aggregations, aggType) };
|
return { [UNGROUPED_FACTORY_KEY]: getValuesFromAggregations(result.aggregations, aggType) };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (timeframe) {
|
if (timeframe) {
|
||||||
// This code should only ever be reached when previewing the alert, not executing it
|
// This code should only ever be reached when previewing the alert, not executing it
|
||||||
const causedByType = e.body?.error?.caused_by?.type;
|
const causedByType = e.body?.error?.caused_by?.type;
|
||||||
if (causedByType === 'too_many_buckets_exception') {
|
if (causedByType === 'too_many_buckets_exception') {
|
||||||
return {
|
return {
|
||||||
'*': {
|
[UNGROUPED_FACTORY_KEY]: {
|
||||||
[TOO_MANY_BUCKETS_PREVIEW_EXCEPTION]: true,
|
[TOO_MANY_BUCKETS_PREVIEW_EXCEPTION]: true,
|
||||||
maxBuckets: e.body.error.caused_by.max_buckets,
|
maxBuckets: e.body.error.caused_by.max_buckets,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { '*': NaN }; // Trigger an Error state
|
return { [UNGROUPED_FACTORY_KEY]: NaN }; // Trigger an Error state
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue