Skip to content

Commit

Permalink
[APM] Add transaction error rate alert
Browse files Browse the repository at this point in the history
  • Loading branch information
sorenlouv committed Sep 17, 2020
1 parent b733375 commit a9ce803
Show file tree
Hide file tree
Showing 23 changed files with 642 additions and 280 deletions.
86 changes: 31 additions & 55 deletions x-pack/plugins/apm/common/alert_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,82 +7,58 @@
import { i18n } from '@kbn/i18n';

export enum AlertType {
ErrorRate = 'apm.error_rate',
ErrorCount = 'apm.error_rate', // ErrorRate was renamed to ErrorCount but the key is kept as `error_rate` for backwards-compat.
TransactionErrorRate = 'apm.transaction_error_rate',
TransactionDuration = 'apm.transaction_duration',
TransactionDurationAnomaly = 'apm.transaction_duration_anomaly',
}

const THRESHOLD_MET_GROUP = {
id: 'threshold_met',
name: i18n.translate('xpack.apm.a.thresholdMet', {
defaultMessage: 'Threshold met',
}),
};

export const ALERT_TYPES_CONFIG = {
[AlertType.ErrorRate]: {
name: i18n.translate('xpack.apm.errorRateAlert.name', {
defaultMessage: 'Error rate',
[AlertType.ErrorCount]: {
name: i18n.translate('xpack.apm.errorCountAlert.name', {
defaultMessage: 'Error count threshold',
}),
actionGroups: [
{
id: 'threshold_met',
name: i18n.translate('xpack.apm.errorRateAlert.thresholdMet', {
defaultMessage: 'Threshold met',
}),
},
],
actionGroups: [THRESHOLD_MET_GROUP],
defaultActionGroupId: 'threshold_met',
producer: 'apm',
},
[AlertType.TransactionDuration]: {
name: i18n.translate('xpack.apm.transactionDurationAlert.name', {
defaultMessage: 'Transaction duration',
defaultMessage: 'Transaction duration threshold',
}),
actionGroups: [
{
id: 'threshold_met',
name: i18n.translate(
'xpack.apm.transactionDurationAlert.thresholdMet',
{
defaultMessage: 'Threshold met',
}
),
},
],
actionGroups: [THRESHOLD_MET_GROUP],
defaultActionGroupId: 'threshold_met',
producer: 'apm',
},
[AlertType.TransactionDurationAnomaly]: {
name: i18n.translate('xpack.apm.transactionDurationAnomalyAlert.name', {
defaultMessage: 'Transaction duration anomaly',
}),
actionGroups: [
{
id: 'threshold_met',
name: i18n.translate(
'xpack.apm.transactionDurationAlert.thresholdMet',
{
defaultMessage: 'Threshold met',
}
),
},
],
actionGroups: [THRESHOLD_MET_GROUP],
defaultActionGroupId: 'threshold_met',
producer: 'apm',
},
[AlertType.TransactionErrorRate]: {
name: i18n.translate('xpack.apm.transactionErrorRateAlert.name', {
defaultMessage: 'Transaction error rate threshold',
}),
actionGroups: [THRESHOLD_MET_GROUP],
defaultActionGroupId: 'threshold_met',
producer: 'apm',
},
};

export const TRANSACTION_ALERT_AGGREGATION_TYPES = {
avg: i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.avg',
{
defaultMessage: 'Average',
}
),
'95th': i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.95th',
{
defaultMessage: '95th percentile',
}
),
'99th': i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.99th',
{
defaultMessage: '99th percentile',
}
),
};
// Server side registrations
// x-pack/plugins/apm/server/lib/alerts/<alert>.ts
// x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts

// Client side registrations:
// x-pack/plugins/apm/public/components/alerting/<alert>/index.tsx
// x-pack/plugins/apm/public/components/alerting/register_apm_alerts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

