diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx index 92cd229333062..3bf792840ab71 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx @@ -12,9 +12,13 @@ import { i18n } from '@kbn/i18n'; import { EuiPanel } from '@elastic/eui'; import { EuiTitle } from '@elastic/eui'; import { EuiIconTip } from '@elastic/eui'; -import { ALERT_DURATION, ALERT_END } from '@kbn/rule-data-utils'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_EVALUATION_THRESHOLD, + ALERT_RULE_TYPE_ID, +} from '@kbn/rule-data-utils'; import moment from 'moment'; -import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { getTransactionType } from '../../../../context/apm_service/apm_service_context'; import { useServiceAgentFetcher } from '../../../../context/apm_service/use_service_agent_fetcher'; import { useServiceTransactionTypesFetcher } from '../../../../context/apm_service/use_service_transaction_types_fetcher'; @@ -41,9 +45,16 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from './types'; -import { getAggsTypeFromRule } from './helpers'; +import { getAggsTypeFromRule, isLatencyThresholdRuleType } from './helpers'; import { filterNil } from '../../../shared/charts/latency_chart'; import { errorRateI18n } from '../../../shared/charts/failed_transaction_rate_chart'; +import { + AlertActiveRect, + AlertAnnotation, + AlertThresholdRect, + AlertThresholdAnnotation, +} from './latency_chart_components'; +import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; export function AlertDetailsAppSection({ rule, @@ -51,10 +62,8 @@ export function AlertDetailsAppSection({ timeZone, }: AlertDetailsAppSectionProps) { const params = rule.params; - const environment = String(params.environment) || ENVIRONMENT_ALL.value; - const latencyAggregationType = getAggsTypeFromRule( - params.aggregationType as string - ); + const environment = alert.fields[SERVICE_ENVIRONMENT]; + const latencyAggregationType = getAggsTypeFromRule(params.aggregationType); // duration is us, convert it to MS const alertDurationMS = alert.fields[ALERT_DURATION]! / 1000; @@ -103,7 +112,7 @@ export function AlertDetailsAppSection({ }); const transactionType = getTransactionType({ - transactionType: String(alert.fields[TRANSACTION_TYPE]), + transactionType: alert.fields[TRANSACTION_TYPE], transactionTypes, agentName, }); @@ -289,9 +298,32 @@ export function AlertDetailsAppSection({ }), }, ]; - /* Error Rate */ + const getLatencyChartAdditionalData = () => { + if (isLatencyThresholdRuleType(alert.fields[ALERT_RULE_TYPE_ID])) { + return [ + , + , + , + , + ]; + } + }; + return ( @@ -313,6 +345,7 @@ export function AlertDetailsAppSection({ + ruleTypeId === 'apm.transaction_duration'; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_active_rect.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_active_rect.tsx new file mode 100644 index 0000000000000..ff6d76165c1fa --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_active_rect.tsx @@ -0,0 +1,32 @@ +/* + * 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 { RectAnnotation } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function AlertActiveRect({ alertStarted }: { alertStarted: number }) { + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_annotation.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_annotation.tsx new file mode 100644 index 0000000000000..27354ea2ac3b8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_annotation.tsx @@ -0,0 +1,47 @@ +/* + * 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 React from 'react'; +import { + AnnotationDomainType, + LineAnnotation, + Position, +} from '@elastic/charts'; +import moment from 'moment'; +import { EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { DEFAULT_DATE_FORMAT } from '../constants'; + +export function AlertAnnotation({ alertStarted }: { alertStarted: number }) { + return ( + } + markerPosition={Position.Top} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_annotation.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_annotation.tsx new file mode 100644 index 0000000000000..c1c6ddefd32f1 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_annotation.tsx @@ -0,0 +1,37 @@ +/* + * 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 React from 'react'; +import { AnnotationDomainType, LineAnnotation } from '@elastic/charts'; + +export function AlertThresholdAnnotation({ + threshold, +}: { + threshold?: number; +}) { + if (!threshold) return <>; + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_rect.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_rect.tsx new file mode 100644 index 0000000000000..24897dce1f63c --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_rect.tsx @@ -0,0 +1,39 @@ +/* + * 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 { RectAnnotation } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function AlertThresholdRect({ + threshold, + alertStarted, +}: { + threshold?: number; + alertStarted: number; +}) { + if (!threshold) return <>; + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/index.ts b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/index.ts new file mode 100644 index 0000000000000..a5d53b0ee3766 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/index.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export { AlertAnnotation } from './alert_annotation'; +export { AlertThresholdRect } from './alert_threshold_rect'; +export { AlertActiveRect } from './alert_active_rect'; +export { AlertThresholdAnnotation } from './alert_threshold_annotation'; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts index 0094d9332009a..99fafb00bcac0 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts @@ -8,17 +8,21 @@ import { Rule } from '@kbn/alerting-plugin/common'; import { TopAlert } from '@kbn/observability-plugin/public/pages/alerts'; import { TIME_UNITS } from '@kbn/triggers-actions-ui-plugin/public'; -import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; +import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; export const SERVICE_NAME = 'service.name' as const; export const TRANSACTION_TYPE = 'transaction.type' as const; export interface AlertDetailsAppSectionProps { rule: Rule<{ environment: string; - aggregationType: LatencyAggregationType; + aggregationType: string; windowSize: number; windowUnit: TIME_UNITS; }>; - alert: TopAlert<{ [SERVICE_NAME]: string; [TRANSACTION_TYPE]: string }>; + alert: TopAlert<{ + [SERVICE_NAME]: string; + [TRANSACTION_TYPE]: string; + [SERVICE_ENVIRONMENT]: string; + }>; timeZone: string; } diff --git a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx index 59d52658682d5..6f5bf5819e240 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx @@ -6,7 +6,6 @@ */ import { - AnnotationDomainType, AreaSeries, Axis, BarSeries, @@ -26,12 +25,11 @@ import { } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { ReactElement } from 'react'; import { useHistory } from 'react-router-dom'; import { useChartTheme } from '@kbn/observability-plugin/public'; import { isExpectedBoundsComparison } from '../time_comparison/get_comparison_options'; -import { asAbsoluteDateTime } from '../../../../common/utils/formatters'; -import { useAnnotationsContext } from '../../../context/annotations/use_annotations_context'; + import { useChartPointerEventContext } from '../../../context/chart_pointer_event/use_chart_pointer_event_context'; import { useTheme } from '../../../hooks/use_theme'; import { unit } from '../../../utils/style'; @@ -51,6 +49,9 @@ interface TimeseriesChartProps extends TimeseriesChartWithContextProps { comparisonEnabled: boolean; offset?: string; timeZone: string; + annotations?: Array< + ReactElement + >; } export function TimeseriesChart({ id, @@ -67,9 +68,9 @@ export function TimeseriesChart({ comparisonEnabled, offset, timeZone, + annotations, }: TimeseriesChartProps) { const history = useHistory(); - const { annotations } = useAnnotationsContext(); const { chartRef, updatePointerEvent } = useChartPointerEventContext(); const theme = useTheme(); const chartTheme = useChartTheme(); @@ -79,7 +80,6 @@ export function TimeseriesChart({ anomalyTimeseriesColor: anomalyTimeseries?.color, }); const isEmpty = isTimeseriesEmpty(timeseries); - const annotationColor = theme.eui.euiColorSuccess; const isComparingExpectedBounds = comparisonEnabled && isExpectedBoundsComparison(offset); const allSeries = [ @@ -219,26 +219,7 @@ export function TimeseriesChart({ tickFormat={yTickFormat ? yTickFormat : yLabelFormat} labelFormat={yLabelFormat} /> - - {showAnnotations && ( - ({ - dataValue: annotation['@timestamp'], - header: asAbsoluteDateTime(annotation['@timestamp']), - details: `${i18n.translate('xpack.apm.chart.annotation.version', { - defaultMessage: 'Version', - })} ${annotation.text}`, - }))} - style={{ - line: { strokeWidth: 1, stroke: annotationColor, opacity: 1 }, - }} - marker={} - markerPosition={Position.Top} - /> - )} - + {showAnnotations && annotations} - {allSeries.map((serie) => { const Series = getChartType(serie.type); diff --git a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart_with_context.tsx b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart_with_context.tsx index 5c9aac5d28bdf..101fe2b9c8967 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart_with_context.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart_with_context.tsx @@ -5,8 +5,18 @@ * 2.0. */ -import { LegendItemListener, YDomainRange } from '@elastic/charts'; +import { + AnnotationDomainType, + LegendItemListener, + LineAnnotation, + Position, + YDomainRange, +} from '@elastic/charts'; import React from 'react'; +import { EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { asAbsoluteDateTime } from '../../../../common/utils/formatters'; +import { useAnnotationsContext } from '../../../context/annotations/use_annotations_context'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { ServiceAnomalyTimeseries } from '../../../../common/anomaly_detection/service_anomaly_timeseries'; import { Coordinate, TimeSeries } from '../../../../typings/timeseries'; @@ -15,6 +25,7 @@ import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { unit } from '../../../utils/style'; import { getTimeZone } from './helper/timezone'; import { TimeseriesChart } from './timeseries_chart'; +import { useTheme } from '../../../hooks/use_theme'; interface AnomalyTimeseries extends ServiceAnomalyTimeseries { color?: string; @@ -62,7 +73,28 @@ export function TimeseriesChartWithContext({ ); const { core } = useApmPluginContext(); const timeZone = getTimeZone(core.uiSettings); + const theme = useTheme(); + const annotationColor = theme.eui.euiColorSuccess; + const { annotations } = useAnnotationsContext(); + const getAnnotations = () => ( + ({ + dataValue: annotation['@timestamp'], + header: asAbsoluteDateTime(annotation['@timestamp']), + details: `${i18n.translate('xpack.apm.chart.annotation.version', { + defaultMessage: 'Version', + })} ${annotation.text}`, + }))} + style={{ + line: { strokeWidth: 1, stroke: annotationColor, opacity: 1 }, + }} + marker={} + markerPosition={Position.Top} + /> + ); return (