Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -69,6 +70,16 @@ const baseAlertParams: Pick<AlertParams, 'count' | 'timeSize' | 'timeUnit'> = {
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', () => {
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -274,6 +290,15 @@ describe('Log threshold executor', () => {
],
},
},
runtime_mappings: {
runtime_field: {
type: 'keyword',
script: {
lang: 'painless',
source: 'emit("a runtime value")',
},
},
},
size: 0,
},
});
Expand All @@ -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: {
Expand Down Expand Up @@ -405,6 +435,15 @@ describe('Log threshold executor', () => {
},
},
},
runtime_mappings: {
runtime_field: {
type: 'keyword',
script: {
lang: 'painless',
source: 'emit("a runtime value")',
},
},
},
size: 0,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { i18n } from '@kbn/i18n';
import { ElasticsearchClient } from 'kibana/server';
import { estypes } from '@elastic/elasticsearch';
import {
AlertExecutorOptions,
AlertServices,
Expand Down Expand Up @@ -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);
Expand All @@ -90,15 +89,17 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
await executeAlert(
validatedParams,
timestampField,
indexPattern,
indices,
runtimeMappings,
scopedClusterClient.asCurrentUser,
alertInstanceFactory
);
} else {
await executeRatioAlert(
validatedParams,
timestampField,
indexPattern,
indices,
runtimeMappings,
scopedClusterClient.asCurrentUser,
alertInstanceFactory
);
Expand All @@ -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');
Expand All @@ -142,6 +144,7 @@ async function executeRatioAlert(
alertParams: RatioAlertParams,
timestampField: string,
indexPattern: string,
runtimeMappings: estypes.RuntimeFields,
esClient: ElasticsearchClient,
alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory']
) {
Expand All @@ -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');
Expand Down Expand Up @@ -189,11 +197,12 @@ async function executeRatioAlert(
const getESQuery = (
alertParams: Omit<AlertParams, 'criteria'> & { 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 = (
Expand Down Expand Up @@ -423,8 +432,9 @@ export const buildFiltersFromCriteria = (
export const getGroupedESQuery = (
params: Pick<AlertParams, 'timeSize' | 'timeUnit' | 'groupBy'> & { criteria: CountCriteria },
timestampField: string,
index: string
): object | undefined => {
index: string,
runtimeMappings: estypes.RuntimeFields
): estypes.SearchRequest | undefined => {
const { groupBy } = params;

if (!groupBy || !groupBy.length) {
Expand Down Expand Up @@ -460,35 +470,37 @@ export const getGroupedESQuery = (
},
};

const body = {
const body: estypes.SearchRequest['body'] = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍

query: {
bool: {
filter: [groupedRangeFilter],
},
},
aggregations,
runtime_mappings: runtimeMappings,
size: 0,
};

return {
index,
allowNoIndices: true,
ignoreUnavailable: true,
allow_no_indices: true,
ignore_unavailable: true,
body,
};
};

export const getUngroupedESQuery = (
params: Pick<AlertParams, 'timeSize' | 'timeUnit'> & { 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: {
Expand All @@ -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,
};
};
Expand Down