import { storiesOf } from '@storybook/react';
import React from 'react';
import { ErrorRateAlertTrigger } from '.';
import { ErrorCountAlertTrigger } from '.';
import { ApmPluginContextValue } from '../../../context/ApmPluginContext';
import {
mockApmPluginContextValue,
MockApmPluginContextWrapper,
} from '../../../context/ApmPluginContext/MockApmPluginContext';

storiesOf('app/ErrorRateAlertTrigger', module).add(
storiesOf('app/ErrorCountAlertTrigger', module).add(
'example',
() => {
const params = {
Expand All @@ -26,7 +26,7 @@ storiesOf('app/ErrorRateAlertTrigger', module).add(
value={(mockApmPluginContextValue as unknown) as ApmPluginContextValue}
>
<div style={{ width: 400 }}>
<ErrorRateAlertTrigger
<ErrorCountAlertTrigger
alertParams={params as any}
setAlertParams={() => undefined}
setAlertProperty={() => undefined}
Expand All @@ -37,7 +37,7 @@ storiesOf('app/ErrorRateAlertTrigger', module).add(
},
{
info: {
propTablesExclude: [ErrorRateAlertTrigger, MockApmPluginContextWrapper],
propTablesExclude: [ErrorCountAlertTrigger, MockApmPluginContextWrapper],
source: false,
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFieldNumber, EuiSelect } from '@elastic/eui';
import { EuiSelect, EuiExpression, EuiFieldNumber } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { isFinite } from 'lodash';
import React from 'react';
import { useParams } from 'react-router-dom';
import { ForLastExpression } from '../../../../../triggers_actions_ui/public';
import { ALERT_TYPES_CONFIG } from '../../../../common/alert_types';
import { ALERT_TYPES_CONFIG, AlertType } from '../../../../common/alert_types';
import {
ENVIRONMENT_ALL,
getEnvironmentLabel,
Expand All @@ -19,20 +19,21 @@ import { useUrlParams } from '../../../hooks/useUrlParams';
import { ServiceAlertTrigger } from '../ServiceAlertTrigger';
import { PopoverExpression } from '../ServiceAlertTrigger/PopoverExpression';

export interface ErrorRateAlertTriggerParams {
export interface AlertParams {
windowSize: number;
windowUnit: string;
threshold: number;
serviceName: string;
environment: string;
}

interface Props {
alertParams: ErrorRateAlertTriggerParams;
alertParams: AlertParams;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}

export function ErrorRateAlertTrigger(props: Props) {
export function ErrorCountAlertTrigger(props: Props) {
const { setAlertParams, setAlertProperty, alertParams } = props;
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams } = useUrlParams();
Expand All @@ -54,6 +55,15 @@ export function ErrorRateAlertTrigger(props: Props) {
const threshold = isFinite(params.threshold) ? params.threshold : '';

const fields = [
<EuiExpression
description={i18n.translate(
'xpack.apm.transactionDurationAnomalyAlertTrigger.service',
{
defaultMessage: 'Service',
}
)}
value={serviceName}
/>,
<PopoverExpression
value={getEnvironmentLabel(params.environment)}
title={i18n.translate('xpack.apm.errorRateAlertTrigger.environment', {
Expand All @@ -66,7 +76,7 @@ export function ErrorRateAlertTrigger(props: Props) {
onChange={(e) =>
setAlertParams(
'environment',
e.target.value as ErrorRateAlertTriggerParams['environment']
e.target.value as AlertParams['environment']
)
}
compressed
Expand Down Expand Up @@ -108,7 +118,7 @@ export function ErrorRateAlertTrigger(props: Props) {

return (
<ServiceAlertTrigger
alertTypeName={ALERT_TYPES_CONFIG['apm.error_rate'].name}
alertTypeName={ALERT_TYPES_CONFIG[AlertType.ErrorCount].name}
defaults={defaults}
fields={fields}
setAlertParams={setAlertParams}
Expand All @@ -120,4 +130,4 @@ export function ErrorRateAlertTrigger(props: Props) {
// Default export is required for React.lazy loading
//
// eslint-disable-next-line import/no-default-export
export default ErrorRateAlertTrigger;
export default ErrorCountAlertTrigger;
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFieldNumber, EuiSelect } from '@elastic/eui';
import { EuiFieldNumber, EuiSelect, EuiExpression } from '@elastic/eui';
import { useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { map } from 'lodash';
import React from 'react';
import { ForLastExpression } from '../../../../../triggers_actions_ui/public';
import {
ALERT_TYPES_CONFIG,
TRANSACTION_ALERT_AGGREGATION_TYPES,
} from '../../../../common/alert_types';
import { ALERT_TYPES_CONFIG } from '../../../../common/alert_types';
import { useEnvironments } from '../../../hooks/useEnvironments';
import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
import { useUrlParams } from '../../../hooks/useUrlParams';
Expand All @@ -22,7 +20,7 @@ import {
getEnvironmentLabel,
} from '../../../../common/environment_filter_values';

interface Params {
interface AlertParams {
windowSize: number;
windowUnit: string;
threshold: number;
Expand All @@ -32,23 +30,42 @@ interface Params {
environment: string;
}

const TRANSACTION_ALERT_AGGREGATION_TYPES = {
avg: i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.avg',
{
defaultMessage: 'Average',
}
),
'95th': i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.95th',
{
defaultMessage: '95th percentile',
}
),
'99th': i18n.translate(
'xpack.apm.transactionDurationAlert.aggregationType.99th',
{
defaultMessage: '99th percentile',
}
),
};

interface Props {
alertParams: Params;
alertParams: AlertParams;
setAlertParams: (key: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
}

export function TransactionDurationAlertTrigger(props: Props) {
const { setAlertParams, alertParams, setAlertProperty } = props;
const { serviceName } = alertParams;
const { urlParams } = useUrlParams();

const transactionTypes = useServiceTransactionTypes(urlParams);

const { start, end } = urlParams;
const { serviceName } = useParams<{ serviceName?: string }>();
const { start, end, transactionType } = urlParams;
const { environmentOptions } = useEnvironments({ serviceName, start, end });

if (!transactionTypes.length) {
if (!transactionTypes.length || !serviceName) {
return null;
}

Expand All @@ -57,7 +74,9 @@ export function TransactionDurationAlertTrigger(props: Props) {
aggregationType: 'avg',
windowSize: 5,
windowUnit: 'm',
transactionType: transactionTypes[0],

// use the current transaction type or default to the first in the list
transactionType: transactionType || transactionTypes[0],
environment: urlParams.environment || ENVIRONMENT_ALL.value,
};

Expand All @@ -67,6 +86,15 @@ export function TransactionDurationAlertTrigger(props: Props) {
};

const fields = [
<EuiExpression
description={i18n.translate(
'xpack.apm.transactionDurationAnomalyAlertTrigger.service',
{
defaultMessage: 'Service',
}
)}
value={serviceName}
/>,
<PopoverExpression
value={getEnvironmentLabel(params.environment)}
title={i18n.translate(
Expand All @@ -80,7 +108,10 @@ export function TransactionDurationAlertTrigger(props: Props) {
value={params.environment}
options={environmentOptions}
onChange={(e) =>
setAlertParams('environment', e.target.value as Params['environment'])
setAlertParams(
'environment',
e.target.value as AlertParams['environment']
)
}
compressed
/>
Expand All @@ -102,7 +133,7 @@ export function TransactionDurationAlertTrigger(props: Props) {
onChange={(e) =>
setAlertParams(
'transactionType',
e.target.value as Params['transactionType']
e.target.value as AlertParams['transactionType']
)
}
compressed
Expand All @@ -125,7 +156,7 @@ export function TransactionDurationAlertTrigger(props: Props) {
onChange={(e) =>
setAlertParams(
'aggregationType',
e.target.value as Params['aggregationType']
e.target.value as AlertParams['aggregationType']
)
}
compressed
Expand Down
Loading

0 comments on commit a9ce803

Please sign in to comment.