Skip to content

Commit

Permalink
[Metrics Alerts] Remove metric field from doc count on backend (elast…
Browse files Browse the repository at this point in the history
…ic#60679)

* Remove metric field from doc count on backend

* Fix tests

* Type fix

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
Zacqary and elasticmachine committed Mar 23, 2020
1 parent fbe3637 commit fce8007
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const alertInstances = new Map();

const services = {
callCluster(_: string, { body }: any) {
const metric = body.query.bool.filter[1].exists.field;
const metric = body.query.bool.filter[1]?.exists.field;
if (body.aggs.groupings) {
if (body.aggs.groupings.composite.after) {
return mocks.compositeEndResponse;
Expand Down Expand Up @@ -228,6 +228,7 @@ describe('The metric threshold alert type', () => {
comparator,
threshold,
aggType: 'count',
metric: undefined,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export const getElasticsearchMetricQuery = (
groupBy?: string,
filterQuery?: string
) => {
if (aggType === 'count' && metric) {
throw new Error('Cannot aggregate document count with a metric');
}
if (aggType !== 'count' && !metric) {
throw new Error('Can only aggregate without a metric if using the document count aggregator');
}
const interval = `${timeSize}${timeUnit}`;

const aggregations =
Expand Down Expand Up @@ -108,25 +114,32 @@ export const getElasticsearchMetricQuery = (
}
: baseAggs;

const rangeFilters = [
{
range: {
'@timestamp': {
gte: `now-${interval}`,
},
},
},
];

const metricFieldFilters = metric
? [
{
exists: {
field: metric,
},
},
]
: [];

const parsedFilterQuery = getParsedFilterQuery(filterQuery);

return {
query: {
bool: {
filter: [
{
range: {
'@timestamp': {
gte: `now-${interval}`,
},
},
},
{
exists: {
field: metric,
},
},
],
filter: [...rangeFilters, ...metricFieldFilters],
...parsedFilterQuery,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,44 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet
}
const alertUUID = uuid.v4();

const baseCriterion = {
threshold: schema.arrayOf(schema.number()),
comparator: schema.oneOf([
schema.literal('>'),
schema.literal('<'),
schema.literal('>='),
schema.literal('<='),
schema.literal('between'),
]),
timeUnit: schema.string(),
timeSize: schema.number(),
indexPattern: schema.string(),
};

const nonCountCriterion = schema.object({
...baseCriterion,
metric: schema.string(),
aggType: schema.oneOf([
schema.literal('avg'),
schema.literal('min'),
schema.literal('max'),
schema.literal('rate'),
schema.literal('cardinality'),
]),
});

const countCriterion = schema.object({
...baseCriterion,
aggType: schema.literal('count'),
metric: schema.never(),
});

alertingPlugin.registerType({
id: METRIC_THRESHOLD_ALERT_TYPE_ID,
name: 'Metric Alert - Threshold',
validate: {
params: schema.object({
criteria: schema.arrayOf(
schema.object({
threshold: schema.arrayOf(schema.number()),
comparator: schema.string(),
aggType: schema.string(),
metric: schema.string(),
timeUnit: schema.string(),
timeSize: schema.number(),
indexPattern: schema.string(),
})
),
criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion])),
groupBy: schema.maybe(schema.string()),
filterQuery: schema.maybe(schema.string()),
}),
Expand Down
16 changes: 13 additions & 3 deletions x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ export enum AlertStates {

export type TimeUnit = 's' | 'm' | 'h' | 'd';

export interface MetricExpressionParams {
aggType: MetricsExplorerAggregation;
metric: string;
interface BaseMetricExpressionParams {
timeSize: number;
timeUnit: TimeUnit;
indexPattern: string;
threshold: number[];
comparator: Comparator;
}

interface NonCountMetricExpressionParams extends BaseMetricExpressionParams {
aggType: Exclude<MetricsExplorerAggregation, 'count'>;
metric: string;
}

interface CountMetricExpressionParams extends BaseMetricExpressionParams {
aggType: 'count';
metric: never;
}

export type MetricExpressionParams = NonCountMetricExpressionParams | CountMetricExpressionParams;
35 changes: 11 additions & 24 deletions x-pack/test/api_integration/apis/infra/metrics_alerting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function({ getService }: FtrProviderContext) {
const client = getService('legacyEs');
const index = 'test-index';
const baseParams = {
metric: 'test.metric',
timeUnit: 'm',
timeSize: 5,
};
const getSearchParams = (aggType: string) =>
({
aggType,
timeUnit: 'm',
timeSize: 5,
...(aggType !== 'count' ? { metric: 'test.metric' } : {}),
} as MetricExpressionParams);
describe('Metrics Threshold Alerts', () => {
before(async () => {
await client.index({
Expand All @@ -30,10 +32,7 @@ export default function({ getService }: FtrProviderContext) {
describe('querying the entire infrastructure', () => {
for (const aggType of aggs) {
it(`should work with the ${aggType} aggregator`, async () => {
const searchBody = getElasticsearchMetricQuery({
...baseParams,
aggType,
} as MetricExpressionParams);
const searchBody = getElasticsearchMetricQuery(getSearchParams(aggType));
const result = await client.search({
index,
body: searchBody,
Expand All @@ -44,10 +43,7 @@ export default function({ getService }: FtrProviderContext) {
}
it('should work with a filterQuery', async () => {
const searchBody = getElasticsearchMetricQuery(
{
...baseParams,
aggType: 'avg',
} as MetricExpressionParams,
getSearchParams('avg'),
undefined,
'{"bool":{"should":[{"match_phrase":{"agent.hostname":"foo"}}],"minimum_should_match":1}}'
);
Expand All @@ -62,13 +58,7 @@ export default function({ getService }: FtrProviderContext) {
describe('querying with a groupBy parameter', () => {
for (const aggType of aggs) {
it(`should work with the ${aggType} aggregator`, async () => {
const searchBody = getElasticsearchMetricQuery(
{
...baseParams,
aggType,
} as MetricExpressionParams,
'agent.id'
);
const searchBody = getElasticsearchMetricQuery(getSearchParams(aggType), 'agent.id');
const result = await client.search({
index,
body: searchBody,
Expand All @@ -79,10 +69,7 @@ export default function({ getService }: FtrProviderContext) {
}
it('should work with a filterQuery', async () => {
const searchBody = getElasticsearchMetricQuery(
{
...baseParams,
aggType: 'avg',
} as MetricExpressionParams,
getSearchParams('avg'),
'agent.id',
'{"bool":{"should":[{"match_phrase":{"agent.hostname":"foo"}}],"minimum_should_match":1}}'
);
Expand Down

0 comments on commit fce8007

Please sign in to comment.