diff --git a/x-pack/plugins/apm/common/utils/formatters.test.ts b/x-pack/plugins/apm/common/utils/formatters.test.ts deleted file mode 100644 index ce317c5a6a827..0000000000000 --- a/x-pack/plugins/apm/common/utils/formatters.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { asPercent } from './formatters'; - -describe('formatters', () => { - describe('asPercent', () => { - it('should format as integer when number is above 10', () => { - expect(asPercent(3725, 10000, 'n/a')).toEqual('37%'); - }); - - it('should add a decimal when value is below 10', () => { - expect(asPercent(0.092, 1)).toEqual('9.2%'); - }); - - it('should format when numerator is 0', () => { - expect(asPercent(0, 1, 'n/a')).toEqual('0%'); - }); - - it('should return fallback when denominator is undefined', () => { - expect(asPercent(3725, undefined, 'n/a')).toEqual('n/a'); - }); - - it('should return fallback when denominator is 0 ', () => { - expect(asPercent(3725, 0, 'n/a')).toEqual('n/a'); - }); - - it('should return fallback when numerator or denominator is NaN', () => { - expect(asPercent(3725, NaN, 'n/a')).toEqual('n/a'); - expect(asPercent(NaN, 10000, 'n/a')).toEqual('n/a'); - }); - }); -}); diff --git a/x-pack/plugins/apm/common/utils/formatters.ts b/x-pack/plugins/apm/common/utils/formatters.ts deleted file mode 100644 index f7c609d949adf..0000000000000 --- a/x-pack/plugins/apm/common/utils/formatters.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import numeral from '@elastic/numeral'; - -export function asPercent( - numerator: number, - denominator: number | undefined, - fallbackResult = '' -) { - if (!denominator || isNaN(numerator)) { - return fallbackResult; - } - - const decimal = numerator / denominator; - - // 33.2 => 33% - // 3.32 => 3.3% - // 0 => 0% - if (Math.abs(decimal) >= 0.1 || decimal === 0) { - return numeral(decimal).format('0%'); - } - - return numeral(decimal).format('0.0%'); -} diff --git a/x-pack/plugins/apm/public/utils/formatters/__test__/datetime.test.ts b/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts similarity index 99% rename from x-pack/plugins/apm/public/utils/formatters/__test__/datetime.test.ts rename to x-pack/plugins/apm/common/utils/formatters/datetime.test.ts index 647a27c59aa4c..733fb7bb5eea1 100644 --- a/x-pack/plugins/apm/public/utils/formatters/__test__/datetime.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/datetime.test.ts @@ -8,7 +8,7 @@ import { asRelativeDateTimeRange, asAbsoluteDateTime, getDateDifference, -} from '../datetime'; +} from './datetime'; describe('date time formatters', () => { beforeAll(() => { diff --git a/x-pack/plugins/apm/public/utils/formatters/datetime.ts b/x-pack/plugins/apm/common/utils/formatters/datetime.ts similarity index 100% rename from x-pack/plugins/apm/public/utils/formatters/datetime.ts rename to x-pack/plugins/apm/common/utils/formatters/datetime.ts diff --git a/x-pack/plugins/apm/public/utils/formatters/__test__/duration.test.ts b/x-pack/plugins/apm/common/utils/formatters/duration.test.ts similarity index 99% rename from x-pack/plugins/apm/public/utils/formatters/__test__/duration.test.ts rename to x-pack/plugins/apm/common/utils/formatters/duration.test.ts index ca8a4919dd216..a39d9b47f41c2 100644 --- a/x-pack/plugins/apm/public/utils/formatters/__test__/duration.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/duration.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { asDuration, toMicroseconds, asMillisecondDuration } from '../duration'; +import { asDuration, toMicroseconds, asMillisecondDuration } from './duration'; describe('duration formatters', () => { describe('asDuration', () => { diff --git a/x-pack/plugins/apm/public/utils/formatters/duration.ts b/x-pack/plugins/apm/common/utils/formatters/duration.ts similarity index 94% rename from x-pack/plugins/apm/public/utils/formatters/duration.ts rename to x-pack/plugins/apm/common/utils/formatters/duration.ts index 8381b0afb5f07..c0a99e0152fa7 100644 --- a/x-pack/plugins/apm/public/utils/formatters/duration.ts +++ b/x-pack/plugins/apm/common/utils/formatters/duration.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { memoize } from 'lodash'; import { NOT_AVAILABLE_LABEL } from '../../../common/i18n'; -import { asDecimal, asInteger } from './formatters'; +import { asDecimalOrInteger, asInteger } from './formatters'; import { TimeUnit } from './datetime'; import { Maybe } from '../../../typings/common'; @@ -31,14 +31,6 @@ export type TimeFormatter = ( type TimeFormatterBuilder = (max: number) => TimeFormatter; -function asDecimalOrInteger(value: number) { - // exact 0 or above 10 should not have decimal - if (value === 0 || value >= 10) { - return asInteger(value); - } - return asDecimal(value); -} - function getUnitLabelAndConvertedValue( unitKey: DurationTimeUnit, value: number diff --git a/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts b/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts new file mode 100644 index 0000000000000..4d6c348fcbee3 --- /dev/null +++ b/x-pack/plugins/apm/common/utils/formatters/formatters.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { asPercent, asDecimalOrInteger } from './formatters'; + +describe('formatters', () => { + describe('asPercent', () => { + it('formats as integer when number is above 10', () => { + expect(asPercent(3725, 10000, 'n/a')).toEqual('37%'); + }); + + it('adds a decimal when value is below 10', () => { + expect(asPercent(0.092, 1)).toEqual('9.2%'); + }); + + it('formats when numerator is 0', () => { + expect(asPercent(0, 1, 'n/a')).toEqual('0%'); + }); + + it('returns fallback when denominator is undefined', () => { + expect(asPercent(3725, undefined, 'n/a')).toEqual('n/a'); + }); + + it('returns fallback when denominator is 0 ', () => { + expect(asPercent(3725, 0, 'n/a')).toEqual('n/a'); + }); + + it('returns fallback when numerator or denominator is NaN', () => { + expect(asPercent(3725, NaN, 'n/a')).toEqual('n/a'); + expect(asPercent(NaN, 10000, 'n/a')).toEqual('n/a'); + }); + }); + + describe('asDecimalOrInteger', () => { + it('formats as integer when number equals to 0 ', () => { + expect(asDecimalOrInteger(0)).toEqual('0'); + }); + it('formats as integer when number is above or equals 10 ', () => { + expect(asDecimalOrInteger(10.123)).toEqual('10'); + expect(asDecimalOrInteger(15.123)).toEqual('15'); + }); + it('formats as decimal when number is below 10 ', () => { + expect(asDecimalOrInteger(0.25435632645)).toEqual('0.3'); + expect(asDecimalOrInteger(1)).toEqual('1.0'); + expect(asDecimalOrInteger(3.374329704990765)).toEqual('3.4'); + expect(asDecimalOrInteger(5)).toEqual('5.0'); + expect(asDecimalOrInteger(9)).toEqual('9.0'); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/utils/formatters/formatters.ts b/x-pack/plugins/apm/common/utils/formatters/formatters.ts similarity index 55% rename from x-pack/plugins/apm/public/utils/formatters/formatters.ts rename to x-pack/plugins/apm/common/utils/formatters/formatters.ts index 6249ce53b6779..d84bf86d0de2f 100644 --- a/x-pack/plugins/apm/public/utils/formatters/formatters.ts +++ b/x-pack/plugins/apm/common/utils/formatters/formatters.ts @@ -23,3 +23,32 @@ export function tpmUnit(type?: string) { defaultMessage: 'tpm', }); } + +export function asPercent( + numerator: number, + denominator: number | undefined, + fallbackResult = '' +) { + if (!denominator || isNaN(numerator)) { + return fallbackResult; + } + + const decimal = numerator / denominator; + + // 33.2 => 33% + // 3.32 => 3.3% + // 0 => 0% + if (Math.abs(decimal) >= 0.1 || decimal === 0) { + return numeral(decimal).format('0%'); + } + + return numeral(decimal).format('0.0%'); +} + +export function asDecimalOrInteger(value: number) { + // exact 0 or above 10 should not have decimal + if (value === 0 || value >= 10) { + return asInteger(value); + } + return asDecimal(value); +} diff --git a/x-pack/plugins/apm/public/utils/formatters/index.ts b/x-pack/plugins/apm/common/utils/formatters/index.ts similarity index 100% rename from x-pack/plugins/apm/public/utils/formatters/index.ts rename to x-pack/plugins/apm/common/utils/formatters/index.ts diff --git a/x-pack/plugins/apm/public/utils/formatters/__test__/size.test.ts b/x-pack/plugins/apm/common/utils/formatters/size.test.ts similarity index 97% rename from x-pack/plugins/apm/public/utils/formatters/__test__/size.test.ts rename to x-pack/plugins/apm/common/utils/formatters/size.test.ts index 07d3d0c1eb08f..a394874fe6d87 100644 --- a/x-pack/plugins/apm/public/utils/formatters/__test__/size.test.ts +++ b/x-pack/plugins/apm/common/utils/formatters/size.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { getFixedByteFormatter, asDynamicBytes } from '../size'; +import { getFixedByteFormatter, asDynamicBytes } from './size'; describe('size formatters', () => { describe('byte formatting', () => { diff --git a/x-pack/plugins/apm/public/utils/formatters/size.ts b/x-pack/plugins/apm/common/utils/formatters/size.ts similarity index 100% rename from x-pack/plugins/apm/public/utils/formatters/size.ts rename to x-pack/plugins/apm/common/utils/formatters/size.ts diff --git a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts index f5764f7268ce5..7995b1d381700 100644 --- a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts +++ b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts @@ -23,6 +23,17 @@ export function registerApmAlerts( errors: [], }), requiresAppContext: true, + defaultActionMessage: i18n.translate( + 'xpack.apm.alertTypes.errorCount.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} alert is firing because of the following conditions: + +- Service name: \\{\\{context.serviceName\\}\\} +- Environment: \\{\\{context.environment\\}\\} +- Threshold: \\{\\{context.threshold\\}\\} errors +- Triggered value: \\{\\{context.triggerValue\\}\\} errors over the last \\{\\{context.interval\\}\\}`, + } + ), }); alertTypeRegistry.register({ @@ -38,6 +49,18 @@ export function registerApmAlerts( errors: [], }), requiresAppContext: true, + defaultActionMessage: i18n.translate( + 'xpack.apm.alertTypes.transactionDuration.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} alert is firing because of the following conditions: + +- Service name: \\{\\{context.serviceName\\}\\} +- Type: \\{\\{context.transactionType\\}\\} +- Environment: \\{\\{context.environment\\}\\} +- Threshold: \\{\\{context.threshold\\}\\}ms +- Triggered value: \\{\\{context.triggerValue\\}\\} over the last \\{\\{context.interval\\}\\}`, + } + ), }); alertTypeRegistry.register({ @@ -53,6 +76,18 @@ export function registerApmAlerts( errors: [], }), requiresAppContext: true, + defaultActionMessage: i18n.translate( + 'xpack.apm.alertTypes.transactionErrorRate.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} alert is firing because of the following conditions: + +- Service name: \\{\\{context.serviceName\\}\\} +- Type: \\{\\{context.transactionType\\}\\} +- Environment: \\{\\{context.environment\\}\\} +- Threshold: \\{\\{context.threshold\\}\\}% +- Triggered value: \\{\\{context.triggerValue\\}\\}% of errors over the last \\{\\{context.interval\\}\\}`, + } + ), }); alertTypeRegistry.register({ @@ -68,5 +103,18 @@ export function registerApmAlerts( errors: [], }), requiresAppContext: true, + defaultActionMessage: i18n.translate( + 'xpack.apm.alertTypes.transactionDurationAnomaly.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} alert is firing because of the following conditions: + +- Service name: \\{\\{context.serviceName\\}\\} +- Type: \\{\\{context.transactionType\\}\\} +- Environment: \\{\\{context.environment\\}\\} +- Severity threshold: \\{\\{context.threshold\\}\\} +- Severity value: \\{\\{context.thresholdValue\\}\\} +`, + } + ), }); } diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx index 3b118bcd91ff1..e17dd9a9eb038 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx @@ -12,7 +12,7 @@ import d3 from 'd3'; import { scaleUtc } from 'd3-scale'; import { mean } from 'lodash'; import React from 'react'; -import { asRelativeDateTimeRange } from '../../../../utils/formatters'; +import { asRelativeDateTimeRange } from '../../../../../common/utils/formatters'; import { getTimezoneOffsetInMs } from '../../../shared/charts/CustomPlot/getTimezoneOffsetInMs'; // @ts-expect-error import Histogram from '../../../shared/charts/Histogram'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx index c1192f5f18274..3938349050e5e 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx @@ -20,7 +20,7 @@ import { } from '../../../../../common/service_health_status'; import { useTheme } from '../../../../hooks/useTheme'; import { fontSize, px } from '../../../../style/variables'; -import { asInteger, asDuration } from '../../../../utils/formatters'; +import { asInteger, asDuration } from '../../../../../common/utils/formatters'; import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink'; import { popoverWidth } from '../cytoscapeOptions'; import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx index ba4451c37b304..1628a664a6c27 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceStatsList.tsx @@ -8,9 +8,12 @@ import { i18n } from '@kbn/i18n'; import { isNumber } from 'lodash'; import React from 'react'; import styled from 'styled-components'; -import { asPercent } from '../../../../../common/utils/formatters'; +import { + asDuration, + asPercent, + tpmUnit, +} from '../../../../../common/utils/formatters'; import { ServiceNodeStats } from '../../../../../common/service_map'; -import { asDuration, tpmUnit } from '../../../../utils/formatters'; export const ItemRow = styled('tr')` line-height: 2; diff --git a/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx index 28477d2448899..02dc3934dd01e 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx @@ -13,7 +13,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { asPercent } from '../../../../common/utils/formatters'; +import { + asDynamicBytes, + asInteger, + asPercent, +} from '../../../../common/utils/formatters'; import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { Projection } from '../../../../common/projections'; @@ -21,7 +25,6 @@ import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { ManagedTable, ITableColumn } from '../../shared/ManagedTable'; import { useFetcher } from '../../../hooks/useFetcher'; -import { asDynamicBytes, asInteger } from '../../../utils/formatters'; import { ServiceNodeMetricOverviewLink } from '../../shared/Links/apm/ServiceNodeMetricOverviewLink'; import { truncate, px, unit } from '../../../style/variables'; diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx index 4c7c0824a7c49..aa0222582b891 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx @@ -11,12 +11,15 @@ import styled from 'styled-components'; import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; -import { asPercent } from '../../../../../common/utils/formatters'; +import { + asPercent, + asDecimal, + asMillisecondDuration, +} from '../../../../../common/utils/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { fontSizes, px, truncate, unit } from '../../../../style/variables'; -import { asDecimal, asMillisecondDuration } from '../../../../utils/formatters'; import { ManagedTable, ITableColumn } from '../../../shared/ManagedTable'; import { EnvironmentBadge } from '../../../shared/EnvironmentBadge'; import { TransactionOverviewLink } from '../../../shared/Links/apm/TransactionOverviewLink'; diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx index cf2fe006f67d2..1c21824656754 100644 --- a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx @@ -10,8 +10,8 @@ import React from 'react'; import styled from 'styled-components'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TransactionGroup } from '../../../../server/lib/transaction_groups/fetcher'; +import { asMillisecondDuration } from '../../../../common/utils/formatters'; import { fontSizes, truncate } from '../../../style/variables'; -import { asMillisecondDuration } from '../../../utils/formatters'; import { EmptyMessage } from '../../shared/EmptyMessage'; import { ImpactBar } from '../../shared/ImpactBar'; import { ITableColumn, ManagedTable } from '../../shared/ManagedTable'; diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx index e18e6b1ed914e..67125d41635a9 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx @@ -10,12 +10,12 @@ import d3 from 'd3'; import { isEmpty } from 'lodash'; import React, { useCallback } from 'react'; import { ValuesType } from 'utility-types'; +import { getDurationFormatter } from '../../../../../common/utils/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TransactionDistributionAPIResponse } from '../../../../../server/lib/transactions/distribution'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DistributionBucket } from '../../../../../server/lib/transactions/distribution/get_buckets'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; -import { getDurationFormatter } from '../../../../utils/formatters'; // @ts-expect-error import Histogram from '../../../shared/charts/Histogram'; import { EmptyMessage } from '../../../shared/EmptyMessage'; diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx index e1b5ffcd0e0f5..070f8a54446b3 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx @@ -9,9 +9,9 @@ import styled from 'styled-components'; import { EuiIcon, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { asDuration } from '../../../../../../../common/utils/formatters'; import { isRumAgentName } from '../../../../../../../common/agent_name'; import { px, unit, units } from '../../../../../../style/variables'; -import { asDuration } from '../../../../../../utils/formatters'; import { ErrorCount } from '../../ErrorCount'; import { IWaterfallItem } from './waterfall_helpers/waterfall_helpers'; import { ErrorOverviewLink } from '../../../../../shared/Links/apm/ErrorOverviewLink'; diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionList/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionList/index.tsx index 68f9c240f1562..7f1dd100d721c 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionList/index.tsx @@ -10,8 +10,11 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { TransactionGroup } from '../../../../../server/lib/transaction_groups/fetcher'; +import { + asDecimal, + asMillisecondDuration, +} from '../../../../../common/utils/formatters'; import { fontFamilyCode, truncate } from '../../../../style/variables'; -import { asDecimal, asMillisecondDuration } from '../../../../utils/formatters'; import { ImpactBar } from '../../../shared/ImpactBar'; import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; diff --git a/x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx b/x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx index 7858bebead408..7e88ebc7639e3 100644 --- a/x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiToolTip, EuiText } from '@elastic/eui'; +import { asDuration } from '../../../../common/utils/formatters'; import { PercentOfParent } from '../../app/TransactionDetails/WaterfallWithSummmary/PercentOfParent'; -import { asDuration } from '../../../utils/formatters'; interface Props { duration: number; diff --git a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx index 504ff36c078f0..eaa291c33cff6 100644 --- a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx @@ -6,7 +6,10 @@ import React from 'react'; import { EuiToolTip } from '@elastic/eui'; import moment from 'moment-timezone'; -import { asAbsoluteDateTime, TimeUnit } from '../../../utils/formatters'; +import { + asAbsoluteDateTime, + TimeUnit, +} from '../../../../common/utils/formatters'; interface Props { /** diff --git a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx index d02c5a5d08927..9fc16ab0f9eab 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx @@ -13,11 +13,11 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { asAbsoluteDateTime } from '../../../../../common/utils/formatters'; import { useTheme } from '../../../../hooks/useTheme'; import { Maybe } from '../../../../../typings/common'; import { Annotation } from '../../../../../common/annotations'; import { PlotValues, SharedPlot } from './plotUtils'; -import { asAbsoluteDateTime } from '../../../../utils/formatters'; interface Props { annotations: Annotation[]; diff --git a/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js b/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js index be0716ba748dd..03fd039a3401e 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js +++ b/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js @@ -9,16 +9,16 @@ import React from 'react'; import d3 from 'd3'; import { HistogramInner } from '../index'; import response from './response.json'; -import { - getDurationFormatter, - asInteger, -} from '../../../../../utils/formatters'; import { disableConsoleWarning, toJson, mountWithTheme, } from '../../../../../utils/testHelpers'; import { getFormattedBuckets } from '../../../../app/TransactionDetails/Distribution/index'; +import { + asInteger, + getDurationFormatter, +} from '../../../../../../common/utils/formatters'; describe('Histogram', () => { let mockConsole; diff --git a/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx index 3b77ef3b51403..270ebd1c0830d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx @@ -5,18 +5,18 @@ */ import { EuiTitle } from '@elastic/eui'; import React from 'react'; -import { asPercent } from '../../../../../common/utils/formatters'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart'; -// @ts-expect-error -import CustomPlot from '../CustomPlot'; import { + asPercent, asDecimal, asInteger, asDynamicBytes, getFixedByteFormatter, asDuration, -} from '../../../../utils/formatters'; +} from '../../../../../common/utils/formatters'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart'; +// @ts-expect-error +import CustomPlot from '../CustomPlot'; import { Coordinate } from '../../../../../typings/timeseries'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; import { useChartsSync } from '../../../../hooks/useChartsSync'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx index 64e0fe33c982f..37d3664e98acd 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { EuiToolTip } from '@elastic/eui'; import styled from 'styled-components'; +import { asDuration } from '../../../../../../common/utils/formatters'; import { useTheme } from '../../../../../hooks/useTheme'; import { px, units } from '../../../../../style/variables'; -import { asDuration } from '../../../../../utils/formatters'; import { Legend } from '../../Legend'; import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx index 4567bc3f0f0b7..de63e2323ddac 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx @@ -7,6 +7,7 @@ import React, { useState } from 'react'; import { EuiPopover, EuiText } from '@elastic/eui'; import styled from 'styled-components'; +import { asDuration } from '../../../../../../common/utils/formatters'; import { useTheme } from '../../../../../hooks/useTheme'; import { TRACE_ID, @@ -14,7 +15,6 @@ import { } from '../../../../../../common/elasticsearch_fieldnames'; import { useUrlParams } from '../../../../../hooks/useUrlParams'; import { px, unit, units } from '../../../../../style/variables'; -import { asDuration } from '../../../../../utils/formatters'; import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks'; import { ErrorDetailLink } from '../../../Links/apm/ErrorDetailLink'; import { Legend, Shape } from '../../Legend'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx index 5cbfcc695e012..cb5a44432dcbc 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx @@ -8,9 +8,9 @@ import React, { ReactNode } from 'react'; import { inRange } from 'lodash'; import { Sticky } from 'react-sticky'; import { XAxis, XYPlot } from 'react-vis'; +import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { useTheme } from '../../../../hooks/useTheme'; import { px } from '../../../../style/variables'; -import { getDurationFormatter } from '../../../../utils/formatters'; import { Mark } from './'; import { LastTickValue } from './LastTickValue'; import { Marker } from './Marker'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js b/x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js index 7ee0c6280526b..7183c4851e993 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js +++ b/x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js @@ -18,7 +18,7 @@ import { fontSizes, } from '../../../../style/variables'; import { Legend } from '../Legend'; -import { asAbsoluteDateTime } from '../../../../utils/formatters'; +import { asAbsoluteDateTime } from '../../../../../common/utils/formatters'; const TooltipElm = styled.div` margin: 0 ${px(unit)}; diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.test.ts b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.test.ts index a476892fa4a3f..850e5d9a16112 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.test.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.test.ts @@ -9,11 +9,12 @@ import { getResponseTimeTooltipFormatter, getMaxY, } from './helper'; + +import { TimeSeries } from '../../../../../typings/timeseries'; import { getDurationFormatter, toMicroseconds, -} from '../../../../utils/formatters'; -import { TimeSeries } from '../../../../../typings/timeseries'; +} from '../../../../../common/utils/formatters'; describe('transaction chart helper', () => { describe('getResponseTimeTickFormatter', () => { diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.tsx index f11a33f932553..db245792982c3 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/helper.tsx @@ -5,10 +5,10 @@ */ import { flatten } from 'lodash'; +import { TimeFormatter } from '../../../../../common/utils/formatters'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; import { TimeSeries, Coordinate } from '../../../../../typings/timeseries'; -import { TimeFormatter } from '../../../../utils/formatters'; export function getResponseTimeTickFormatter(formatter: TimeFormatter) { return (t: number) => { diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index 30ee0ba3eaa1f..0b741447f6fec 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -24,7 +24,7 @@ import { Coordinate } from '../../../../../typings/timeseries'; import { LicenseContext } from '../../../../context/LicenseContext'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { ITransactionChartData } from '../../../../selectors/chartSelectors'; -import { asDecimal, tpmUnit } from '../../../../utils/formatters'; +import { asDecimal, tpmUnit } from '../../../../../common/utils/formatters'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; import { ErroneousTransactionsRateChart } from '../ErroneousTransactionsRateChart'; import { TransactionBreakdown } from '../../TransactionBreakdown'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.test.tsx index 78ff9a398b2e7..fc873cbda7bf2 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.test.tsx @@ -5,9 +5,9 @@ */ import React from 'react'; import { TimeSeries } from '../../../../../typings/timeseries'; -import { toMicroseconds } from '../../../../utils/formatters'; import { useFormatter } from './use_formatter'; import { render, fireEvent, act } from '@testing-library/react'; +import { toMicroseconds } from '../../../../../common/utils/formatters'; function MockComponent({ timeSeries, diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.ts b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.ts index 8cd8929c89960..d4694bc3caf1d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/use_formatter.ts @@ -9,7 +9,7 @@ import { isEmpty } from 'lodash'; import { getDurationFormatter, TimeFormatter, -} from '../../../../utils/formatters'; +} from '../../../../../common/utils/formatters'; import { TimeSeries } from '../../../../../typings/timeseries'; import { getMaxY } from './helper'; diff --git a/x-pack/plugins/apm/public/selectors/chartSelectors.ts b/x-pack/plugins/apm/public/selectors/chartSelectors.ts index 26c2365ed77e1..8c6093859f969 100644 --- a/x-pack/plugins/apm/public/selectors/chartSelectors.ts +++ b/x-pack/plugins/apm/public/selectors/chartSelectors.ts @@ -17,10 +17,10 @@ import { RectCoordinate, TimeSeries, } from '../../typings/timeseries'; -import { asDecimal, asDuration, tpmUnit } from '../utils/formatters'; import { IUrlParams } from '../context/UrlParamsContext/types'; import { getEmptySeries } from '../components/shared/charts/CustomPlot/getEmptySeries'; import { httpStatusCodeToColor } from '../utils/httpStatusCodeToColor'; +import { asDecimal, asDuration, tpmUnit } from '../../common/utils/formatters'; export interface ITpmBucket { title: string; diff --git a/x-pack/plugins/apm/server/lib/alerts/action_variables.ts b/x-pack/plugins/apm/server/lib/alerts/action_variables.ts index f2558da3a30e4..19af337e18811 100644 --- a/x-pack/plugins/apm/server/lib/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/lib/alerts/action_variables.ts @@ -45,4 +45,14 @@ export const apmActionVariables = { ), name: 'triggerValue', }, + interval: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.intervalSize', + { + defaultMessage: + 'The length and unit of the time period where the alert conditions were met', + } + ), + name: 'interval', + }, }; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.test.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.test.ts index 6a6e2b7137055..8dbc8054d4371 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.test.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.test.ts @@ -100,7 +100,7 @@ describe('Error count alert', () => { })), alertInstanceFactory: jest.fn(() => ({ scheduleActions })), }; - const params = { threshold: 1 }; + const params = { threshold: 1, windowSize: 5, windowUnit: 'm' }; await alertExecutor!({ services, params }); [ @@ -117,24 +117,28 @@ describe('Error count alert', () => { environment: 'env-foo', threshold: 1, triggerValue: 2, + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'foo', environment: 'env-foo-2', threshold: 1, triggerValue: 2, + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', environment: 'env-bar', threshold: 1, triggerValue: 2, + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', environment: 'env-bar-2', threshold: 1, triggerValue: 2, + interval: '5m', }); }); it('sends alerts with service name', async () => { @@ -174,7 +178,7 @@ describe('Error count alert', () => { })), alertInstanceFactory: jest.fn(() => ({ scheduleActions })), }; - const params = { threshold: 1 }; + const params = { threshold: 1, windowSize: 5, windowUnit: 'm' }; await alertExecutor!({ services, params }); ['apm.error_rate_foo', 'apm.error_rate_bar'].forEach((instanceName) => @@ -186,12 +190,14 @@ describe('Error count alert', () => { environment: undefined, threshold: 1, triggerValue: 2, + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', environment: undefined, threshold: 1, triggerValue: 2, + interval: '5m', }); }); }); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index 26e4a5e84b995..7b63f2c354916 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -55,6 +55,7 @@ export function registerErrorCountAlertType({ apmActionVariables.environment, apmActionVariables.threshold, apmActionVariables.triggerValue, + apmActionVariables.interval, ], }, producer: 'apm', @@ -138,6 +139,7 @@ export function registerErrorCountAlertType({ environment, threshold: alertParams.threshold, triggerValue: errorCount, + interval: `${alertParams.windowSize}${alertParams.windowUnit}`, }); } response.aggregations?.services.buckets.forEach((serviceBucket) => { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index 373d4bd4da832..1d8b664751ba2 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; +import { getDurationFormatter } from '../../../common/utils/formatters'; import { ProcessorEvent } from '../../../common/processor_event'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; import { ESSearchResponse } from '../../../typings/elasticsearch'; @@ -15,6 +16,7 @@ import { SERVICE_NAME, TRANSACTION_TYPE, TRANSACTION_DURATION, + SERVICE_ENVIRONMENT, } from '../../../common/elasticsearch_fieldnames'; import { AlertingPlugin } from '../../../../alerts/server'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; @@ -62,6 +64,7 @@ export function registerTransactionDurationAlertType({ apmActionVariables.environment, apmActionVariables.threshold, apmActionVariables.triggerValue, + apmActionVariables.interval, ], }, producer: 'apm', @@ -106,6 +109,11 @@ export function registerTransactionDurationAlertType({ ], }, }, + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + }, + }, }, }, }; @@ -119,7 +127,7 @@ export function registerTransactionDurationAlertType({ return; } - const { agg } = response.aggregations; + const { agg, environments } = response.aggregations; const transactionDuration = 'values' in agg ? Object.values(agg.values)[0] : agg?.value; @@ -127,15 +135,25 @@ export function registerTransactionDurationAlertType({ const threshold = alertParams.threshold * 1000; if (transactionDuration && transactionDuration > threshold) { - const alertInstance = services.alertInstanceFactory( - AlertType.TransactionDuration - ); - alertInstance.scheduleActions(alertTypeConfig.defaultActionGroupId, { - transactionType: alertParams.transactionType, - serviceName: alertParams.serviceName, - environment: alertParams.environment, - threshold, - triggerValue: transactionDuration, + const durationFormatter = getDurationFormatter(transactionDuration); + const transactionDurationFormatted = durationFormatter( + transactionDuration + ).formatted; + + environments.buckets.map((bucket) => { + const environment = bucket.key; + const alertInstance = services.alertInstanceFactory( + `${AlertType.TransactionDuration}_${environment}` + ); + + alertInstance.scheduleActions(alertTypeConfig.defaultActionGroupId, { + transactionType: alertParams.transactionType, + serviceName: alertParams.serviceName, + environment, + threshold, + triggerValue: transactionDurationFormatted, + interval: `${alertParams.windowSize}${alertParams.windowUnit}`, + }); }); } }, diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.test.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.test.ts index 6e97262dd77bb..17f6bb3ac19cf 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.test.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.test.ts @@ -180,12 +180,14 @@ describe('Transaction duration anomaly alert', () => { transaction_types: { buckets: [{ key: 'type-foo' }], }, + record_avg: { value: 80 }, }, { key: 'bar', transaction_types: { buckets: [{ key: 'type-bar' }], }, + record_avg: { value: 20 }, }, ], }, @@ -223,11 +225,15 @@ describe('Transaction duration anomaly alert', () => { serviceName: 'foo', transactionType: 'type-foo', environment: 'production', + threshold: 'minor', + thresholdValue: 'critical', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: 'type-bar', environment: 'production', + threshold: 'minor', + thresholdValue: 'warning', }); }); @@ -267,7 +273,10 @@ describe('Transaction duration anomaly alert', () => { hits: { total: { value: 2 } }, aggregations: { services: { - buckets: [{ key: 'foo' }, { key: 'bar' }], + buckets: [ + { key: 'foo', record_avg: { value: 80 } }, + { key: 'bar', record_avg: { value: 20 } }, + ], }, }, }), @@ -305,21 +314,29 @@ describe('Transaction duration anomaly alert', () => { serviceName: 'foo', transactionType: undefined, environment: 'production', + threshold: 'minor', + thresholdValue: 'critical', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: undefined, environment: 'production', + threshold: 'minor', + thresholdValue: 'warning', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'foo', transactionType: undefined, environment: 'testing', + threshold: 'minor', + thresholdValue: 'critical', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: undefined, environment: 'testing', + threshold: 'minor', + thresholdValue: 'warning', }); }); }); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts index 36b7964e8128d..9b3da56b6f640 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { isEmpty } from 'lodash'; +import { getSeverity } from '../../../common/anomaly_detection'; import { ANOMALY_SEVERITY } from '../../../../ml/common'; import { KibanaRequest } from '../../../../../../src/core/server'; import { @@ -61,6 +62,8 @@ export function registerTransactionDurationAnomalyAlertType({ apmActionVariables.serviceName, apmActionVariables.transactionType, apmActionVariables.environment, + apmActionVariables.threshold, + apmActionVariables.triggerValue, ], }, producer: 'apm', @@ -148,6 +151,11 @@ export function registerTransactionDurationAnomalyAlertType({ field: 'by_field_value', }, }, + record_avg: { + avg: { + field: 'record_score', + }, + }, }, }, }, @@ -162,6 +170,7 @@ export function registerTransactionDurationAnomalyAlertType({ services: { buckets: Array<{ key: string; + record_avg: { value: number }; transaction_types: { buckets: Array<{ key: string }> }; }>; }; @@ -173,10 +182,12 @@ export function registerTransactionDurationAnomalyAlertType({ if (hitCount > 0) { function scheduleAction({ serviceName, + severity, environment, transactionType, }: { serviceName: string; + severity: string; environment?: string; transactionType?: string; }) { @@ -192,23 +203,31 @@ export function registerTransactionDurationAnomalyAlertType({ const alertInstance = services.alertInstanceFactory( alertInstanceName ); + alertInstance.scheduleActions(alertTypeConfig.defaultActionGroupId, { serviceName, environment, transactionType, + threshold: selectedOption?.label, + thresholdValue: severity, }); } - mlJobs.map((job) => { const environment = job.custom_settings?.job_tags?.environment; response.aggregations?.services.buckets.forEach((serviceBucket) => { const serviceName = serviceBucket.key as string; + const severity = getSeverity(serviceBucket.record_avg.value); if (isEmpty(serviceBucket.transaction_types?.buckets)) { - scheduleAction({ serviceName, environment }); + scheduleAction({ serviceName, severity, environment }); } else { serviceBucket.transaction_types?.buckets.forEach((typeBucket) => { const transactionType = typeBucket.key as string; - scheduleAction({ serviceName, environment, transactionType }); + scheduleAction({ + serviceName, + severity, + environment, + transactionType, + }); }); } }); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.test.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.test.ts index 90db48f84b5d9..7c13f2a17b255 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.test.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.test.ts @@ -115,7 +115,7 @@ describe('Transaction error rate alert', () => { })), alertInstanceFactory: jest.fn(() => ({ scheduleActions })), }; - const params = { threshold: 10 }; + const params = { threshold: 10, windowSize: 5, windowUnit: 'm' }; await alertExecutor!({ services, params }); [ @@ -132,28 +132,32 @@ describe('Transaction error rate alert', () => { transactionType: 'type-foo', environment: 'env-foo', threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'foo', transactionType: 'type-foo', environment: 'env-foo-2', threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: 'type-bar', environment: 'env-bar', threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: 'type-bar', environment: 'env-bar-2', threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); }); it('sends alerts with service name and transaction type', async () => { @@ -202,7 +206,7 @@ describe('Transaction error rate alert', () => { })), alertInstanceFactory: jest.fn(() => ({ scheduleActions })), }; - const params = { threshold: 10 }; + const params = { threshold: 10, windowSize: 5, windowUnit: 'm' }; await alertExecutor!({ services, params }); [ @@ -217,14 +221,16 @@ describe('Transaction error rate alert', () => { transactionType: 'type-foo', environment: undefined, threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: 'type-bar', environment: undefined, threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); }); @@ -261,7 +267,7 @@ describe('Transaction error rate alert', () => { })), alertInstanceFactory: jest.fn(() => ({ scheduleActions })), }; - const params = { threshold: 10 }; + const params = { threshold: 10, windowSize: 5, windowUnit: 'm' }; await alertExecutor!({ services, params }); [ @@ -276,14 +282,16 @@ describe('Transaction error rate alert', () => { transactionType: undefined, environment: undefined, threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', transactionType: undefined, environment: undefined, threshold: 10, - triggerValue: 50, + triggerValue: '50', + interval: '5m', }); }); }); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts index e14360029e5dd..969f4ceaca93a 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { isEmpty } from 'lodash'; +import { asDecimalOrInteger } from '../../../common/utils/formatters'; import { ProcessorEvent } from '../../../common/processor_event'; import { EventOutcome } from '../../../common/event_outcome'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; @@ -60,6 +61,7 @@ export function registerTransactionErrorRateAlertType({ apmActionVariables.environment, apmActionVariables.threshold, apmActionVariables.triggerValue, + apmActionVariables.interval, ], }, producer: 'apm', @@ -170,7 +172,8 @@ export function registerTransactionErrorRateAlertType({ transactionType, environment, threshold: alertParams.threshold, - triggerValue: transactionErrorRate, + triggerValue: asDecimalOrInteger(transactionErrorRate), + interval: `${alertParams.windowSize}${alertParams.windowUnit}`, }); }