[RAM] Fix rule details event log aggregation filtering (#137913) (#138217)

* Fix exec log aggregations

* Fix tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit d66ae141a6)

Co-authored-by: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2022-08-05 19:07:12 -04:00 committed by GitHub
parent 345088db84
commit d8d331ee85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 21 deletions

View file

@ -142,7 +142,35 @@ describe('getExecutionLogAggregation', () => {
},
},
aggs: {
executionUuidCardinality: { cardinality: { field: 'kibana.alert.rule.execution.uuid' } },
executionUuidCardinality: {
aggs: {
executionUuidCardinality: {
cardinality: { field: 'kibana.alert.rule.execution.uuid' },
},
},
filter: {
bool: {
must: [
{
bool: {
must: [
{
match: {
'event.action': 'execute',
},
},
{
match: {
'event.provider': 'alerting',
},
},
],
},
},
],
},
},
},
executionUuid: {
terms: {
field: 'kibana.alert.rule.execution.uuid',
@ -175,12 +203,28 @@ describe('getExecutionLogAggregation', () => {
},
aggs: { actionOutcomes: { terms: { field: 'event.outcome', size: 2 } } },
},
minExecutionUuidBucket: {
bucket_selector: {
buckets_path: {
count: 'ruleExecution._count',
},
script: {
source: 'params.count > 0',
},
},
},
ruleExecution: {
filter: {
bool: {
must: [
{ match: { 'event.action': 'execute' } },
{ match: { 'event.provider': 'alerting' } },
{
bool: {
must: [
{ match: { 'event.action': 'execute' } },
{ match: { 'event.provider': 'alerting' } },
],
},
},
],
},
},
@ -448,7 +492,9 @@ describe('formatExecutionLogResult', () => {
],
},
executionUuidCardinality: {
value: 374,
executionUuidCardinality: {
value: 374,
},
},
},
},
@ -683,7 +729,9 @@ describe('formatExecutionLogResult', () => {
],
},
executionUuidCardinality: {
value: 374,
executionUuidCardinality: {
value: 374,
},
},
},
},
@ -910,7 +958,9 @@ describe('formatExecutionLogResult', () => {
],
},
executionUuidCardinality: {
value: 374,
executionUuidCardinality: {
value: 374,
},
},
},
},
@ -1142,7 +1192,9 @@ describe('formatExecutionLogResult', () => {
],
},
executionUuidCardinality: {
value: 417,
executionUuidCardinality: {
value: 417,
},
},
},
},

View file

