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 all 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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 57 additions & 12 deletions x-pack/plugins/apm/common/rules/apm_rule_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ import type {
import type { ActionGroup } from '@kbn/alerting-plugin/common';
import { formatDurationFromTimeUnitChar } from '@kbn/observability-plugin/common';
import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from '../ml_constants';
import {
ERROR_GROUP_ID,
SERVICE_ENVIRONMENT,
SERVICE_NAME,
TRANSACTION_NAME,
TRANSACTION_TYPE,
} from '../es_fields/apm';
import { getEnvironmentLabel } from '../environment_filter_values';

export const APM_SERVER_FEATURE_ID = 'apm';

Expand All @@ -40,95 +48,132 @@ const THRESHOLD_MET_GROUP: ActionGroup<ThresholdMetActionGroupId> = {
}),
};

const getFieldNameLabel = (field: string): string => {
switch (field) {
case SERVICE_NAME:
return 'service';
case SERVICE_ENVIRONMENT:
return 'env';
case TRANSACTION_TYPE:
return 'type';
case TRANSACTION_NAME:
return 'name';
case ERROR_GROUP_ID:
return 'error key';
default:
return field;
}
};

export const getFieldValueLabel = (
field: string,
fieldValue: string
): string => {
return field === SERVICE_ENVIRONMENT
? getEnvironmentLabel(fieldValue)
: fieldValue;
};

const formatGroupByFields = (groupByFields: Record<string, string>): string => {
const groupByFieldLabels = Object.keys(groupByFields).map(
(field) =>
`${getFieldNameLabel(field)}: ${getFieldValueLabel(
field,
groupByFields[field]
)}`
);
return groupByFieldLabels.join(', ');
};

export function formatErrorCountReason({
threshold,
measured,
serviceName,
windowSize,
windowUnit,
groupByFields,
}: {
threshold: number;
measured: number;
serviceName: string;
windowSize: number;
windowUnit: string;
groupByFields: Record<string, 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} for {group}. Alert when > {threshold}.`,
values: {
threshold,
measured,
serviceName,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroupByFields(groupByFields),
},
});
}

export function formatTransactionDurationReason({
threshold,
measured,
serviceName,
asDuration,
aggregationType,
windowSize,
windowUnit,
groupByFields,
}: {
threshold: number;
measured: number;
serviceName: string;
asDuration: AsDuration;
aggregationType: string;
windowSize: number;
windowUnit: string;
groupByFields: Record<string, 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} for {group}. Alert when > {threshold}.`,
values: {
threshold: asDuration(threshold),
measured: asDuration(measured),
serviceName,
aggregationType: aggregationTypeFormatted,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroupByFields(groupByFields),
},
});
}

export function formatTransactionErrorRateReason({
threshold,
measured,
serviceName,
asPercent,
windowSize,
windowUnit,
groupByFields,
}: {
threshold: number;
measured: number;
serviceName: string;
asPercent: AsPercent;
windowSize: number;
windowUnit: string;
groupByFields: Record<string, 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} for {group}. Alert when > {threshold}.`,
values: {
threshold: asPercent(threshold, 100),
measured: asPercent(measured, 100),
serviceName,
interval: formatDurationFromTimeUnitChar(
windowSize,
windowUnit as TimeUnitChar
),
group: formatGroupByFields(groupByFields),
},
});
}
Expand Down
3 changes: 3 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,7 @@ export const errorCountParamsSchema = schema.object({
threshold: schema.number(),
serviceName: schema.maybe(schema.string()),
environment: schema.string(),
groupBy: schema.maybe(schema.arrayOf(schema.string())),
kpatticha marked this conversation as resolved.
Show resolved Hide resolved
errorGroupingKey: schema.maybe(schema.string()),
});

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

export const anomalyParamsSchema = schema.object({
Expand All @@ -55,6 +57,7 @@ export const transactionErrorRateParamsSchema = schema.object({
transactionName: schema.maybe(schema.string()),
serviceName: schema.maybe(schema.string()),
environment: schema.string(),
groupBy: schema.maybe(schema.arrayOf(schema.string())),
});

type ErrorCountParamsType = TypeOf<typeof errorCountParamsSchema>;
Expand Down
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 @@ -27,13 +29,21 @@ 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,
TRANSACTION_NAME,
ERROR_GROUP_ID,
} from '../../../../../common/es_fields/apm';

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

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

const onGroupByChange = useCallback(
(group: string[] | null) => {
setRuleParams('groupBy', group ?? []);
},
[setRuleParams]
);

const fields = [
<ServiceField
currentValue={params.serviceName}
Expand Down Expand Up @@ -149,11 +166,42 @@ 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 }}
fields={[TRANSACTION_NAME, ERROR_GROUP_ID]}
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
Loading
Loading