diff --git a/x-pack/plugins/apm/common/rules/schema.ts b/x-pack/plugins/apm/common/rules/schema.ts index 7e48ad989b606..698b4507c5b3f 100644 --- a/x-pack/plugins/apm/common/rules/schema.ts +++ b/x-pack/plugins/apm/common/rules/schema.ts @@ -15,6 +15,7 @@ export const errorCountParamsSchema = schema.object({ threshold: schema.number(), serviceName: schema.maybe(schema.string()), environment: schema.string(), + errorGroupingKey: schema.maybe(schema.string()), }); export const transactionDurationParamsSchema = schema.object({ diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx index 09ca441cd26e9..f23dc6f4fb362 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx @@ -21,6 +21,7 @@ import { createCallApmApi } from '../../../../services/rest/create_call_apm_api' import { ChartPreview } from '../../ui_components/chart_preview'; import { EnvironmentField, + ErrorGroupingKeyField, IsAboveField, ServiceField, } from '../../utils/fields'; @@ -33,6 +34,7 @@ export interface RuleParams { threshold?: number; serviceName?: string; environment?: string; + errorGroupingKey?: string; } interface Props { @@ -74,6 +76,7 @@ export function ErrorCountRuleType(props: Props) { query: { environment: params.environment, serviceName: params.serviceName, + errorGroupingKey: params.errorGroupingKey, interval, start, end, @@ -88,6 +91,7 @@ export function ErrorCountRuleType(props: Props) { params.windowUnit, params.environment, params.serviceName, + params.errorGroupingKey, ] ); @@ -98,6 +102,7 @@ export function ErrorCountRuleType(props: Props) { if (value !== params.serviceName) { setRuleParams('serviceName', value); setRuleParams('environment', ENVIRONMENT_ALL.value); + setRuleParams('errorGroupingKey', undefined); } }} />, @@ -106,6 +111,12 @@ export function ErrorCountRuleType(props: Props) { onChange={(value) => setRuleParams('environment', value)} serviceName={params.serviceName} />, + setRuleParams('errorGroupingKey', value)} + serviceName={params.serviceName} + />, + void; + serviceName?: string; +}) { + const label = i18n.translate('xpack.apm.alerting.fields.error.group.id', { + defaultMessage: 'Error grouping key', + }); + return ( + + + + ); +} + export function IsAboveField({ value, unit, diff --git a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts index 471d2ff3141b0..1bb9c41546cf8 100644 --- a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts @@ -94,4 +94,13 @@ export const apmActionVariables = { name: 'viewInAppUrl' as const, usesPublicBaseUrl: true, }, + errorGroupingKey: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.errorGroupingKey', + { + defaultMessage: 'The error grouping key the alert is created for', + } + ), + name: 'errorGroupingKey' as const, + }, }; diff --git a/x-pack/plugins/apm/server/routes/alerts/route.ts b/x-pack/plugins/apm/server/routes/alerts/route.ts index 314e05fed539a..13b790155520f 100644 --- a/x-pack/plugins/apm/server/routes/alerts/route.ts +++ b/x-pack/plugins/apm/server/routes/alerts/route.ts @@ -28,6 +28,7 @@ const alertParamsRt = t.intersection([ t.literal(AggregationType.P99), ]), serviceName: t.string, + errorGroupingKey: t.string, transactionType: t.string, transactionName: t.string, }), diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts index 6175b2578a236..59ae52eab6a5a 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts @@ -7,7 +7,10 @@ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; +import { + ERROR_GROUP_ID, + SERVICE_NAME, +} from '../../../../../common/es_fields/apm'; import { AlertParams } from '../../route'; import { environmentQuery } from '../../../../../common/utils/environment_query'; import { APMEventClient } from '../../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -24,12 +27,14 @@ export async function getTransactionErrorCountChartPreview({ apmEventClient: APMEventClient; alertParams: AlertParams; }): Promise { - const { serviceName, environment, interval, start, end } = alertParams; + const { serviceName, environment, errorGroupingKey, interval, start, end } = + alertParams; const query = { bool: { filter: [ ...termQuery(SERVICE_NAME, serviceName), + ...termQuery(ERROR_GROUP_ID, errorGroupingKey), ...rangeQuery(start, end), ...environmentQuery(environment), ], diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts index a008dd0d23cb3..2527a11e3f001 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts @@ -25,6 +25,7 @@ import { getEnvironmentLabel, } from '../../../../../common/environment_filter_values'; import { + ERROR_GROUP_ID, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_NAME, @@ -77,6 +78,7 @@ export function registerErrorCountRuleType({ apmActionVariables.interval, apmActionVariables.reason, apmActionVariables.serviceName, + apmActionVariables.errorGroupingKey, apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.viewInAppUrl, @@ -112,6 +114,7 @@ export function registerErrorCountRuleType({ }, { term: { [PROCESSOR_EVENT]: ProcessorEvent.error } }, ...termQuery(SERVICE_NAME, ruleParams.serviceName), + ...termQuery(ERROR_GROUP_ID, ruleParams.errorGroupingKey), ...environmentQuery(ruleParams.environment), ], }, @@ -164,7 +167,12 @@ export function registerErrorCountRuleType({ windowUnit: ruleParams.windowUnit, }); - const id = [ApmRuleType.ErrorCount, serviceName, environment] + const id = [ + ApmRuleType.ErrorCount, + serviceName, + environment, + ruleParams.errorGroupingKey, + ] .filter((name) => name) .join('_'); @@ -188,6 +196,7 @@ export function registerErrorCountRuleType({ [PROCESSOR_EVENT]: ProcessorEvent.error, [ALERT_EVALUATION_VALUE]: errorCount, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, + [ERROR_GROUP_ID]: ruleParams.errorGroupingKey, [ALERT_REASON]: alertReason, ...sourceFields, }, @@ -201,6 +210,7 @@ export function registerErrorCountRuleType({ reason: alertReason, serviceName, threshold: ruleParams.threshold, + errorGroupingKey: ruleParams.errorGroupingKey, triggerValue: errorCount, viewInAppUrl, }); diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts index f070098a66661..7ec09849b7ff2 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts @@ -100,6 +100,33 @@ export default function ApiTest({ getService }: FtrProviderContext) { ).to.equal(true); }); + it('error_count with error grouping key', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + errorGroupingKey: 'd16d39e7fa133b8943cea035430a7b4e', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview).to.eql([ + { x: 1627974600000, y: 4 }, + { x: 1627974900000, y: 2 }, + { x: 1627975200000, y: 0 }, + ]); + }); + it('transaction_duration (with data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({