@ -9,6 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import Boom from '@hapi/boom';
import { flatMap, get } from 'lodash';
import { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server';
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
import { parseDuration } from '.';
import { IExecutionLog, IExecutionLogResult } from '../../common';
@ -74,9 +75,12 @@ export interface ExecutionUuidAggResult<TBucket = IExecutionUuidAggBucket>
interface ExcludeExecuteStartAggResult extends estypes.AggregationsAggregateBase {
executionUuid: ExecutionUuidAggResult;
executionUuidCardinality: estypes.AggregationsCardinalityAggregate;
executionUuidCardinality: {
executionUuidCardinality: estypes.AggregationsCardinalityAggregate;
};
}
export interface IExecutionLogAggOptions {
filter?: string;
page: number;
perPage: number;
sort: estypes.Sort;
@ -95,7 +99,12 @@ const ExecutionLogSortFields: Record<string, string> = {
num_new_alerts: 'ruleExecution>numNewAlerts',
};
export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLogAggOptions) {
export function getExecutionLogAggregation({
filter,
page,
perPage,
sort,
}: IExecutionLogAggOptions) {
// Check if valid sort fields
const sortFields = flatMap(sort as estypes.SortCombinations[], (s) => Object.keys(s));
for (const field of sortFields) {
@ -118,6 +127,13 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo
throw Boom.badRequest(`Invalid perPage field "${perPage}" - must be greater than 0`);
}
let dslFilterQuery: estypes.QueryDslBoolQuery['filter'];
try {
dslFilterQuery = filter ? toElasticsearchQuery(fromKueryExpression(filter)) : undefined;
} catch (err) {
throw Boom.badRequest(`Invalid kuery syntax for filter ${filter}`);
}
return {
excludeExecuteStart: {
filter: {
@ -134,8 +150,18 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo
aggs: {
// Get total number of executions
executionUuidCardinality: {
cardinality: {
field: EXECUTION_UUID_FIELD,
filter: {
bool: {
...(dslFilterQuery ? { filter: dslFilterQuery } : {}),
must: [getProviderAndActionFilter('alerting', 'execute')],
},
},
aggs: {
executionUuidCardinality: {
cardinality: {
field: EXECUTION_UUID_FIELD,
},
},
},
},
executionUuid: {
@ -169,7 +195,12 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo
},
// Filter by rule execute doc and get information from this event
ruleExecution: {
filter: getProviderAndActionFilter('alerting', 'execute'),
filter: {
bool: {
...(dslFilterQuery ? { filter: dslFilterQuery } : {}),
must: [getProviderAndActionFilter('alerting', 'execute')],
},
},
aggs: {
executeStartTime: {
min: {
@ -235,6 +266,17 @@ export function getExecutionLogAggregation({ page, perPage, sort }: IExecutionLo
timeoutMessage: {
filter: getProviderAndActionFilter('alerting', 'execute-timeout'),
},
// Filter out execution UUID buckets where ruleExecution doc count is 0
minExecutionUuidBucket: {
bucket_selector: {
buckets_path: {
count: 'ruleExecution._count',
},
script: {
source: 'params.count > 0',
},
},
},
},
},
},
@ -315,7 +357,7 @@ export function formatExecutionLogResult(
const aggs = aggregations.excludeExecuteStart as ExcludeExecuteStartAggResult;
const total = aggs.executionUuidCardinality.value;
const total = aggs.executionUuidCardinality.executionUuidCardinality.value;
const buckets = aggs.executionUuid.buckets;
return {

View file

@ -856,8 +856,8 @@ export class RulesClient {
{
start: parsedDateStart.toISOString(),
end: parsedDateEnd.toISOString(),
filter,
aggs: getExecutionLogAggregation({
filter,
page,
perPage,
sort,

View file

@ -273,7 +273,9 @@ const aggregateResults = {
],
},
executionUuidCardinality: {
value: 374,
executionUuidCardinality: {
value: 374,
},
},
},
},
@ -453,9 +455,9 @@ describe('getExecutionLogForRule()', () => {
aggs: getExecutionLogAggregation({
page: 1,
perPage: 10,
filter: 'event.outcome: success',
sort: [{ timestamp: { order: 'desc' } }],
}),
filter: 'event.outcome: success',
end: mockedDateString,
start: '2019-02-12T20:01:22.479Z',
},

View file

@ -117,7 +117,7 @@ describe('loadActionErrorLog', () => {
"query": Object {
"date_end": "2022-03-23T16:17:53.482Z",
"date_start": "2022-03-23T16:17:53.482Z",
"filter": "kibana.alert.rule.execution.uuid: 123 and message: \\"test\\"",
"filter": "kibana.alert.rule.execution.uuid: 123 and message: \\"test\\" OR error.message: \\"test\\"",
"page": 1,
"per_page": 10,
"sort": "[{\\"@timestamp\\":{\\"order\\":\\"asc\\"}}]",

View file

@ -58,7 +58,8 @@ const getFilter = ({ runId, message }: { runId?: string; message?: string }) =>
}
if (message) {
filter.push(`message: "${message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&')}"`);
const escapedMessage = message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&');
filter.push(`message: "${escapedMessage}" OR error.message: "${escapedMessage}"`);
}
return filter;

View file

@ -84,7 +84,7 @@ describe('loadExecutionLogAggregations', () => {
"query": Object {
"date_end": "2022-03-23T16:17:53.482Z",
"date_start": "2022-03-23T16:17:53.482Z",
"filter": "event.provider: alerting AND event.outcome: success or unknown",
"filter": "event.outcome: success or unknown",
"page": 1,
"per_page": 10,
"sort": "[{\\"timestamp\\":{\\"order\\":\\"asc\\"}}]",

View file

@ -45,11 +45,12 @@ const getFilter = ({ outcomeFilter, message }: { outcomeFilter?: string[]; messa
const filter: string[] = [];
if (outcomeFilter && outcomeFilter.length) {
filter.push(`event.provider: alerting AND event.outcome: ${outcomeFilter.join(' or ')}`);
filter.push(`event.outcome: ${outcomeFilter.join(' or ')}`);
}
if (message) {
filter.push(`message: "${message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&')}"`);
const escapedMessage = message.replace(/([\)\(\<\>\}\{\"\:\\])/gm, '\\$&');
filter.push(`message: "${escapedMessage}" OR error.message: "${escapedMessage}"`);
}
return filter;