Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add group-by feature in APM rules #155001

Merged
merged 45 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bfaf0cb
add group alerts by feature in apm rules
benakansara Apr 16, 2023
d790929
fix ci errors
benakansara Apr 16, 2023
e351476
fix translations errors
benakansara Apr 16, 2023
e76bc4f
fix tests
benakansara Apr 16, 2023
0ca0259
fix tests
benakansara Apr 16, 2023
02053b7
fix tests
benakansara Apr 16, 2023
66be550
fix integration tests
benakansara Apr 16, 2023
aebab24
update snapshot
benakansara Apr 16, 2023
f295ce7
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 17, 2023
4419fe8
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 17, 2023
a6cf45d
add transaction.name in AAD mapping, add transactionName in UI
benakansara Apr 18, 2023
bc90ecf
update type
benakansara Apr 19, 2023
3bec382
remove string from groupBy
benakansara Apr 20, 2023
85ac15e
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 20, 2023
8b1666a
simplify logic of using groupBy
benakansara Apr 20, 2023
f718d8c
remove string type from groupBy
benakansara Apr 20, 2023
dcd1582
remove commented code
benakansara Apr 21, 2023
1ff3b95
Update x-pack/plugins/apm/public/components/alerting/ui_components/ap…
benakansara Apr 21, 2023
18d04e3
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 21, 2023
435ef9b
Update x-pack/plugins/apm/server/routes/alerts/rule_types/error_count…
benakansara Apr 21, 2023
42b3e1a
remove explicit type
benakansara Apr 21, 2023
a69ba12
code review changes, refactoring
benakansara Apr 22, 2023
f35d1b6
renaming transactionName
benakansara Apr 22, 2023
adf2cd1
updated comment
benakansara Apr 22, 2023
1948f16
fix tests
benakansara Apr 22, 2023
47bb2a5
code review changes
benakansara Apr 24, 2023
103e50d
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 24, 2023
e1e6c10
add two new group by fields in error count rule
benakansara Apr 24, 2023
fd36f7d
add error grouping fields to index mapping
benakansara Apr 24, 2023
6c553d4
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 24, 2023
31e3c09
change error.grouping_name type to keyword
benakansara Apr 24, 2023
5fd7ae4
update snapshot
benakansara Apr 24, 2023
c126029
Merge branch 'main' into feat/add-groupby-in-apm-rules
benakansara Apr 24, 2023
65a507f
Update x-pack/plugins/apm/common/rules/apm_rule_types.ts
benakansara Apr 24, 2023
b7badaa
update reason message for translation
benakansara Apr 24, 2023
42127e0
Update x-pack/plugins/apm/server/routes/alerts/rule_types/error_count…
benakansara Apr 25, 2023
6f8e6cc
update tests
benakansara Apr 25, 2023
48a22c5
index environment in alert doc also when not defined
benakansara Apr 25, 2023
050dbf5
update snapshot
benakansara Apr 25, 2023
546f0cb
always adding predefined group bys
benakansara Apr 25, 2023
009ead9
remove error.grouping_name groupby in error count rule
benakansara Apr 25, 2023
1bde480
rename back action variables
benakansara Apr 25, 2023
69af32b
updated tests
benakansara Apr 25, 2023
b662a15
updated tests
benakansara Apr 25, 2023
1b1cbe6
remove hardcoded error grouping key
benakansara Apr 25, 2023
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
20 changes: 14 additions & 6 deletions x-pack/plugins/apm/common/rules/apm_rule_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,33 @@ const THRESHOLD_MET_GROUP: ActionGroup<ThresholdMetActionGroupId> = {
}),
};

const formatGroup = (group: string) => (group ? ` for ${group}` : '');

