From 9128aa6c3573094054e914ec4567985380b0f33a Mon Sep 17 00:00:00 2001 From: Phillip Burch Date: Mon, 28 Sep 2020 09:03:08 -0500 Subject: [PATCH] Show anomaly annotation on timeline --- .../components/timeline/timeline.tsx | 93 ++++++++++++------- .../inventory_view/hooks/use_timeline.ts | 8 +- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx index 7184b146bb04c..e3cc443061935 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import React, { useMemo, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import { first, last } from 'lodash'; -import { EuiLoadingChart, EuiText, EuiEmptyPrompt, EuiButton } from '@elastic/eui'; +import { EuiLoadingChart, EuiText, EuiEmptyPrompt, EuiButton, EuiIcon } from '@elastic/eui'; import { Axis, Chart, @@ -18,6 +18,9 @@ import { TooltipValue, niceTimeFormatter, ElementClickListener, + LineAnnotation, + AnnotationDomainTypes, + LineAnnotationDatum, } from '@elastic/charts'; import { useUiSetting } from '../../../../../../../../../src/plugins/kibana_react/public'; import { toMetricOpt } from '../../../../../../common/snapshot_metric_i18n'; @@ -49,18 +52,8 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible const { metric, nodeType, accountId, region } = useWaffleOptionsContext(); const { currentTime, jumpToTime, stopAutoReload } = useWaffleTimeContext(); const { filterQueryAsJson } = useWaffleFiltersContext(); - const [start] = useState(moment().toDate().getTime()); - const SORT_DEFAULTS = { - direction: 'desc' as const, - field: 'anomalyScore' as const, - }; - - const PAGINATION_DEFAULTS = { - pageSize: 100, - }; - - const { loading, error, timeseries, reload } = useTimeline( + const { loading, error, startTime, endTime, timeseries, reload } = useTimeline( filterQueryAsJson, [metric], nodeType, @@ -72,21 +65,23 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible isVisible ); - const { metricsHostsAnomalies, getMetricsHostsAnomalies } = useMetricsHostsAnomaliesResults({ + const anomalyParams = { sourceId: 'default', - startTime: moment(new Date(start)).subtract(10, 'd').toDate().getTime(), - endTime: start, - defaultSortOptions: SORT_DEFAULTS, - defaultPaginationOptions: PAGINATION_DEFAULTS, - }); + startTime, + endTime, + defaultSortOptions: { + direction: 'desc' as const, + field: 'anomalyScore' as const, + }, + defaultPaginationOptions: { pageSize: 100 }, + }; - const { metricsK8sAnomalies, getMetricsK8sAnomalies } = useMetricsK8sAnomaliesResults({ - sourceId: 'default', - startTime: moment(new Date(start)).subtract(10, 'd').toDate().getTime(), - endTime: start, - defaultSortOptions: SORT_DEFAULTS, - defaultPaginationOptions: PAGINATION_DEFAULTS, - }); + const { metricsHostsAnomalies, getMetricsHostsAnomalies } = useMetricsHostsAnomaliesResults( + anomalyParams + ); + const { metricsK8sAnomalies, getMetricsK8sAnomalies } = useMetricsK8sAnomaliesResults( + anomalyParams + ); const getAnomalies = useMemo(() => { if (nodeType === 'host') { @@ -96,13 +91,13 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible } }, [nodeType, getMetricsK8sAnomalies, getMetricsHostsAnomalies]); - // const anomalies = useMemo(() => { - // if (nodeType === 'host') { - // return metricsHostsAnomalies; - // } else if (nodeType === 'pod') { - // return metricsK8sAnomalies; - // } - // }, [nodeType, metricsHostsAnomalies, metricsK8sAnomalies]); + const anomalies = useMemo(() => { + if (nodeType === 'host') { + return metricsHostsAnomalies; + } else if (nodeType === 'pod') { + return metricsK8sAnomalies; + } + }, [nodeType, metricsHostsAnomalies, metricsK8sAnomalies]); const metricLabel = toMetricOpt(metric.type)?.textLC; @@ -194,6 +189,10 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible ); } + function generateAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` })); + } + return ( @@ -209,6 +208,19 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible + {(anomalies || []).map((a) => { + return ( + alert('clicked')} iconType="alert" />} + markerPosition={'bottom'} + /> + ); + })} = ({ interval, yAxisFormatter, isVisible ); }; +const annotationStyle = { + line: { + strokeWidth: 3, + stroke: '#f00', + opacity: 1, + }, + details: { + fontSize: 12, + fontFamily: 'Arial', + fontStyle: 'bold', + fill: 'gray', + padding: 0, + }, +}; + const TimelineContainer = euiStyled.div` background-color: ${(props) => props.theme.eui.euiPageBackgroundColor}; border-top: 1px solid ${(props) => props.theme.eui.euiColorLightShade}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts index acf9011ac7ddd..597c268180819 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts @@ -81,10 +81,12 @@ export function useTimeline( ]); const { timeLength, intervalInSeconds } = timeLengthResult; + const endTime = currentTime + intervalInSeconds * 1000; + const startTime = currentTime - timeLength * 1000; const timerange: InfraTimerangeInput = { interval: displayInterval ?? '', - to: currentTime + intervalInSeconds * 1000, - from: currentTime - timeLength * 1000, + to: endTime, + from: startTime, ignoreLookback: true, forceInterval: true, }; @@ -127,6 +129,8 @@ export function useTimeline( error: (error && error.message) || null, loading: !interval ? true : loading, timeseries, + startTime, + endTime, reload: makeRequest, }; }