diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts index 0914fab00dbe2..321273c656216 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts @@ -36,9 +36,7 @@ export async function getChartPreviewData( alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset, buckets: number ) { - const indexPattern = resolvedLogSourceConfiguration.indices; - const timestampField = resolvedLogSourceConfiguration.timestampField; - + const { indices, timestampField, runtimeMappings } = resolvedLogSourceConfiguration; const { groupBy, timeSize, timeUnit } = alertParams; const isGrouped = groupBy && groupBy.length > 0 ? true : false; @@ -51,8 +49,8 @@ export async function getChartPreviewData( const { rangeFilter } = buildFiltersFromCriteria(expandedAlertParams, timestampField); const query = isGrouped - ? getGroupedESQuery(expandedAlertParams, timestampField, indexPattern) - : getUngroupedESQuery(expandedAlertParams, timestampField, indexPattern); + ? getGroupedESQuery(expandedAlertParams, timestampField, indices, runtimeMappings) + : getUngroupedESQuery(expandedAlertParams, timestampField, indices, runtimeMappings); if (!query) { throw new Error('ES query could not be built from the provided alert params'); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts index d2533fb4d79bc..1c1edb3ea8328 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts @@ -24,6 +24,7 @@ import { GroupedSearchQueryResponse, } from '../../../../common/alerting/logs/log_threshold/types'; import { alertsMock } from '../../../../../alerting/server/mocks'; +import { estypes } from '@elastic/elasticsearch'; // Mocks // const numericField = { @@ -69,6 +70,16 @@ const baseAlertParams: Pick = { const TIMESTAMP_FIELD = '@timestamp'; const FILEBEAT_INDEX = 'filebeat-*'; +const runtimeMappings: estypes.RuntimeFields = { + runtime_field: { + type: 'keyword', + script: { + lang: 'painless', + source: 'emit("a runtime value")', + }, + }, +}; + describe('Log threshold executor', () => { describe('Comparators', () => { test('Correctly categorises positive comparators', () => { @@ -188,11 +199,16 @@ describe('Log threshold executor', () => { ...baseAlertParams, criteria: [...positiveCriteria, ...negativeCriteria], }; - const query = getUngroupedESQuery(alertParams, TIMESTAMP_FIELD, FILEBEAT_INDEX); + const query = getUngroupedESQuery( + alertParams, + TIMESTAMP_FIELD, + FILEBEAT_INDEX, + runtimeMappings + ); expect(query).toEqual({ index: 'filebeat-*', - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, body: { track_total_hits: true, query: { @@ -274,6 +290,15 @@ describe('Log threshold executor', () => { ], }, }, + runtime_mappings: { + runtime_field: { + type: 'keyword', + script: { + lang: 'painless', + source: 'emit("a runtime value")', + }, + }, + }, size: 0, }, }); @@ -285,11 +310,16 @@ describe('Log threshold executor', () => { groupBy: ['host.name'], criteria: [...positiveCriteria, ...negativeCriteria], }; - const query = getGroupedESQuery(alertParams, TIMESTAMP_FIELD, FILEBEAT_INDEX); + const query = getGroupedESQuery( + alertParams, + TIMESTAMP_FIELD, + FILEBEAT_INDEX, + runtimeMappings + ); expect(query).toEqual({ index: 'filebeat-*', - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, body: { query: { bool: { @@ -405,6 +435,15 @@ describe('Log threshold executor', () => { }, }, }, + runtime_mappings: { + runtime_field: { + type: 'keyword', + script: { + lang: 'painless', + source: 'emit("a runtime value")', + }, + }, + }, size: 0, }, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index b81219b1afda2..3e910e5dfbf46 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { ElasticsearchClient } from 'kibana/server'; +import { estypes } from '@elastic/elasticsearch'; import { AlertExecutorOptions, AlertServices, @@ -73,15 +74,13 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => const { sources } = libs; const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default'); - const resolvedLogSourceConfiguration = await resolveLogSourceConfiguration( + const { indices, timestampField, runtimeMappings } = await resolveLogSourceConfiguration( sourceConfiguration.configuration, await libs.framework.getIndexPatternsService( savedObjectsClient, scopedClusterClient.asCurrentUser ) ); - const indexPattern = resolvedLogSourceConfiguration.indices; - const timestampField = resolvedLogSourceConfiguration.timestampField; try { const validatedParams = decodeOrThrow(alertParamsRT)(params); @@ -90,7 +89,8 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => await executeAlert( validatedParams, timestampField, - indexPattern, + indices, + runtimeMappings, scopedClusterClient.asCurrentUser, alertInstanceFactory ); @@ -98,7 +98,8 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => await executeRatioAlert( validatedParams, timestampField, - indexPattern, + indices, + runtimeMappings, scopedClusterClient.asCurrentUser, alertInstanceFactory ); @@ -112,10 +113,11 @@ async function executeAlert( alertParams: CountAlertParams, timestampField: string, indexPattern: string, + runtimeMappings: estypes.RuntimeFields, esClient: ElasticsearchClient, alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { - const query = getESQuery(alertParams, timestampField, indexPattern); + const query = getESQuery(alertParams, timestampField, indexPattern, runtimeMappings); if (!query) { throw new Error('ES query could not be built from the provided alert params'); @@ -142,6 +144,7 @@ async function executeRatioAlert( alertParams: RatioAlertParams, timestampField: string, indexPattern: string, + runtimeMappings: estypes.RuntimeFields, esClient: ElasticsearchClient, alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { @@ -156,8 +159,13 @@ async function executeRatioAlert( criteria: getDenominator(alertParams.criteria), }; - const numeratorQuery = getESQuery(numeratorParams, timestampField, indexPattern); - const denominatorQuery = getESQuery(denominatorParams, timestampField, indexPattern); + const numeratorQuery = getESQuery(numeratorParams, timestampField, indexPattern, runtimeMappings); + const denominatorQuery = getESQuery( + denominatorParams, + timestampField, + indexPattern, + runtimeMappings + ); if (!numeratorQuery || !denominatorQuery) { throw new Error('ES query could not be built from the provided ratio alert params'); @@ -189,11 +197,12 @@ async function executeRatioAlert( const getESQuery = ( alertParams: Omit & { criteria: CountCriteria }, timestampField: string, - indexPattern: string + indexPattern: string, + runtimeMappings: estypes.RuntimeFields ) => { return hasGroupBy(alertParams) - ? getGroupedESQuery(alertParams, timestampField, indexPattern) - : getUngroupedESQuery(alertParams, timestampField, indexPattern); + ? getGroupedESQuery(alertParams, timestampField, indexPattern, runtimeMappings) + : getUngroupedESQuery(alertParams, timestampField, indexPattern, runtimeMappings); }; export const processUngroupedResults = ( @@ -423,8 +432,9 @@ export const buildFiltersFromCriteria = ( export const getGroupedESQuery = ( params: Pick & { criteria: CountCriteria }, timestampField: string, - index: string -): object | undefined => { + index: string, + runtimeMappings: estypes.RuntimeFields +): estypes.SearchRequest | undefined => { const { groupBy } = params; if (!groupBy || !groupBy.length) { @@ -460,20 +470,21 @@ export const getGroupedESQuery = ( }, }; - const body = { + const body: estypes.SearchRequest['body'] = { query: { bool: { filter: [groupedRangeFilter], }, }, aggregations, + runtime_mappings: runtimeMappings, size: 0, }; return { index, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, body, }; }; @@ -481,14 +492,15 @@ export const getGroupedESQuery = ( export const getUngroupedESQuery = ( params: Pick & { criteria: CountCriteria }, timestampField: string, - index: string + index: string, + runtimeMappings: estypes.RuntimeFields ): object => { const { rangeFilter, mustFilters, mustNotFilters } = buildFiltersFromCriteria( params, timestampField ); - const body = { + const body: estypes.SearchRequest['body'] = { // Ensure we accurately track the hit count for the ungrouped case, otherwise we can only ensure accuracy up to 10,000. track_total_hits: true, query: { @@ -497,13 +509,14 @@ export const getUngroupedESQuery = ( ...(mustNotFilters.length > 0 && { must_not: mustNotFilters }), }, }, + runtime_mappings: runtimeMappings, size: 0, }; return { index, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, body, }; };