export function formatErrorCountReason({
threshold,
measured,
serviceName,
windowSize,
windowUnit,
group,
}: {
threshold: number;
measured: number;
serviceName: string;
windowSize: number;
windowUnit: string;
group: string;
}) {
return i18n.translate('xpack.apm.alertTypes.errorCount.reason', {
defaultMessage: `Error count is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`,
defaultMessage: `Error count is {measured} in the last {interval}{group}. Alert when > {threshold}.`,
benakansara marked this conversation as resolved.
Show resolved Hide resolved
values: {
threshold,
measured,
serviceName,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroup(group),
},
});
}
Expand All @@ -75,6 +79,7 @@ export function formatTransactionDurationReason({
aggregationType,
windowSize,
windowUnit,
group,
}: {
threshold: number;
measured: number;
Expand All @@ -83,23 +88,24 @@ export function formatTransactionDurationReason({
aggregationType: string;
windowSize: number;
windowUnit: string;
group: string;
}) {
let aggregationTypeFormatted =
aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1);
if (aggregationTypeFormatted === 'Avg')
aggregationTypeFormatted = aggregationTypeFormatted + '.';

return i18n.translate('xpack.apm.alertTypes.transactionDuration.reason', {
defaultMessage: `{aggregationType} latency is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`,
defaultMessage: `{aggregationType} latency is {measured} in the last {interval}{group}. Alert when > {threshold}.`,
values: {
threshold: asDuration(threshold),
measured: asDuration(measured),
serviceName,
aggregationType: aggregationTypeFormatted,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroup(group),
},
});
}
Expand All @@ -111,24 +117,26 @@ export function formatTransactionErrorRateReason({
asPercent,
windowSize,
windowUnit,
group,
}: {
threshold: number;
measured: number;
serviceName: string;
asPercent: AsPercent;
windowSize: number;
windowUnit: string;
group: string;
}) {
return i18n.translate('xpack.apm.alertTypes.transactionErrorRate.reason', {
defaultMessage: `Failed transactions is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`,
defaultMessage: `Failed transactions is {measured} in the last {interval}{group}. Alert when > {threshold}.`,
values: {
threshold: asPercent(threshold, 100),
measured: asPercent(measured, 100),
serviceName,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroup(group),
},
});
}
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/apm/common/rules/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const errorCountParamsSchema = schema.object({
threshold: schema.number(),
serviceName: schema.maybe(schema.string()),
environment: schema.string(),
groupBy: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
),
benakansara marked this conversation as resolved.
Show resolved Hide resolved
});

export const transactionDurationParamsSchema = schema.object({
Expand All @@ -29,6 +32,9 @@ export const transactionDurationParamsSchema = schema.object({
schema.literal(AggregationType.P99),
]),
environment: schema.string(),
groupBy: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
),
});

export const anomalyParamsSchema = schema.object({
Expand All @@ -52,6 +58,9 @@ export const transactionErrorRateParamsSchema = schema.object({
transactionType: schema.maybe(schema.string()),
serviceName: schema.maybe(schema.string()),
environment: schema.string(),
groupBy: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
),
});

type ErrorCountParamsType = TypeOf<typeof errorCountParamsSchema>;
Expand Down
45 changes: 45 additions & 0 deletions x-pack/plugins/apm/common/utils/get_groupby_terms.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { getGroupByTerms } from './get_groupby_terms';

describe('get terms fields based on group-by', () => {
it('returns single terms field', () => {
const ruleParams = { groupBy: 'service.name' };
const terms = getGroupByTerms(ruleParams.groupBy);
expect(terms).toEqual([
{ field: 'service.name', missing: 'SERVICE_NAME_NOT_DEFINED' },
]);
});

it('returns multiple terms fields', () => {
const ruleParams = {
groupBy: [
'service.name',
'service.environment',
'transaction.type',
'transaction.name',
],
};
const terms = getGroupByTerms(ruleParams.groupBy);
expect(terms).toEqual([
{ field: 'service.name', missing: 'SERVICE_NAME_NOT_DEFINED' },
{
field: 'service.environment',
missing: 'ENVIRONMENT_NOT_DEFINED',
},
{ field: 'transaction.type', missing: 'TRANSACTION_TYPE_NOT_DEFINED' },
{ field: 'transaction.name', missing: 'TRANSACTION_NAME_NOT_DEFINED' },
]);
});

it('returns an empty array', () => {
const ruleParams = { groupBy: undefined };
const terms = getGroupByTerms(ruleParams.groupBy);
expect(terms).toEqual([]);
});
});
21 changes: 21 additions & 0 deletions x-pack/plugins/apm/common/utils/get_groupby_terms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { ENVIRONMENT_NOT_DEFINED } from '../environment_filter_values';
import { SERVICE_ENVIRONMENT } from '../es_fields/apm';

export const getGroupByTerms = (groupBy: string[] | string | undefined) => {
return (groupBy ? [groupBy] : []).flat().map((group) => {
kdelemme marked this conversation as resolved.
Show resolved Hide resolved
return {
field: group,
missing:
group === SERVICE_ENVIRONMENT
? ENVIRONMENT_NOT_DEFINED.value
: group.replaceAll('.', '_').toUpperCase().concat('_NOT_DEFINED'),
};
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

import { i18n } from '@kbn/i18n';
import { defaults, omit } from 'lodash';
import React, { useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import { CoreStart } from '@kbn/core/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import {
ForLastExpression,
TIME_UNITS,
} from '@kbn/triggers-actions-ui-plugin/public';
import { EuiFormRow } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values';
import { asInteger } from '../../../../../common/utils/formatters';
import { useFetcher } from '../../../../hooks/use_fetcher';
Expand All @@ -26,13 +28,19 @@ import {
} from '../../utils/fields';
import { AlertMetadata, getIntervalAndTimeRange } from '../../utils/helper';
import { ApmRuleParamsContainer } from '../../ui_components/apm_rule_params_container';
import { APMRuleGroupBy } from '../../ui_components/apm_rule_group_by';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '../../../../../common/es_fields/apm';

export interface RuleParams {
windowSize?: number;
windowUnit?: TIME_UNITS;
threshold?: number;
serviceName?: string;
environment?: string;
groupBy?: string | string[] | undefined;
}

interface Props {
Expand Down Expand Up @@ -91,6 +99,13 @@ export function ErrorCountRuleType(props: Props) {
]
);

const onGroupByChange = useCallback(
(group: string[] | string | null) => {
kdelemme marked this conversation as resolved.
Show resolved Hide resolved
setRuleParams('groupBy', group && group.length ? group : '');
},
[setRuleParams]
);

const fields = [
<ServiceField
currentValue={params.serviceName}
Expand Down Expand Up @@ -138,11 +153,41 @@ export function ErrorCountRuleType(props: Props) {
/>
);

const groupAlertsBy = (
<>
<EuiFormRow
label={i18n.translate(
'xpack.apm.ruleFlyout.errorCount.createAlertPerText',
{
defaultMessage: 'Group alerts by',
}
)}
helpText={i18n.translate(
'xpack.apm.ruleFlyout.errorCount.createAlertPerHelpText',
{
defaultMessage:
'Create an alert for every unique value. For example: "transaction.name". By default, alert is created for every unique service.name and service.environment.',
}
)}
fullWidth
display="rowCompressed"
>
<APMRuleGroupBy
onChange={onGroupByChange}
options={{ groupBy: ruleParams.groupBy }}
preSelectedOptions={[SERVICE_NAME, SERVICE_ENVIRONMENT]}
/>
</EuiFormRow>
<EuiSpacer size="m" />
</>
);

return (
<ApmRuleParamsContainer
minimumWindowSize={{ value: 5, unit: TIME_UNITS.MINUTE }}
defaultParams={params}
fields={fields}
groupAlertsBy={groupAlertsBy}
setRuleParams={setRuleParams}
setRuleProperty={setRuleProperty}
chartPreview={chartPreview}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import { EuiSelect } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { defaults, map, omit } from 'lodash';
import React, { useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import { CoreStart } from '@kbn/core/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import {
ForLastExpression,
TIME_UNITS,
} from '@kbn/triggers-actions-ui-plugin/public';
import { EuiFormRow } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import { AggregationType } from '../../../../../common/rules/apm_rule_types';
import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values';
import { getDurationFormatter } from '../../../../../common/utils/formatters';
Expand All @@ -34,6 +36,12 @@ import {
import { AlertMetadata, getIntervalAndTimeRange } from '../../utils/helper';
import { ApmRuleParamsContainer } from '../../ui_components/apm_rule_params_container';
import { PopoverExpression } from '../../ui_components/popover_expression';
import { APMRuleGroupBy } from '../../ui_components/apm_rule_group_by';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
TRANSACTION_TYPE,
} from '../../../../../common/es_fields/apm';

export interface RuleParams {
aggregationType: AggregationType;
Expand All @@ -43,6 +51,7 @@ export interface RuleParams {
transactionType: string;
windowSize: number;
windowUnit: string;
groupBy?: string | string[] | undefined;
}

const TRANSACTION_ALERT_AGGREGATION_TYPES: Record<AggregationType, string> = {
Expand Down Expand Up @@ -142,6 +151,13 @@ export function TransactionDurationRuleType(props: Props) {
/>
);

const onGroupByChange = useCallback(
(group: string[] | string | null) => {
kdelemme marked this conversation as resolved.
Show resolved Hide resolved
setRuleParams('groupBy', group && group.length ? group : '');
},
[setRuleParams]
);

const fields = [
<ServiceField
allowAll={false}
Expand Down Expand Up @@ -206,12 +222,46 @@ export function TransactionDurationRuleType(props: Props) {
/>,
];

const groupAlertsBy = (
<>
<EuiFormRow
label={i18n.translate(
'xpack.apm.ruleFlyout.transactionDuration.createAlertPerText',
{
defaultMessage: 'Group alerts by',
}
)}
helpText={i18n.translate(
'xpack.apm.ruleFlyout.transactionDuration.createAlertPerHelpText',
{
defaultMessage:
'Create an alert for every unique value. For example: "transaction.name". By default, alert is created for every unique service.name, service.environment and transaction.type.',
}
)}
fullWidth
display="rowCompressed"
>
<APMRuleGroupBy
onChange={onGroupByChange}
options={{ groupBy: ruleParams.groupBy }}
preSelectedOptions={[
SERVICE_NAME,
SERVICE_ENVIRONMENT,
TRANSACTION_TYPE,
]}
/>
</EuiFormRow>
<EuiSpacer size="m" />
</>
);

return (
<ApmRuleParamsContainer
minimumWindowSize={{ value: 5, unit: TIME_UNITS.MINUTE }}
chartPreview={chartPreview}
defaultParams={params}
fields={fields}
groupAlertsBy={groupAlertsBy}
setRuleParams={setRuleParams}
setRuleProperty={setRuleProperty}
/>
Expand Down
Loading