From 4adc91d307a054756b66c4b668cad4f551020e07 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 3 Oct 2019 15:15:09 +0100 Subject: [PATCH 01/89] Amend outer page / panel structure --- .../logs/analysis/page_results_content.tsx | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index aaf24c22594e5..d3b6b6e3b18a7 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -131,27 +131,26 @@ export const AnalysisResultsContent = ({ ) : ( <> - - - - - - - - - - - - - + + + + + + + + + + + + {isFirstUse && !hasResults ? : null} - - - - + + + + Anomalies + + + )} From 62c7a6b7d488b6ac84f28f7cce115020e981d2af Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 3 Oct 2019 15:18:37 +0100 Subject: [PATCH 02/89] Remove unused imports --- .../logs/analysis/page_results_content.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index d3b6b6e3b18a7..be07449e2898a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -5,21 +5,10 @@ */ import datemath from '@elastic/datemath'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPanel, - EuiSuperDatePicker, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel, EuiSuperDatePicker } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React, { useCallback, useMemo, useState } from 'react'; - -import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; @@ -184,7 +173,3 @@ const stringToNumericTimeRange = (timeRange: StringTimeRange): TimeRange => ({ }) ).valueOf(), }); - -const ExpandingPage = euiStyled(EuiPage)` - flex: 1 0 0%; -`; From 0cefd2378e755af3741341e3f7fcebb822b149f4 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 7 Oct 2019 17:09:57 +0100 Subject: [PATCH 03/89] Add anomalies section and overall chart --- .../logs/analysis/page_results_content.tsx | 10 +- .../analysis/sections/anomalies/chart.tsx | 106 +++++++++++++ .../analysis/sections/anomalies/index.tsx | 139 ++++++++++++++++++ .../analysis/sections/log_rate/bar_chart.tsx | 72 +-------- .../logs/analysis/sections/log_rate/index.tsx | 27 +++- 5 files changed, 280 insertions(+), 74 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index be07449e2898a..f5bcc4196cf9d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -20,6 +20,7 @@ import { import { useTrackPageview } from '../../../hooks/use_track_metric'; import { FirstUseCallout } from './first_use'; import { LogRateResults } from './sections/log_rate'; +import { AnomaliesResults } from './sections/anomalies'; export const AnalysisResultsContent = ({ sourceId, @@ -150,7 +151,14 @@ export const AnalysisResultsContent = ({ - Anomalies + + + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx new file mode 100644 index 0000000000000..90c000a706921 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -0,0 +1,106 @@ +/* + * 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 { RectAnnotationDatum } from '@elastic/charts'; +import { + Axis, + BarSeries, + Chart, + getAxisId, + getSpecId, + niceTimeFormatter, + Settings, + TooltipValue, + LIGHT_THEME, + DARK_THEME, + getAnnotationId, + RectAnnotation, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import React, { useCallback, useMemo } from 'react'; + +import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; +import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting'; + +export const AnomaliesChart: React.FunctionComponent<{ + chartId: string; + setTimeRange: (timeRange: TimeRange) => void; + timeRange: TimeRange; + series: Array<{ time: number; value: number }>; + annotations: RectAnnotationDatum[]; +}> = ({ chartId, series, annotations, setTimeRange, timeRange }) => { + const [dateFormat] = useKibanaUiSetting('dateFormat'); + const [isDarkMode] = useKibanaUiSetting('theme:darkMode'); + + const chartDateFormatter = useMemo( + () => niceTimeFormatter([timeRange.startTime, timeRange.endTime]), + [timeRange] + ); + + const logEntryRateSpecId = getSpecId('averageValues'); + + const tooltipProps = useMemo( + () => ({ + headerFormatter: (tooltipData: TooltipValue) => + moment(tooltipData.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'), + }), + [dateFormat] + ); + + const handleBrushEnd = useCallback( + (startTime: number, endTime: number) => { + setTimeRange({ + endTime, + startTime, + }); + }, + [setTimeRange] + ); + const chartAnnotationsId = getAnnotationId(`anomalies-${chartId}`); + + return ( +
+ + + Number(value).toFixed(0)} + /> + + + + +
+ ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx new file mode 100644 index 0000000000000..545097c7206fd --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -0,0 +1,139 @@ +/* + * 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 { RectAnnotationDatum } from '@elastic/charts'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingChart, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useMemo } from 'react'; + +import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; +import { AnomaliesChart } from './chart'; + +export const AnomaliesResults = ({ + isLoading, + results, + setTimeRange, + timeRange, +}: { + isLoading: boolean; + results: GetLogEntryRateSuccessResponsePayload['data'] | null; + setTimeRange: (timeRange: TimeRange) => void; + timeRange: TimeRange; +}) => { + const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { + defaultMessage: 'Anomalies', + }); + + const loadingAriaLabel = i18n.translate( + 'xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel', + { defaultMessage: 'Loading anomalies' } + ); + + const logEntryRateSeries = useMemo( + () => + results && results.histogramBuckets + ? results.histogramBuckets.reduce>( + (buckets, bucket) => { + return [ + ...buckets, + { + time: bucket.startTime, + value: bucket.dataSets.reduce((accumulatedValue, dataSet) => { + return accumulatedValue + dataSet.averageActualLogEntryRate; + }, 0), + }, + ]; + }, + [] + ) + : [], + [results] + ); + + const logEntryRateAnomalyAnnotations = useMemo( + () => + results && results.histogramBuckets + ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { + const anomalies = bucket.dataSets.reduce( + (accumulatedAnomalies, dataSet) => [...accumulatedAnomalies, ...dataSet.anomalies], + [] + ); + if (anomalies.length <= 0) { + return annotatedBuckets; + } + return [ + ...annotatedBuckets, + { + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, + }, + details: i18n.translate( + 'xpack.infra.logs.analysis.anomalySectionAnomalyCountTooltipLabel', + { + defaultMessage: `{anomalyCount, plural, one {# anomaly} other {# anomalies}}`, + values: { + anomalyCount: anomalies.length, + }, + } + ), + }, + ]; + }, []) + : [], + [results] + ); + + return ( + <> + +

{title}

+
+ + {isLoading ? ( + + + + + + ) : !results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( + + {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataTitle', { + defaultMessage: 'There is no data to display.', + })} + + } + titleSize="m" + body={ +

+ {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoDataBody', { + defaultMessage: 'You may want to adjust your time range.', + })} +

+ } + /> + ) : ( + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index 0719bf956b680..8819d5f0d99d3 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -8,12 +8,9 @@ import { Axis, BarSeries, Chart, - getAnnotationId, getAxisId, getSpecId, niceTimeFormatter, - RectAnnotation, - RectAnnotationDatum, Settings, TooltipValue, LIGHT_THEME, @@ -23,18 +20,14 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React, { useCallback, useMemo } from 'react'; -import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting'; -type LogEntryRateHistogramBuckets = GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets']; - export const LogEntryRateBarChart: React.FunctionComponent<{ - bucketDuration: number; - histogramBuckets: LogEntryRateHistogramBuckets | null; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; -}> = ({ bucketDuration, histogramBuckets, setTimeRange, timeRange }) => { + series: Array<{ group: string; time: number; value: number }>; +}> = ({ series, setTimeRange, timeRange }) => { const [dateFormat] = useKibanaUiSetting('dateFormat'); const [isDarkMode] = useKibanaUiSetting('theme:darkMode'); @@ -43,62 +36,7 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ [timeRange] ); - const logEntryRateSeries = useMemo( - () => - histogramBuckets - ? histogramBuckets.reduce>( - (buckets, bucket) => { - return [ - ...buckets, - ...bucket.dataSets.map(dataSet => ({ - group: dataSet.dataSetId === '' ? 'unknown' : dataSet.dataSetId, - time: bucket.startTime, - value: dataSet.averageActualLogEntryRate, - })), - ]; - }, - [] - ) - : [], - [histogramBuckets] - ); - - const logEntryRateAnomalyAnnotations = useMemo( - () => - histogramBuckets - ? histogramBuckets.reduce((annotatedBuckets, bucket) => { - const anomalies = bucket.dataSets.reduce( - (accumulatedAnomalies, dataSet) => [...accumulatedAnomalies, ...dataSet.anomalies], - [] - ); - if (anomalies.length <= 0) { - return annotatedBuckets; - } - return [ - ...annotatedBuckets, - { - coordinates: { - x0: bucket.startTime, - x1: bucket.startTime + bucketDuration, - }, - details: i18n.translate( - 'xpack.infra.logs.analysis.logRateSectionAnomalyCountTooltipLabel', - { - defaultMessage: `{anomalyCount, plural, one {# anomaly} other {# anomalies}}`, - values: { - anomalyCount: anomalies.length, - }, - } - ), - }, - ]; - }, []) - : [], - [histogramBuckets] - ); - const logEntryRateSpecId = getSpecId('averageValues'); - const logEntryRateAnomalyAnnotationsId = getAnnotationId('anomalies'); const tooltipProps = useMemo( () => ({ @@ -149,11 +87,7 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ yAccessors={['value']} splitSeriesAccessors={['group']} stackAccessors={['time']} - data={logEntryRateSeries} - /> - { const title = i18n.translate('xpack.infra.logs.analysis.logRateSectionTitle', { - defaultMessage: 'Log rate', + defaultMessage: 'Logs entries', }); const loadingAriaLabel = i18n.translate( @@ -39,6 +39,26 @@ export const LogRateResults = ({ { defaultMessage: 'Loading log rate results' } ); + const logEntryRateSeries = useMemo( + () => + results && results.histogramBuckets + ? results.histogramBuckets.reduce>( + (buckets, bucket) => { + return [ + ...buckets, + ...bucket.dataSets.map(dataSet => ({ + group: dataSet.dataSetId === '' ? 'unknown' : dataSet.dataSetId, + time: bucket.startTime, + value: dataSet.averageActualLogEntryRate, + })), + ]; + }, + [] + ) + : [], + [results] + ); + return ( <> @@ -71,10 +91,9 @@ export const LogRateResults = ({ /> ) : ( )} From 752456980809f6e8d0934cfeaf9f6a6b426e478d Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 8 Oct 2019 12:04:08 +0100 Subject: [PATCH 04/89] Add legend --- .../pages/logs/analysis/sections/log_rate/bar_chart.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index 8819d5f0d99d3..4d2ad47fa153b 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -57,7 +57,7 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ ); return ( -
+
From 4119a05e562eb051f82220fb6eb6b24364bc6f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 8 Oct 2019 15:32:19 +0200 Subject: [PATCH 05/89] Rename dataSet to partition --- .../log_analysis/results/log_entry_rate.ts | 6 ++-- .../analysis/sections/anomalies/index.tsx | 4 +-- .../logs/analysis/sections/log_rate/index.tsx | 4 +-- .../server/lib/log_analysis/log_analysis.ts | 30 +++++++++---------- .../log_analysis/queries/log_entry_rate.ts | 20 ++++++------- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts index 3af07980910b8..34733522b0303 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts @@ -37,15 +37,15 @@ export const logEntryRateAnomaly = rt.type({ typicalLogEntryRate: rt.number, }); -export const logEntryRateDataSetRT = rt.type({ +export const logEntryRatePartitionRT = rt.type({ analysisBucketCount: rt.number, anomalies: rt.array(logEntryRateAnomaly), averageActualLogEntryRate: rt.number, - dataSetId: rt.string, + partitionId: rt.string, }); export const logEntryRateHistogramBucket = rt.type({ - dataSets: rt.array(logEntryRateDataSetRT), + partitions: rt.array(logEntryRatePartitionRT), startTime: rt.number, }); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 545097c7206fd..4a78075107ec2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -49,7 +49,7 @@ export const AnomaliesResults = ({ ...buckets, { time: bucket.startTime, - value: bucket.dataSets.reduce((accumulatedValue, dataSet) => { + value: bucket.partitions.reduce((accumulatedValue, dataSet) => { return accumulatedValue + dataSet.averageActualLogEntryRate; }, 0), }, @@ -65,7 +65,7 @@ export const AnomaliesResults = ({ () => results && results.histogramBuckets ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const anomalies = bucket.dataSets.reduce( + const anomalies = bucket.partitions.reduce( (accumulatedAnomalies, dataSet) => [...accumulatedAnomalies, ...dataSet.anomalies], [] ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index 31886f7a7b1e9..160aada73d703 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -46,8 +46,8 @@ export const LogRateResults = ({ (buckets, bucket) => { return [ ...buckets, - ...bucket.dataSets.map(dataSet => ({ - group: dataSet.dataSetId === '' ? 'unknown' : dataSet.dataSetId, + ...bucket.partitions.map(dataSet => ({ + group: dataSet.partitionId === '' ? 'unknown' : dataSet.partitionId, time: bucket.startTime, value: dataSet.averageActualLogEntryRate, })), diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts index 31d9c5403e2d2..9944fbfcdfe06 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts @@ -15,7 +15,7 @@ import { logRateModelPlotResponseRT, createLogEntryRateQuery, LogRateModelPlotBucket, - CompositeTimestampDataSetKey, + CompositeTimestampPartitionKey, } from './queries'; const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; @@ -43,7 +43,7 @@ export class InfraLogAnalysis { const logRateJobId = this.getJobIds(request, sourceId).logEntryRate; let mlModelPlotBuckets: LogRateModelPlotBucket[] = []; - let afterLatestBatchKey: CompositeTimestampDataSetKey | undefined; + let afterLatestBatchKey: CompositeTimestampPartitionKey | undefined; while (true) { const mlModelPlotResponse = await this.libs.framework.callWithRequest( @@ -67,7 +67,7 @@ export class InfraLogAnalysis { const { after_key: afterKey, buckets: latestBatchBuckets } = pipe( logRateModelPlotResponseRT.decode(mlModelPlotResponse), - map(response => response.aggregations.timestamp_data_set_buckets), + map(response => response.aggregations.timestamp_partition_buckets), fold(throwErrors(createPlainError), identity) ); @@ -81,7 +81,7 @@ export class InfraLogAnalysis { return mlModelPlotBuckets.reduce< Array<{ - dataSets: Array<{ + partitions: Array<{ analysisBucketCount: number; anomalies: Array<{ actualLogEntryRate: number; @@ -91,15 +91,15 @@ export class InfraLogAnalysis { typicalLogEntryRate: number; }>; averageActualLogEntryRate: number; - dataSetId: string; + partitionId: string; }>; startTime: number; }> - >((histogramBuckets, timestampDataSetBucket) => { + >((histogramBuckets, timestampPartitionBucket) => { const previousHistogramBucket = histogramBuckets[histogramBuckets.length - 1]; - const dataSet = { - analysisBucketCount: timestampDataSetBucket.filter_model_plot.doc_count, - anomalies: timestampDataSetBucket.filter_records.top_hits_record.hits.hits.map( + const partition = { + analysisBucketCount: timestampPartitionBucket.filter_model_plot.doc_count, + anomalies: timestampPartitionBucket.filter_records.top_hits_record.hits.hits.map( ({ _source: record }) => ({ actualLogEntryRate: record.actual[0], anomalyScore: record.record_score, @@ -108,26 +108,26 @@ export class InfraLogAnalysis { typicalLogEntryRate: record.typical[0], }) ), - averageActualLogEntryRate: timestampDataSetBucket.filter_model_plot.average_actual.value, - dataSetId: timestampDataSetBucket.key.data_set, + averageActualLogEntryRate: timestampPartitionBucket.filter_model_plot.average_actual.value || 0, + partitionId: timestampPartitionBucket.key.partition, }; if ( previousHistogramBucket && - previousHistogramBucket.startTime === timestampDataSetBucket.key.timestamp + previousHistogramBucket.startTime === timestampPartitionBucket.key.timestamp ) { return [ ...histogramBuckets.slice(0, -1), { ...previousHistogramBucket, - dataSets: [...previousHistogramBucket.dataSets, dataSet], + partitions: [...previousHistogramBucket.partitions, partition], }, ]; } else { return [ ...histogramBuckets, { - dataSets: [dataSet], - startTime: timestampDataSetBucket.key.timestamp, + partitions: [partition], + startTime: timestampPartitionBucket.key.timestamp, }, ]; } diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts index b10b1fe04db24..ea05105586e09 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts @@ -14,7 +14,7 @@ export const createLogEntryRateQuery = ( endTime: number, bucketDuration: number, size: number, - afterKey?: CompositeTimestampDataSetKey + afterKey?: CompositeTimestampPartitionKey ) => ({ allowNoIndices: true, body: { @@ -45,7 +45,7 @@ export const createLogEntryRateQuery = ( }, }, aggs: { - timestamp_data_set_buckets: { + timestamp_partition_buckets: { composite: { after: afterKey, size, @@ -60,7 +60,7 @@ export const createLogEntryRateQuery = ( }, }, { - data_set: { + partition: { terms: { field: 'partition_field_value', order: 'asc', @@ -124,18 +124,18 @@ const logRateMlRecordRT = rt.type({ }); const metricAggregationRT = rt.type({ - value: rt.number, + value: rt.union([rt.number, rt.null]), }); -const compositeTimestampDataSetKeyRT = rt.type({ - data_set: rt.string, +const compositeTimestampPartitionKeyRT = rt.type({ + partition: rt.string, timestamp: rt.number, }); -export type CompositeTimestampDataSetKey = rt.TypeOf; +export type CompositeTimestampPartitionKey = rt.TypeOf; export const logRateModelPlotBucketRT = rt.type({ - key: compositeTimestampDataSetKeyRT, + key: compositeTimestampPartitionKeyRT, filter_records: rt.type({ doc_count: rt.number, top_hits_record: rt.type({ @@ -158,12 +158,12 @@ export type LogRateModelPlotBucket = rt.TypeOf; export const logRateModelPlotResponseRT = rt.type({ aggregations: rt.type({ - timestamp_data_set_buckets: rt.intersection([ + timestamp_partition_buckets: rt.intersection([ rt.type({ buckets: rt.array(logRateModelPlotBucketRT), }), rt.partial({ - after_key: compositeTimestampDataSetKeyRT, + after_key: compositeTimestampPartitionKeyRT, }), ]), }), From 8d887a87cbee08b6926ff5b925435017fcbe2748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 8 Oct 2019 15:35:04 +0200 Subject: [PATCH 06/89] Add max bucket anomaly score --- .../common/http_api/log_analysis/results/log_entry_rate.ts | 1 + .../plugins/infra/server/lib/log_analysis/log_analysis.ts | 6 +++++- .../infra/server/lib/log_analysis/queries/log_entry_rate.ts | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts index 34733522b0303..24deea0199af2 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts @@ -41,6 +41,7 @@ export const logEntryRatePartitionRT = rt.type({ analysisBucketCount: rt.number, anomalies: rt.array(logEntryRateAnomaly), averageActualLogEntryRate: rt.number, + maximumAnomalyScore: rt.number, partitionId: rt.string, }); diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts index 9944fbfcdfe06..428626557549b 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts @@ -91,6 +91,7 @@ export class InfraLogAnalysis { typicalLogEntryRate: number; }>; averageActualLogEntryRate: number; + maximumAnomalyScore: number; partitionId: string; }>; startTime: number; @@ -108,7 +109,10 @@ export class InfraLogAnalysis { typicalLogEntryRate: record.typical[0], }) ), - averageActualLogEntryRate: timestampPartitionBucket.filter_model_plot.average_actual.value || 0, + averageActualLogEntryRate: + timestampPartitionBucket.filter_model_plot.average_actual.value || 0, + maximumAnomalyScore: + timestampPartitionBucket.filter_records.maximum_record_score.value || 0, partitionId: timestampPartitionBucket.key.partition, }; if ( diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts index ea05105586e09..7930b3d5bc87a 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts @@ -91,6 +91,11 @@ export const createLogEntryRateQuery = ( }, }, aggs: { + maximum_record_score: { + max: { + field: 'record_score', + }, + }, top_hits_record: { top_hits: { _source: Object.keys(logRateMlRecordRT.props), @@ -138,6 +143,7 @@ export const logRateModelPlotBucketRT = rt.type({ key: compositeTimestampPartitionKeyRT, filter_records: rt.type({ doc_count: rt.number, + maximum_record_score: metricAggregationRT, top_hits_record: rt.type({ hits: rt.type({ hits: rt.array( From 6ff0634f1841fbc7d6424cd3bf10c13f95e90b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 18 Sep 2019 21:00:06 +0200 Subject: [PATCH 07/89] Move job and setup status types to common --- .../infra/common/log_analysis/log_analysis.ts | 24 +++++++++++---- .../log_analysis_status_state.tsx | 29 +++++-------------- .../logs/analysis/page_setup_content.tsx | 2 +- .../pages/logs/analysis/setup/steps/index.tsx | 9 +++--- .../analysis/setup/steps/setup_process.tsx | 9 +++--- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts index 870d9f50d44de..d2abf45f5d6f3 100644 --- a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts @@ -12,10 +12,22 @@ export const jobTypeRT = rt.keyof({ export type JobType = rt.TypeOf; -export const jobStatusRT = rt.keyof({ - created: null, - missing: null, - running: null, -}); +// combines and abstracts job and datafeed status +export type JobStatus = + | 'unknown' + | 'missing' + | 'initializing' + | 'stopped' + | 'started' + | 'finished' + | 'failed'; -export type JobStatus = rt.TypeOf; +export type SetupStatus = + | 'initializing' // acquiring job statuses to determine setup status + | 'unknown' // job status could not be acquired (failed request etc) + | 'required' // jobs are missing + | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response + | 'succeeded' // setup succeeded, notifying user + | 'failed' // setup failed, notifying user + | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time + | 'skipped'; // setup hidden because the module is in a correct state already diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index efe5b245517ab..454f9454b636c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -5,30 +5,17 @@ */ import { useReducer } from 'react'; -import { getDatafeedId, getJobId, JobType } from '../../../../common/log_analysis'; + +import { + getDatafeedId, + getJobId, + JobStatus, + JobType, + SetupStatus, +} from '../../../../common/log_analysis'; import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api'; import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; -// combines and abstracts job and datafeed status -type JobStatus = - | 'unknown' - | 'missing' - | 'initializing' - | 'stopped' - | 'started' - | 'finished' - | 'failed'; - -export type SetupStatus = - | 'initializing' // acquiring job statuses to determine setup status - | 'unknown' // job status could not be acquired (failed request etc) - | 'required' // jobs are missing - | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response - | 'succeeded' // setup succeeded, notifying user - | 'failed' // setup failed, notifying user - | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time - | 'skipped'; // setup hidden because the module is in a correct state already - interface StatusReducerState { jobStatus: Record; setupStatus: SetupStatus; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx index 120ae11b69f91..f483c399b3a8a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx @@ -18,9 +18,9 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import euiStyled from '../../../../../../common/eui_styled_components'; +import { SetupStatus } from '../../../../common/log_analysis'; import { useTrackPageview } from '../../../hooks/use_track_metric'; import { AnalysisSetupSteps } from './setup/steps'; -import { SetupStatus } from '../../../containers/logs/log_analysis'; interface AnalysisSetupContentProps { setup: (startTime?: number | undefined, endTime?: number | undefined) => void; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx index 1dde313845ea4..a0eed0c2711a3 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +import { SetupStatus } from '../../../../../../common/log_analysis'; +import { useAnalysisSetupState } from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; import { InitialConfiguration } from './initial_configuration'; import { SetupProcess } from './setup_process'; -import { useAnalysisSetupState } from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; -import { SetupStatus } from '../../../../../containers/logs/log_analysis'; interface AnalysisSetupStepsProps { setup: (startTime?: number | undefined, endTime?: number | undefined) => void; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx index d08d52fd159ae..0e71ba3e8d214 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx @@ -4,18 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { - EuiLoadingSpinner, EuiButton, - EuiSpacer, EuiFlexGroup, EuiFlexItem, + EuiLoadingSpinner, + EuiSpacer, EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { SetupStatus } from '../../../../../../common/log_analysis'; import { CreateMLJobsButton } from '../create_ml_jobs_button'; -import { SetupStatus } from '../../../../../containers/logs/log_analysis'; interface Props { viewResults: () => void; From 5eeefcef34238207b05072a32ac364b706be0891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 18 Sep 2019 21:01:43 +0200 Subject: [PATCH 08/89] Move initial job status fetching into page content --- .../containers/logs/log_analysis/log_analysis_jobs.tsx | 7 ++----- .../infra/public/pages/logs/analysis/page_content.tsx | 6 +++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx index 2c9e16de6a06a..a81927f70093b 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx @@ -5,7 +5,7 @@ */ import createContainer from 'constate-latest'; -import { useEffect, useMemo, useCallback } from 'react'; +import { useMemo, useCallback } from 'react'; import { bucketSpan } from '../../../../common/log_analysis'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callJobsSummaryAPI } from './api/ml_get_jobs_summary_api'; @@ -69,10 +69,6 @@ export const useLogAnalysisJobs = ({ [indexPattern, spaceId, sourceId] ); - useEffect(() => { - fetchJobStatus(); - }, []); - const isLoadingSetupStatus = useMemo(() => fetchJobStatusRequest.state === 'pending', [ fetchJobStatusRequest.state, ]); @@ -96,6 +92,7 @@ export const useLogAnalysisJobs = ({ ); return { + fetchJobStatus, setupMlModuleRequest, jobStatus: statusState.jobStatus, isLoadingSetupStatus, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx index bf3ac99a92bf7..f36eb7cc4b87c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React, { useContext, useEffect } from 'react'; import { LoadingPage } from '../../../components/loading_page'; import { LogAnalysisCapabilities, LogAnalysisJobs } from '../../../containers/logs/log_analysis'; @@ -23,6 +23,10 @@ export const AnalysisPageContent = () => { LogAnalysisJobs.Context ); + useEffect(() => { + fetchJobStatus(); + }, []); + if (!hasLogAnalysisCapabilites) { return ; } else if (setupStatus === 'initializing') { From 43c2edf7ed21fdf0349d75460d13c50db4bb5257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 18 Sep 2019 21:03:15 +0200 Subject: [PATCH 09/89] Poll for job status on results page --- .../logs/log_analysis/log_analysis_status_state.tsx | 2 +- .../pages/logs/analysis/page_results_content.tsx | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index 454f9454b636c..5cd7c2588d5e8 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -92,7 +92,7 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): case 'fetchingJobStatuses': { return { ...state, - setupStatus: 'initializing', + setupStatus: state.setupStatus === 'unknown' ? 'initializing' : state.setupStatus, }; } case 'fetchedJobStatuses': { diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index aaf24c22594e5..29a45c6a60c04 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -17,21 +17,25 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useMemo, useState } from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { + LogAnalysisJobs, StringTimeRange, useLogAnalysisResults, useLogAnalysisResultsUrlState, } from '../../../containers/logs/log_analysis'; +import { useInterval } from '../../../hooks/use_interval'; import { useTrackPageview } from '../../../hooks/use_track_metric'; import { FirstUseCallout } from './first_use'; import { LogRateResults } from './sections/log_rate'; +const JOB_STATUS_POLLING_INTERVAL = 10000; + export const AnalysisResultsContent = ({ sourceId, isFirstUse, @@ -120,6 +124,12 @@ export const AnalysisResultsContent = ({ [setAutoRefresh] ); + const { fetchJobStatus, jobStatus } = useContext(LogAnalysisJobs.Context); + + useInterval(() => { + fetchJobStatus(); + }, JOB_STATUS_POLLING_INTERVAL); + return ( <> {isLoading && !logEntryRate ? ( From 78116295044bb8eb92950f9a8870df9fc528a891 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 8 Oct 2019 19:17:48 +0100 Subject: [PATCH 10/89] Add table / expanded row charts --- .../analysis/sections/anomalies/chart.tsx | 8 +- .../sections/anomalies/expanded_row.tsx | 87 +++++++++++++ .../analysis/sections/anomalies/index.tsx | 51 ++++++-- .../analysis/sections/anomalies/table.tsx | 116 ++++++++++++++++++ .../analysis/sections/log_rate/bar_chart.tsx | 3 - .../logs/analysis/sections/log_rate/index.tsx | 6 +- 6 files changed, 247 insertions(+), 24 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 90c000a706921..05401026debcd 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -63,22 +63,16 @@ export const AnomaliesChart: React.FunctionComponent<{ const chartAnnotationsId = getAnnotationId(`anomalies-${chartId}`); return ( -
+
Number(value).toFixed(0)} /> diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx new file mode 100644 index 0000000000000..ff5d71dd01b49 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -0,0 +1,87 @@ +/* + * 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 React, { useMemo, useState, useCallback } from 'react'; +import { RectAnnotationDatum } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { AnomaliesChart } from './chart'; +import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; + +export const AnomaliesTableExpandedRow: React.FunctionComponent<{ + partitionId: string; + topAnomalyScore: number; + results: GetLogEntryRateSuccessResponsePayload['data']; + setTimeRange: (timeRange: TimeRange) => void; + timeRange: TimeRange; +}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId }) => { + const logEntryRateSeries = useMemo( + () => + results && results.histogramBuckets + ? results.histogramBuckets.reduce>( + (buckets, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return partition.partitionId === partitionId; + }); + if (!partitionResults) { + return buckets; + } + return [ + ...buckets, + { + time: bucket.startTime, + value: partitionResults.averageActualLogEntryRate, + }, + ]; + }, + [] + ) + : [], + [results] + ); + // TODO: Split into various colours based on severity scoring + const anomalyAnnotations = useMemo( + () => + results && results.histogramBuckets + ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return partition.partitionId === partitionId; + }); + if (!partitionResults || !partitionResults.maximumAnomalyScore) { + return annotatedBuckets; + } + return [ + ...annotatedBuckets, + { + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, + }, + details: i18n.translate( + 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', + { + defaultMessage: 'Anomaly score: {maxAnomalyScore}', + values: { + maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), + }, + } + ), + }, + ]; + }, []) + : [], + [results] + ); + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 4a78075107ec2..77610cf89ebee 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -19,6 +19,7 @@ import React, { useMemo } from 'react'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { AnomaliesChart } from './chart'; +import { AnomaliesTable } from './table'; export const AnomaliesResults = ({ isLoading, @@ -40,6 +41,16 @@ export const AnomaliesResults = ({ { defaultMessage: 'Loading anomalies' } ); + const hasAnomalies = useMemo(() => { + return results && results.histogramBuckets + ? results.histogramBuckets.some(bucket => { + return bucket.partitions.some(partition => { + return partition.anomalies.length > 0; + }); + }) + : false; + }, [results]); + const logEntryRateSeries = useMemo( () => results && results.histogramBuckets @@ -49,8 +60,8 @@ export const AnomaliesResults = ({ ...buckets, { time: bucket.startTime, - value: bucket.partitions.reduce((accumulatedValue, dataSet) => { - return accumulatedValue + dataSet.averageActualLogEntryRate; + value: bucket.partitions.reduce((accumulatedValue, partition) => { + return accumulatedValue + partition.averageActualLogEntryRate; }, 0), }, ]; @@ -60,13 +71,16 @@ export const AnomaliesResults = ({ : [], [results] ); - + // TODO: Convert to correct data (anomaly scores), also base on severity score const logEntryRateAnomalyAnnotations = useMemo( () => results && results.histogramBuckets ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { const anomalies = bucket.partitions.reduce( - (accumulatedAnomalies, dataSet) => [...accumulatedAnomalies, ...dataSet.anomalies], + (accumulatedAnomalies, partition) => [ + ...accumulatedAnomalies, + ...partition.anomalies, + ], [] ); if (anomalies.length <= 0) { @@ -125,14 +139,29 @@ export const AnomaliesResults = ({

} /> - ) : ( - + {i18n.translate('xpack.infra.logs.analysis.anomalySectionNoAnomaliesTitle', { + defaultMessage: 'No anomalies were detected.', + })} + + } + titleSize="m" /> + ) : ( + <> + + + + )} ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx new file mode 100644 index 0000000000000..e7524a17a1468 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -0,0 +1,116 @@ +/* + * 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 React, { useMemo, useState, useCallback } from 'react'; +import { EuiBasicTable, EuiButtonIcon } from '@elastic/eui'; +import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; +import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; +import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import { AnomaliesTableExpandedRow } from './expanded_row'; + +export const AnomaliesTable: React.FunctionComponent<{ + results: GetLogEntryRateSuccessResponsePayload['data']; + setTimeRange: (timeRange: TimeRange) => void; + timeRange: TimeRange; +}> = ({ results, timeRange, setTimeRange }) => { + const tableItems = useMemo(() => { + const partitionsTopAnomalyScores = results.histogramBuckets.reduce>( + (topScores, bucket) => { + bucket.partitions.forEach(partition => { + if (partition.maximumAnomalyScore > 0) { + topScores = { + ...topScores, + [partition.partitionId]: + !topScores[partition.partitionId] || + partition.maximumAnomalyScore > topScores[partition.partitionId] + ? partition.maximumAnomalyScore + : topScores[partition.partitionId], + }; + } + }); + return topScores; + }, + {} + ); + + return Object.entries(partitionsTopAnomalyScores).map(([key, value]) => { + return { + id: key, + partition: key, + topAnomalyScore: Number(value).toFixed(3), + }; + }); + }, [results]); + + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState< + Record + >({}); + + const toggleExpandedItems = useCallback( + item => { + if (itemIdToExpandedRowMap[item.id]) { + const newItemIdToExpandedRowMap = { + ...itemIdToExpandedRowMap, + }; + delete newItemIdToExpandedRowMap[item.id]; + setItemIdToExpandedRowMap(newItemIdToExpandedRowMap); + } else { + const newItemIdToExpandedRowMap = { + ...itemIdToExpandedRowMap, + [item.id]: ( + + ), + }; + setItemIdToExpandedRowMap(newItemIdToExpandedRowMap); + } + }, + [results, setTimeRange, timeRange, itemIdToExpandedRowMap, setItemIdToExpandedRowMap] + ); + + const columns = [ + { + field: 'partition', + name: 'Partition', + sortable: true, + truncateText: true, + }, + { + field: 'topAnomalyScore', + name: 'Top anomaly score', + sortable: true, + truncateText: true, + }, + { + align: RIGHT_ALIGNMENT, + width: '40px', + isExpander: true, + render: item => ( + toggleExpandedItems(item)} + aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'} + iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'} + /> + ), + }, + ]; + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index 4d2ad47fa153b..36a281846e9c7 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -61,9 +61,6 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ { return [ ...buckets, - ...bucket.partitions.map(dataSet => ({ - group: dataSet.partitionId === '' ? 'unknown' : dataSet.partitionId, + ...bucket.partitions.map(partition => ({ + group: partition.partitionId === '' ? 'unknown' : partition.partitionId, time: bucket.startTime, - value: dataSet.averageActualLogEntryRate, + value: partition.averageActualLogEntryRate, })), ]; }, From ae059f0775fd6bd632618c2b8492832702e9d3bf Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 8 Oct 2019 19:35:13 +0100 Subject: [PATCH 11/89] Add bucket span text --- .../analysis/sections/log_rate/bar_chart.tsx | 3 - .../logs/analysis/sections/log_rate/index.tsx | 72 ++++++++++++------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index 36a281846e9c7..ba7938802b47d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -67,9 +67,6 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ /> Number(value).toFixed(0)} /> diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index 567c263947319..c0ad8a26db3dc 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -11,6 +11,7 @@ import { EuiLoadingChart, EuiSpacer, EuiTitle, + EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; @@ -64,37 +65,56 @@ export const LogRateResults = ({

{title}

- {isLoading ? ( - - - - - + <> + + + + + + + ) : !results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( - - {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataTitle', { - defaultMessage: 'There is no data to display.', - })} - - } - titleSize="m" - body={ + <> + + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataTitle', { + defaultMessage: 'There is no data to display.', + })} + + } + titleSize="m" + body={ +

+ {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataBody', { + defaultMessage: 'You may want to adjust your time range.', + })} +

+ } + /> + + ) : ( + <> +

- {i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataBody', { - defaultMessage: 'You may want to adjust your time range.', + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanLabel', { + defaultMessage: 'Bucket span: ', + })} + + {i18n.translate('xpack.infra.logs.analysis.logRateSectionBucketSpanValue', { + defaultMessage: '15 minutes', })}

- } - /> - ) : ( - +
+ + )} ); From a3820b727d86971c0a5e4d715a7fb873a153701a Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 8 Oct 2019 19:56:52 +0100 Subject: [PATCH 12/89] Add stat section to expanded rows --- .../sections/anomalies/expanded_row.tsx | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index ff5d71dd01b49..fdc838373ac23 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useState, useCallback } from 'react'; +import React, { useMemo } from 'react'; import { RectAnnotationDatum } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; import { AnomaliesChart } from './chart'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; @@ -76,12 +77,28 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ [results] ); return ( - + + + + + + + + ); }; From 1e8678d8a6b1208166a862e99ba1b0323ff21aac Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 8 Oct 2019 20:14:20 +0100 Subject: [PATCH 13/89] Amend annotations on overall graph --- .../analysis/sections/anomalies/index.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 77610cf89ebee..35dc8120437cf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -71,19 +71,18 @@ export const AnomaliesResults = ({ : [], [results] ); - // TODO: Convert to correct data (anomaly scores), also base on severity score - const logEntryRateAnomalyAnnotations = useMemo( + // TODO: Add grouping / colouring based on severity scoring + const anomalyAnnotations = useMemo( () => results && results.histogramBuckets ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const anomalies = bucket.partitions.reduce( - (accumulatedAnomalies, partition) => [ - ...accumulatedAnomalies, - ...partition.anomalies, - ], - [] + const sumPartitionMaxAnomalyScores = bucket.partitions.reduce( + (scoreSum, partition) => { + return scoreSum + partition.maximumAnomalyScore; + }, + 0 ); - if (anomalies.length <= 0) { + if (sumPartitionMaxAnomalyScores === 0) { return annotatedBuckets; } return [ @@ -94,11 +93,11 @@ export const AnomaliesResults = ({ x1: bucket.startTime + results.bucketDuration, }, details: i18n.translate( - 'xpack.infra.logs.analysis.anomalySectionAnomalyCountTooltipLabel', + 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', { - defaultMessage: `{anomalyCount, plural, one {# anomaly} other {# anomalies}}`, + defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', values: { - anomalyCount: anomalies.length, + sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), }, } ), @@ -157,7 +156,7 @@ export const AnomaliesResults = ({ setTimeRange={setTimeRange} timeRange={timeRange} series={logEntryRateSeries} - annotations={logEntryRateAnomalyAnnotations} + annotations={anomalyAnnotations} /> From 427105d7196cd20c8313560408a1e47de24250e0 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 13:05:46 +0100 Subject: [PATCH 14/89] Add rule to account for EuiFlexItem edge case --- .../pages/logs/analysis/page_results_content.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index f5bcc4196cf9d..b041bbd6430cc 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -11,6 +11,7 @@ import moment from 'moment'; import React, { useCallback, useMemo, useState } from 'react'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; +import euiStyled from '../../../../../../../legacy/common/eui_styled_components'; import { LoadingPage } from '../../../components/loading_page'; import { StringTimeRange, @@ -120,7 +121,7 @@ export const AnalysisResultsContent = ({ /> ) : ( <> - + @@ -161,7 +162,7 @@ export const AnalysisResultsContent = ({ - + )} @@ -181,3 +182,11 @@ const stringToNumericTimeRange = (timeRange: StringTimeRange): TimeRange => ({ }) ).valueOf(), }); + +// This is needed due to the flex-basis: 100% !important; rule that +// kicks in on small screens via media queries breaking when using direction="column" +export const ResultsContentPage = euiStyled(EuiPage)` + .euiFlexGroup--responsive > .euiFlexItem { + flex-basis: auto !important; + } +`; From 123084b5c2132ab69f9e558a22e702031a2c5112 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 13:51:37 +0100 Subject: [PATCH 15/89] Move functions that handle derivations of data to a new file --- .../sections/anomalies/expanded_row.tsx | 57 +------ .../analysis/sections/anomalies/index.tsx | 53 +----- .../analysis/sections/anomalies/table.tsx | 26 +-- .../sections/helpers/data_formatters.tsx | 152 ++++++++++++++++++ .../logs/analysis/sections/log_rate/index.tsx | 18 +-- 5 files changed, 169 insertions(+), 137 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index fdc838373ac23..ef9b109abd810 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -5,12 +5,15 @@ */ import React, { useMemo } from 'react'; -import { RectAnnotationDatum } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; import { AnomaliesChart } from './chart'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; +import { + getLogEntryRateSeriesForPartition, + getAnnotationsForPartition, +} from '../helpers/data_formatters'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ partitionId: string; @@ -22,59 +25,15 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ const logEntryRateSeries = useMemo( () => results && results.histogramBuckets - ? results.histogramBuckets.reduce>( - (buckets, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return partition.partitionId === partitionId; - }); - if (!partitionResults) { - return buckets; - } - return [ - ...buckets, - { - time: bucket.startTime, - value: partitionResults.averageActualLogEntryRate, - }, - ]; - }, - [] - ) + ? getLogEntryRateSeriesForPartition(results, partitionId) : [], - [results] + [results, partitionId] ); // TODO: Split into various colours based on severity scoring const anomalyAnnotations = useMemo( () => - results && results.histogramBuckets - ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return partition.partitionId === partitionId; - }); - if (!partitionResults || !partitionResults.maximumAnomalyScore) { - return annotatedBuckets; - } - return [ - ...annotatedBuckets, - { - coordinates: { - x0: bucket.startTime, - x1: bucket.startTime + results.bucketDuration, - }, - details: i18n.translate( - 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', - { - defaultMessage: 'Anomaly score: {maxAnomalyScore}', - values: { - maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), - }, - } - ), - }, - ]; - }, []) - : [], - [results] + results && results.histogramBuckets ? getAnnotationsForPartition(results, partitionId) : [], + [results, partitionId] ); return ( diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 35dc8120437cf..950f133484a32 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RectAnnotationDatum } from '@elastic/charts'; import { EuiEmptyPrompt, EuiFlexGroup, @@ -20,6 +19,7 @@ import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; +import { getLogEntryRateCombinedSeries, getAnnotationsForAll } from '../helpers/data_formatters'; export const AnomaliesResults = ({ isLoading, @@ -52,59 +52,12 @@ export const AnomaliesResults = ({ }, [results]); const logEntryRateSeries = useMemo( - () => - results && results.histogramBuckets - ? results.histogramBuckets.reduce>( - (buckets, bucket) => { - return [ - ...buckets, - { - time: bucket.startTime, - value: bucket.partitions.reduce((accumulatedValue, partition) => { - return accumulatedValue + partition.averageActualLogEntryRate; - }, 0), - }, - ]; - }, - [] - ) - : [], + () => (results && results.histogramBuckets ? getLogEntryRateCombinedSeries(results) : []), [results] ); // TODO: Add grouping / colouring based on severity scoring const anomalyAnnotations = useMemo( - () => - results && results.histogramBuckets - ? results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const sumPartitionMaxAnomalyScores = bucket.partitions.reduce( - (scoreSum, partition) => { - return scoreSum + partition.maximumAnomalyScore; - }, - 0 - ); - if (sumPartitionMaxAnomalyScores === 0) { - return annotatedBuckets; - } - return [ - ...annotatedBuckets, - { - coordinates: { - x0: bucket.startTime, - x1: bucket.startTime + results.bucketDuration, - }, - details: i18n.translate( - 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', - { - defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', - values: { - sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), - }, - } - ), - }, - ]; - }, []) - : [], + () => (results && results.histogramBuckets ? getAnnotationsForAll(results) : []), [results] ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index e7524a17a1468..f4a53a9389b8a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -10,6 +10,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { AnomaliesTableExpandedRow } from './expanded_row'; +import { getTopAnomalyScoresByPartition } from '../helpers/data_formatters'; export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; @@ -17,29 +18,10 @@ export const AnomaliesTable: React.FunctionComponent<{ timeRange: TimeRange; }> = ({ results, timeRange, setTimeRange }) => { const tableItems = useMemo(() => { - const partitionsTopAnomalyScores = results.histogramBuckets.reduce>( - (topScores, bucket) => { - bucket.partitions.forEach(partition => { - if (partition.maximumAnomalyScore > 0) { - topScores = { - ...topScores, - [partition.partitionId]: - !topScores[partition.partitionId] || - partition.maximumAnomalyScore > topScores[partition.partitionId] - ? partition.maximumAnomalyScore - : topScores[partition.partitionId], - }; - } - }); - return topScores; - }, - {} - ); - - return Object.entries(partitionsTopAnomalyScores).map(([key, value]) => { + return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { - id: key, - partition: key, + id: key || 'unknown', + partition: key || 'unknown', topAnomalyScore: Number(value).toFixed(3), }; }); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx new file mode 100644 index 0000000000000..0d550e07059b6 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -0,0 +1,152 @@ +/* + * 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 { RectAnnotationDatum } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; + +export const getLogEntryRatePartitionedSeries = ( + results: GetLogEntryRateSuccessResponsePayload['data'] +) => { + return results.histogramBuckets.reduce>( + (buckets, bucket) => { + return [ + ...buckets, + ...bucket.partitions.map(partition => ({ + group: partition.partitionId === '' ? 'unknown' : partition.partitionId, + time: bucket.startTime, + value: partition.averageActualLogEntryRate, + })), + ]; + }, + [] + ); +}; + +export const getLogEntryRateCombinedSeries = ( + results: GetLogEntryRateSuccessResponsePayload['data'] +) => { + return results.histogramBuckets.reduce>( + (buckets, bucket) => { + return [ + ...buckets, + { + time: bucket.startTime, + value: bucket.partitions.reduce((accumulatedValue, partition) => { + return accumulatedValue + partition.averageActualLogEntryRate; + }, 0), + }, + ]; + }, + [] + ); +}; + +export const getLogEntryRateSeriesForPartition = ( + results: GetLogEntryRateSuccessResponsePayload['data'], + partitionId: string +) => { + return results.histogramBuckets.reduce>( + (buckets, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return partition.partitionId === partitionId; + }); + if (!partitionResults) { + return buckets; + } + return [ + ...buckets, + { + time: bucket.startTime, + value: partitionResults.averageActualLogEntryRate, + }, + ]; + }, + [] + ); +}; + +export const getTopAnomalyScoresByPartition = ( + results: GetLogEntryRateSuccessResponsePayload['data'] +) => { + return results.histogramBuckets.reduce>((topScores, bucket) => { + bucket.partitions.forEach(partition => { + if (partition.maximumAnomalyScore > 0) { + topScores = { + ...topScores, + [partition.partitionId]: + !topScores[partition.partitionId] || + partition.maximumAnomalyScore > topScores[partition.partitionId] + ? partition.maximumAnomalyScore + : topScores[partition.partitionId], + }; + } + }); + return topScores; + }, {}); +}; + +export const getAnnotationsForPartition = ( + results: GetLogEntryRateSuccessResponsePayload['data'], + partitionId: string +) => { + return results.histogramBuckets.reduce((annotatedBuckets, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return partition.partitionId === partitionId; + }); + if (!partitionResults || !partitionResults.maximumAnomalyScore) { + return annotatedBuckets; + } + return [ + ...annotatedBuckets, + { + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, + }, + details: i18n.translate( + 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', + { + defaultMessage: 'Anomaly score: {maxAnomalyScore}', + values: { + maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), + }, + } + ), + }, + ]; + }, []); +}; + +export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { + return results.histogramBuckets.reduce((annotatedBuckets, bucket) => { + const sumPartitionMaxAnomalyScores = bucket.partitions.reduce((scoreSum, partition) => { + return scoreSum + partition.maximumAnomalyScore; + }, 0); + + if (sumPartitionMaxAnomalyScores === 0) { + return annotatedBuckets; + } + return [ + ...annotatedBuckets, + { + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, + }, + details: i18n.translate( + 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', + { + defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', + values: { + sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), + }, + } + ), + }, + ]; + }, []); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index c0ad8a26db3dc..1c7dda4eb46e5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -19,6 +19,7 @@ import React, { useMemo } from 'react'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { LogEntryRateBarChart } from './bar_chart'; +import { getLogEntryRatePartitionedSeries } from '../helpers/data_formatters'; export const LogRateResults = ({ isLoading, @@ -41,22 +42,7 @@ export const LogRateResults = ({ ); const logEntryRateSeries = useMemo( - () => - results && results.histogramBuckets - ? results.histogramBuckets.reduce>( - (buckets, bucket) => { - return [ - ...buckets, - ...bucket.partitions.map(partition => ({ - group: partition.partitionId === '' ? 'unknown' : partition.partitionId, - time: bucket.startTime, - value: partition.averageActualLogEntryRate, - })), - ]; - }, - [] - ) - : [], + () => (results && results.histogramBuckets ? getLogEntryRatePartitionedSeries(results) : []), [results] ); From 737a25db1dab4a59b9b82e91949a6a9bce888a12 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 13:59:45 +0100 Subject: [PATCH 16/89] Tweak data points fetched --- .../infra/public/pages/logs/analysis/page_results_content.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index b041bbd6430cc..e3c6d93524be5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -47,13 +47,13 @@ export const AnalysisResultsContent = ({ const bucketDuration = useMemo(() => { // This function takes the current time range in ms, // works out the bucket interval we'd need to always - // display 200 data points, and then takes that new + // display 100 data points, and then takes that new // value and works out the nearest multiple of // 900000 (15 minutes) to it, so that we don't end up with // jaggy bucket boundaries between the ML buckets and our // aggregation buckets. const msRange = moment(queryTimeRange.endTime).diff(moment(queryTimeRange.startTime)); - const bucketIntervalInMs = msRange / 200; + const bucketIntervalInMs = msRange / 100; const result = bucketSpan * Math.round(bucketIntervalInMs / bucketSpan); const roundedResult = parseInt(Number(result).toFixed(0), 10); return roundedResult < bucketSpan ? bucketSpan : roundedResult; From ded51175a8508b6ae3169414e8a038aadf7f9fac Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 14:16:40 +0100 Subject: [PATCH 17/89] Style bars in grey for anomalies charts --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 05401026debcd..32842fcefbaca 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -86,12 +86,13 @@ export const AnomaliesChart: React.FunctionComponent<{ xAccessor={'time'} yAccessors={['value']} data={series} + barSeriesStyle={{ rect: { fill: '#D3DAE6' } }} // TODO: Acquire this from "theme" as euiColorLightShade />
From 94fdb76cdd288018f24bf909f3f37206147556d5 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 15:21:09 +0100 Subject: [PATCH 18/89] Add severity scoring to annotations --- .../analysis/sections/anomalies/chart.tsx | 31 +++- .../sections/helpers/data_formatters.tsx | 151 ++++++++++++------ 2 files changed, 130 insertions(+), 52 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 32842fcefbaca..f895e91c9ecd6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -25,13 +25,14 @@ import React, { useCallback, useMemo } from 'react'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting'; +import { MLSeverityScoreCategories } from '../helpers/data_formatters'; export const AnomaliesChart: React.FunctionComponent<{ chartId: string; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; series: Array<{ time: number; value: number }>; - annotations: RectAnnotationDatum[]; + annotations: Record; }> = ({ chartId, series, annotations, setTimeRange, timeRange }) => { const [dateFormat] = useKibanaUiSetting('dateFormat'); const [isDarkMode] = useKibanaUiSetting('theme:darkMode'); @@ -60,7 +61,10 @@ export const AnomaliesChart: React.FunctionComponent<{ }, [setTimeRange] ); - const chartAnnotationsId = getAnnotationId(`anomalies-${chartId}`); + const warningAnnotationsId = getAnnotationId(`anomalies-${chartId}-warning`); + const minorAnnotationsId = getAnnotationId(`anomalies-${chartId}-minor`); + const majorAnnotationsId = getAnnotationId(`anomalies-${chartId}-major`); + const criticalAnnotationsId = getAnnotationId(`anomalies-${chartId}-critical`); return (
@@ -86,9 +90,28 @@ export const AnomaliesChart: React.FunctionComponent<{ xAccessor={'time'} yAccessors={['value']} data={series} - barSeriesStyle={{ rect: { fill: '#D3DAE6' } }} // TODO: Acquire this from "theme" as euiColorLightShade + barSeriesStyle={{ rect: { fill: '#D3DAE6', opacity: 0.7 } }} // TODO: Acquire this from "theme" as euiColorLightShade + /> + + + + - ; +const ML_SEVERITY_SCORES: MLSeverityScores = { + warning: 3, + minor: 25, + major: 50, + critical: 75, +}; + export const getLogEntryRatePartitionedSeries = ( results: GetLogEntryRateSuccessResponsePayload['data'] ) => { @@ -93,60 +102,106 @@ export const getAnnotationsForPartition = ( results: GetLogEntryRateSuccessResponsePayload['data'], partitionId: string ) => { - return results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return partition.partitionId === partitionId; - }); - if (!partitionResults || !partitionResults.maximumAnomalyScore) { - return annotatedBuckets; - } - return [ - ...annotatedBuckets, - { - coordinates: { - x0: bucket.startTime, - x1: bucket.startTime + results.bucketDuration, - }, - details: i18n.translate( - 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', + return results.histogramBuckets.reduce>( + (annotatedBucketsBySeverity, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return partition.partitionId === partitionId; + }); + if ( + !partitionResults || + !partitionResults.maximumAnomalyScore || + partitionResults.maximumAnomalyScore < ML_SEVERITY_SCORES.warning + ) { + return annotatedBucketsBySeverity; + } + const severityCategory = getSeverityCategoryForScore(partitionResults.maximumAnomalyScore); + return { + ...annotatedBucketsBySeverity, + [severityCategory]: [ + ...annotatedBucketsBySeverity[severityCategory], { - defaultMessage: 'Anomaly score: {maxAnomalyScore}', - values: { - maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, }, - } - ), - }, - ]; - }, []); + details: i18n.translate( + 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', + { + defaultMessage: 'Anomaly score: {maxAnomalyScore}', + values: { + maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), + }, + } + ), + }, + ], + }; + }, + { + warning: [], + minor: [], + major: [], + critical: [], + } + ); }; export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { - return results.histogramBuckets.reduce((annotatedBuckets, bucket) => { - const sumPartitionMaxAnomalyScores = bucket.partitions.reduce((scoreSum, partition) => { - return scoreSum + partition.maximumAnomalyScore; - }, 0); - - if (sumPartitionMaxAnomalyScores === 0) { - return annotatedBuckets; - } - return [ - ...annotatedBuckets, - { - coordinates: { - x0: bucket.startTime, - x1: bucket.startTime + results.bucketDuration, + return results.histogramBuckets.reduce>( + (annotatedBucketsBySeverity, bucket) => { + const sumPartitionMaxAnomalyScores = bucket.partitions.reduce( + (scoreSum, partition) => { + return scoreSum + partition.maximumAnomalyScore; }, - details: i18n.translate( - 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', + 0 + ); + + if ( + sumPartitionMaxAnomalyScores === 0 || + sumPartitionMaxAnomalyScores < ML_SEVERITY_SCORES.warning + ) { + return annotatedBucketsBySeverity; + } + const severityCategory = getSeverityCategoryForScore(sumPartitionMaxAnomalyScores); + return { + ...annotatedBucketsBySeverity, + [severityCategory]: [ + ...annotatedBucketsBySeverity[severityCategory], { - defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', - values: { - sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), + coordinates: { + x0: bucket.startTime, + x1: bucket.startTime + results.bucketDuration, }, - } - ), - }, - ]; - }, []); + details: i18n.translate( + 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', + { + defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', + values: { + sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), + }, + } + ), + }, + ], + }; + }, + { + warning: [], + minor: [], + major: [], + critical: [], + } + ); +}; + +const getSeverityCategoryForScore = (score: number) => { + if (score >= ML_SEVERITY_SCORES.warning && score < ML_SEVERITY_SCORES.minor) { + return 'warning'; + } else if (score >= ML_SEVERITY_SCORES.minor && score < ML_SEVERITY_SCORES.major) { + return 'minor'; + } else if (score >= ML_SEVERITY_SCORES.major && score < ML_SEVERITY_SCORES.critical) { + return 'major'; + } else { + return 'critical'; + } }; From 25d6e544e1dac4497bea99045b53fddf6e3f84c8 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 19:09:10 +0100 Subject: [PATCH 19/89] Fix default --- .../logs/analysis/sections/anomalies/expanded_row.tsx | 10 ++++++++-- .../pages/logs/analysis/sections/anomalies/index.tsx | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index ef9b109abd810..2a8c4ef66023e 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -29,10 +29,16 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ : [], [results, partitionId] ); - // TODO: Split into various colours based on severity scoring const anomalyAnnotations = useMemo( () => - results && results.histogramBuckets ? getAnnotationsForPartition(results, partitionId) : [], + results && results.histogramBuckets + ? getAnnotationsForPartition(results, partitionId) + : { + warning: [], + minor: [], + major: [], + critical: [], + }, [results, partitionId] ); return ( diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 950f133484a32..0d6d36a83174d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -55,9 +55,16 @@ export const AnomaliesResults = ({ () => (results && results.histogramBuckets ? getLogEntryRateCombinedSeries(results) : []), [results] ); - // TODO: Add grouping / colouring based on severity scoring const anomalyAnnotations = useMemo( - () => (results && results.histogramBuckets ? getAnnotationsForAll(results) : []), + () => + results && results.histogramBuckets + ? getAnnotationsForAll(results) + : { + warning: [], + minor: [], + major: [], + critical: [], + }, [results] ); From 7be35d2dbda1537414416137adffed0cbed509f5 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 9 Oct 2019 19:11:00 +0100 Subject: [PATCH 20/89] Remove decimal places from anomaly score representations --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- .../public/pages/logs/analysis/sections/anomalies/table.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 2a8c4ef66023e..c2ae4ab874b4c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -54,7 +54,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ Date: Wed, 9 Oct 2019 21:12:45 +0100 Subject: [PATCH 21/89] Show all partitions and overall anomaly score in annotation tooltip for overall chart --- .../analysis/sections/anomalies/chart.tsx | 7 ++- .../analysis/sections/anomalies/index.tsx | 48 +++++++++++++++++++ .../sections/helpers/data_formatters.tsx | 34 ++++++++----- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index f895e91c9ecd6..dc858e2032cb5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -33,7 +33,8 @@ export const AnomaliesChart: React.FunctionComponent<{ timeRange: TimeRange; series: Array<{ time: number; value: number }>; annotations: Record; -}> = ({ chartId, series, annotations, setTimeRange, timeRange }) => { + renderAnnotationTooltip?: (details?: string) => JSX.Element; +}> = ({ chartId, series, annotations, setTimeRange, timeRange, renderAnnotationTooltip }) => { const [dateFormat] = useKibanaUiSetting('dateFormat'); const [isDarkMode] = useKibanaUiSetting('theme:darkMode'); @@ -96,21 +97,25 @@ export const AnomaliesChart: React.FunctionComponent<{ dataValues={annotations.warning} annotationId={warningAnnotationsId} style={{ fill: '#006BB4', opacity: 0.8 }} + renderTooltip={renderAnnotationTooltip} /> @@ -125,3 +126,50 @@ export const AnomaliesResults = ({ ); }; + +interface ParsedAnnotationDetails { + overallAnomalyScore: number; + anomalyScoresByPartition: Record; +} + +const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ details }) => { + const parsedDetails: ParsedAnnotationDetails = JSON.parse(details); + return ( +
+ {`Overall anomaly score: ${parsedDetails.overallAnomalyScore}`} +
    + {Object.entries(parsedDetails.anomalyScoresByPartition).map((entry, index) => { + return ( +
  • + <> + {entry[0]} + {': '} + + {entry[1]} + + +
  • + ); + })} +
+
+ ); +}; + +const renderAnnotationTooltip = (details?: string) => { + // Seems to be necessary to get things typed correctly all the way through to elastic-charts components + if (!details) { + return
; + } + return ; +}; + +// i18n.translate( +// 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', +// { +// defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', +// values: { +// sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), +// }, +// } +// ), diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index 3b183d4e7a58a..2a38c7d2d0aae 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -149,9 +149,24 @@ export const getAnnotationsForPartition = ( export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { return results.histogramBuckets.reduce>( (annotatedBucketsBySeverity, bucket) => { - const sumPartitionMaxAnomalyScores = bucket.partitions.reduce( - (scoreSum, partition) => { - return scoreSum + partition.maximumAnomalyScore; + const maxAnomalyScoresByPartition = bucket.partitions.reduce>( + (bucketMaxAnomalyScoresByPartition, partition) => { + if (partition.maximumAnomalyScore < ML_SEVERITY_SCORES.warning) { + return bucketMaxAnomalyScoresByPartition; + } + return { + ...bucketMaxAnomalyScoresByPartition, + [partition.partitionId ? partition.partitionId : 'unknown']: parseInt( + Number(partition.maximumAnomalyScore).toFixed(0), + 10 + ), + }; + }, + {} + ); + const sumPartitionMaxAnomalyScores = Object.entries(maxAnomalyScoresByPartition).reduce( + (sumMaxAnomalyScore, entry) => { + return (sumMaxAnomalyScore += entry[1]); }, 0 ); @@ -172,15 +187,10 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl x0: bucket.startTime, x1: bucket.startTime + results.bucketDuration, }, - details: i18n.translate( - 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', - { - defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', - values: { - sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), - }, - } - ), + details: JSON.stringify({ + overallAnomalyScore: parseInt(Number(sumPartitionMaxAnomalyScores).toFixed(0), 10), + anomalyScoresByPartition: maxAnomalyScoresByPartition, + }), }, ], }; From 2f7855e200f2c3da88ca898664c1a19463f11eed Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 11:18:29 +0100 Subject: [PATCH 22/89] Handle 'unknown' to workaround lack of '' suuport in tables --- .../pages/logs/analysis/sections/anomalies/table.tsx | 2 +- .../logs/analysis/sections/helpers/data_formatters.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 03b098e5d98a0..2c15fed864932 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -20,7 +20,7 @@ export const AnomaliesTable: React.FunctionComponent<{ const tableItems = useMemo(() => { return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { - id: key || 'unknown', + id: key || 'unknown', // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap partition: key || 'unknown', topAnomalyScore: Number(value).toFixed(0), }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index 2a38c7d2d0aae..e4569a437eb3c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -61,7 +61,10 @@ export const getLogEntryRateSeriesForPartition = ( return results.histogramBuckets.reduce>( (buckets, bucket) => { const partitionResults = bucket.partitions.find(partition => { - return partition.partitionId === partitionId; + return ( + partition.partitionId === partitionId || + (partition.partitionId === '' && partitionId === 'unknown') + ); }); if (!partitionResults) { return buckets; @@ -105,7 +108,10 @@ export const getAnnotationsForPartition = ( return results.histogramBuckets.reduce>( (annotatedBucketsBySeverity, bucket) => { const partitionResults = bucket.partitions.find(partition => { - return partition.partitionId === partitionId; + return ( + partition.partitionId === partitionId || + (partition.partitionId === '' && partitionId === 'unknown') + ); }); if ( !partitionResults || From 78750b3409cbc97d707422c375f17d29c8a40edd Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 13:04:11 +0100 Subject: [PATCH 23/89] Add stats section to overall anomalies section --- .../analysis/sections/anomalies/index.tsx | 80 ++++++++++++------- .../sections/helpers/data_formatters.tsx | 12 +++ 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index cf7c562fc62b6..607f6631d903f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -11,6 +11,7 @@ import { EuiLoadingChart, EuiSpacer, EuiTitle, + EuiStat, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; @@ -19,7 +20,11 @@ import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; -import { getLogEntryRateCombinedSeries, getAnnotationsForAll } from '../helpers/data_formatters'; +import { + getLogEntryRateCombinedSeries, + getAnnotationsForAll, + getTopAnomalyScoreAcrossAllPartitions, +} from '../helpers/data_formatters'; export const AnomaliesResults = ({ isLoading, @@ -68,6 +73,14 @@ export const AnomaliesResults = ({ [results] ); + const topAnomalyScore = useMemo( + () => + results && results.histogramBuckets + ? getTopAnomalyScoreAcrossAllPartitions(results) + : undefined, + [results] + ); + return ( <> @@ -111,14 +124,30 @@ export const AnomaliesResults = ({ /> ) : ( <> - + + + + + + + + @@ -132,22 +161,27 @@ interface ParsedAnnotationDetails { anomalyScoresByPartition: Record; } +const overallAnomalyScoreLabel = i18n.translate( + 'xpack.infra.logs.analysis.overallAnomalyChartOverallScoreLabel', + { + defaultMessage: 'Overall anomaly score', + } +); const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ details }) => { const parsedDetails: ParsedAnnotationDetails = JSON.parse(details); return (
- {`Overall anomaly score: ${parsedDetails.overallAnomalyScore}`} + + {`${overallAnomalyScoreLabel}: `} {parsedDetails.overallAnomalyScore} +
    {Object.entries(parsedDetails.anomalyScoresByPartition).map((entry, index) => { return (
  • - <> - {entry[0]} - {': '} - - {entry[1]} - - + + {`${entry[0]}: `} + {entry[1]} +
  • ); })} @@ -157,19 +191,9 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai }; const renderAnnotationTooltip = (details?: string) => { - // Seems to be necessary to get things typed correctly all the way through to elastic-charts components + // Note: Seems to be necessary to get things typed correctly all the way through to elastic-charts components if (!details) { return
    ; } return ; }; - -// i18n.translate( -// 'xpack.infra.logs.analysis.logRateBucketMaxAnomalyScoreAnnotationLabel', -// { -// defaultMessage: 'Anomaly score: {sumPartitionMaxAnomalyScores}', -// values: { -// sumPartitionMaxAnomalyScores: Number(sumPartitionMaxAnomalyScores).toFixed(0), -// }, -// } -// ), diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index e4569a437eb3c..de518367c32d8 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -210,6 +210,18 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl ); }; +export const getTopAnomalyScoreAcrossAllPartitions = ( + results: GetLogEntryRateSuccessResponsePayload['data'] +) => { + const allMaxScores = results.histogramBuckets.reduce((scores, bucket) => { + const bucketMaxScores = bucket.partitions.reduce((bucketScores, partition) => { + return [...bucketScores, partition.maximumAnomalyScore]; + }, []); + return [...scores, ...bucketMaxScores]; + }, []); + return Math.max(...allMaxScores); +}; + const getSeverityCategoryForScore = (score: number) => { if (score >= ML_SEVERITY_SCORES.warning && score < ML_SEVERITY_SCORES.minor) { return 'warning'; From 7e72fe4225231a78de19a9817e73f68725f308b3 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 13:23:50 +0100 Subject: [PATCH 24/89] Base x-domain off the series so that certain buckets aren't omitted --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 1 - .../public/pages/logs/analysis/sections/log_rate/bar_chart.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index dc858e2032cb5..0ac4bfe4ee5ef 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -121,7 +121,6 @@ export const AnomaliesChart: React.FunctionComponent<{ onBrushEnd={handleBrushEnd} tooltip={tooltipProps} baseTheme={isDarkMode ? DARK_THEME : LIGHT_THEME} - xDomain={{ min: timeRange.startTime, max: timeRange.endTime }} />
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index ba7938802b47d..d77996f10eb2f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -87,7 +87,6 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ onBrushEnd={handleBrushEnd} tooltip={tooltipProps} theme={isDarkMode ? DARK_THEME : LIGHT_THEME} - xDomain={{ min: timeRange.startTime, max: timeRange.endTime }} showLegend legendPosition="right" /> From cb3d5255ea1a05fc54d3bf5942c44a7a48c908a7 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 15:23:18 +0100 Subject: [PATCH 25/89] Tweak colours and DRY up annotation rendering --- .../analysis/sections/anomalies/chart.tsx | 78 ++++++++++++------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 0ac4bfe4ee5ef..ec7e95f236a27 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RectAnnotationDatum } from '@elastic/charts'; +import { RectAnnotationDatum, AnnotationId } from '@elastic/charts'; import { Axis, BarSeries, @@ -62,10 +62,6 @@ export const AnomaliesChart: React.FunctionComponent<{ }, [setTimeRange] ); - const warningAnnotationsId = getAnnotationId(`anomalies-${chartId}-warning`); - const minorAnnotationsId = getAnnotationId(`anomalies-${chartId}-minor`); - const majorAnnotationsId = getAnnotationId(`anomalies-${chartId}-major`); - const criticalAnnotationsId = getAnnotationId(`anomalies-${chartId}-critical`); return (
@@ -91,32 +87,9 @@ export const AnomaliesChart: React.FunctionComponent<{ xAccessor={'time'} yAccessors={['value']} data={series} - barSeriesStyle={{ rect: { fill: '#D3DAE6', opacity: 0.7 } }} // TODO: Acquire this from "theme" as euiColorLightShade - /> - - - - + {renderAnnotations(annotations, chartId, renderAnnotationTooltip)} ); }; + +interface SeverityConfig { + annotationId: AnnotationId; + style: { + fill: string; + opacity: number; + }; +} + +const severityConfigs: Record = { + warning: { + annotationId: getAnnotationId(`anomalies-warning`), + style: { fill: 'rgb(125, 180, 226)', opacity: 0.7 }, + }, + minor: { + annotationId: getAnnotationId(`anomalies-minor`), + style: { fill: 'rgb(255, 221, 0)', opacity: 0.7 }, + }, + major: { + annotationId: getAnnotationId(`anomalies-major`), + style: { fill: 'rgb(229, 113, 0)', opacity: 0.7 }, + }, + critical: { + annotationId: getAnnotationId(`anomalies-critical`), + style: { fill: 'rgb(228, 72, 72)', opacity: 0.7 }, + }, +}; + +const renderAnnotations = ( + annotations: Record, + chartId: string, + renderAnnotationTooltip?: (details?: string) => JSX.Element +) => { + return Object.entries(annotations).map((entry, index) => { + return ( + + ); + }); +}; From 9db183472b290957f073415f373ee05bdbe32f55 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 16:37:28 +0100 Subject: [PATCH 26/89] Add sorting to table --- .../analysis/sections/anomalies/table.tsx | 53 +++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 2c15fed864932..0a50a08e8bc6f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -12,17 +12,23 @@ import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/ import { AnomaliesTableExpandedRow } from './expanded_row'; import { getTopAnomalyScoresByPartition } from '../helpers/data_formatters'; +interface TableItem { + id: string; + partition: string; + topAnomalyScore: number; +} + export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; }> = ({ results, timeRange, setTimeRange }) => { - const tableItems = useMemo(() => { + const tableItems: TableItem[] = useMemo(() => { return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { id: key || 'unknown', // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap partition: key || 'unknown', - topAnomalyScore: Number(value).toFixed(0), + topAnomalyScore: parseInt(Number(value).toFixed(0), 10), }; }); }, [results]); @@ -31,6 +37,43 @@ export const AnomaliesTable: React.FunctionComponent<{ Record >({}); + interface SortingOptions { + sort: { + field: string; + direction: string; + }; + } + + const [sorting, setSorting] = useState({ + sort: { + field: 'topAnomalyScore', + direction: 'desc', + }, + }); + + const handleTableChange = useCallback( + ({ sort = {} }) => { + const { field, direction } = sort; + setSorting({ + sort: { + field, + direction, + }, + }); + }, + [setSorting] + ); + + const sortedTableItems = useMemo(() => { + let sortedItems: TableItem[] = []; + if (sorting.sort.field === 'partition') { + sortedItems = tableItems.sort((a, b) => (a.partition > b.partition ? 1 : -1)); + } else if (sorting.sort.field === 'topAnomalyScore') { + sortedItems = tableItems.sort((a, b) => a.topAnomalyScore - b.topAnomalyScore); + } + return sorting.sort.direction === 'asc' ? sortedItems : sortedItems.reverse(); + }, [tableItems, sorting]); + const toggleExpandedItems = useCallback( item => { if (itemIdToExpandedRowMap[item.id]) { @@ -75,7 +118,7 @@ export const AnomaliesTable: React.FunctionComponent<{ align: RIGHT_ALIGNMENT, width: '40px', isExpander: true, - render: item => ( + render: (item: TableItem) => ( toggleExpandedItems(item)} aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'} @@ -87,12 +130,14 @@ export const AnomaliesTable: React.FunctionComponent<{ return ( ); }; From fd26252542aa30a92d4f0d68aeb2232a8aa64620 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 10 Oct 2019 22:01:27 +0100 Subject: [PATCH 27/89] Add "number of logs" to API results and render in UI stats --- .../log_analysis/results/log_entry_rate.ts | 2 ++ .../sections/anomalies/expanded_row.tsx | 18 ++++++++++++++++++ .../analysis/sections/anomalies/index.tsx | 10 ++++++++++ .../sections/helpers/data_formatters.tsx | 19 +++++++++++++++++++ .../server/lib/log_analysis/log_analysis.ts | 2 ++ .../log_analysis/queries/log_entry_rate.ts | 6 ++++++ .../log_analysis/results/log_entry_rate.ts | 13 +++++++++++++ 7 files changed, 70 insertions(+) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts index 24deea0199af2..f6dc224621b23 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts @@ -42,6 +42,7 @@ export const logEntryRatePartitionRT = rt.type({ anomalies: rt.array(logEntryRateAnomaly), averageActualLogEntryRate: rt.number, maximumAnomalyScore: rt.number, + numberOfLogs: rt.number, partitionId: rt.string, }); @@ -54,6 +55,7 @@ export const getLogEntryRateSuccessReponsePayloadRT = rt.type({ data: rt.type({ bucketDuration: rt.number, histogramBuckets: rt.array(logEntryRateHistogramBucket), + totalNumberOfLogs: rt.number, }), }); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index c2ae4ab874b4c..effaa6227171c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -13,6 +13,7 @@ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { getLogEntryRateSeriesForPartition, getAnnotationsForPartition, + getTotalNumberOfLogsForPartition, } from '../helpers/data_formatters'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ @@ -41,6 +42,13 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ }, [results, partitionId] ); + const totalNumberOfLogs = useMemo( + () => + results && results.histogramBuckets + ? getTotalNumberOfLogsForPartition(results, partitionId) + : undefined, + [results, partitionId] + ); return ( @@ -53,6 +61,16 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ /> + + { + return results.histogramBuckets.reduce((sumPartitionNumberOfLogs, bucket) => { + const partitionResults = bucket.partitions.find(partition => { + return ( + partition.partitionId === partitionId || + (partition.partitionId === '' && partitionId === 'unknown') + ); + }); + if (!partitionResults || !partitionResults.numberOfLogs) { + return sumPartitionNumberOfLogs; + } else { + return (sumPartitionNumberOfLogs += partitionResults.numberOfLogs); + } + }, 0); +}; + export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { return results.histogramBuckets.reduce>( (annotatedBucketsBySeverity, bucket) => { diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts index 428626557549b..5ac22ddf6b793 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts @@ -92,6 +92,7 @@ export class InfraLogAnalysis { }>; averageActualLogEntryRate: number; maximumAnomalyScore: number; + numberOfLogs: number; partitionId: string; }>; startTime: number; @@ -113,6 +114,7 @@ export class InfraLogAnalysis { timestampPartitionBucket.filter_model_plot.average_actual.value || 0, maximumAnomalyScore: timestampPartitionBucket.filter_records.maximum_record_score.value || 0, + numberOfLogs: timestampPartitionBucket.filter_model_plot.sum_actual.value || 0, partitionId: timestampPartitionBucket.key.partition, }; if ( diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts index 7930b3d5bc87a..2dd0880cbf8cb 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts @@ -82,6 +82,11 @@ export const createLogEntryRateQuery = ( field: 'actual', }, }, + sum_actual: { + sum: { + field: 'actual', + }, + }, }, }, filter_records: { @@ -157,6 +162,7 @@ export const logRateModelPlotBucketRT = rt.type({ filter_model_plot: rt.type({ doc_count: rt.number, average_actual: metricAggregationRT, + sum_actual: metricAggregationRT, }), }); diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 3212ecaf65e88..ededd10eb7304 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -14,6 +14,7 @@ import { LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, getLogEntryRateRequestPayloadRT, getLogEntryRateSuccessReponsePayloadRT, + GetLogEntryRateSuccessResponsePayload, } from '../../../../common/http_api/log_analysis'; import { throwErrors } from '../../../../common/runtime_types'; import { NoLogRateResultsIndexError } from '../../../lib/log_analysis'; @@ -52,9 +53,21 @@ export const initLogAnalysisGetLogEntryRateRoute = ({ data: { bucketDuration: payload.data.bucketDuration, histogramBuckets: logEntryRateBuckets, + totalNumberOfLogs: getTotalNumberOfLogs(logEntryRateBuckets), }, }) ); }, }); }; + +const getTotalNumberOfLogs = ( + logEntryRateBuckets: GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets'] +) => { + return logEntryRateBuckets.reduce((sumNumberOfLogs, bucket) => { + const sumPartitions = bucket.partitions.reduce((partitionsTotal, partition) => { + return (partitionsTotal += partition.numberOfLogs); + }, 0); + return (sumNumberOfLogs += sumPartitions); + }, 0); +}; From 2569bc8debb0533dcd811facc2175a959bc9d857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 9 Oct 2019 00:16:30 +0200 Subject: [PATCH 28/89] Track and render out-of-sync job configurations --- .../infra/common/log_analysis/log_analysis.ts | 25 ++- .../logging/log_analysis_job_status/index.ts | 7 + .../job_configuration_outdated_callout.tsx | 35 +++ .../job_definition_outdated_callout.tsx | 35 +++ .../log_analysis_job_problem_indicator.tsx | 28 +++ .../logs/log_analysis/api/ml_api_types.ts | 16 ++ .../api/ml_get_jobs_summary_api.ts | 9 +- .../logs/log_analysis/api/ml_get_module.ts | 42 ++++ .../log_analysis/api/ml_setup_module_api.ts | 45 ++-- .../logs/log_analysis/log_analysis_jobs.tsx | 82 +++++-- .../log_analysis/log_analysis_setup_state.tsx | 25 ++- .../log_analysis_status_state.tsx | 202 ++++++++++++++++-- .../pages/logs/analysis/page_content.tsx | 7 +- .../logs/analysis/page_results_content.tsx | 17 +- .../logs/analysis/page_setup_content.tsx | 16 +- .../setup/recreate_ml_jobs_button.tsx | 29 +++ .../pages/logs/analysis/setup/steps/index.tsx | 27 ++- .../analysis/setup/steps/setup_process.tsx | 9 +- 18 files changed, 576 insertions(+), 80 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts index d2abf45f5d6f3..89e42f69c9daf 100644 --- a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts @@ -26,8 +26,31 @@ export type SetupStatus = | 'initializing' // acquiring job statuses to determine setup status | 'unknown' // job status could not be acquired (failed request etc) | 'required' // jobs are missing + | 'requiredForReconfiguration' // the configurations don't match the source configurations + | 'requiredForUpdate' // the definitions don't match the module definitions | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response | 'succeeded' // setup succeeded, notifying user | 'failed' // setup failed, notifying user | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time - | 'skipped'; // setup hidden because the module is in a correct state already + | 'skipped' // setup hidden because the module is in a correct state already + | 'skippedButReconfigurable' // setup hidden even though the job configurations are outdated + | 'skippedButUpdatable'; // setup hidden even though the job definitions are outdated + +/** + * Maps a job status to the possibility that results have already been produced + * before this state was reached. + */ +export const isJobStatusWithResults = (jobStatus: JobStatus) => + ['started', 'finished', 'stopped', 'failed'].includes(jobStatus); + +export const isHealthyJobStatus = (jobStatus: JobStatus) => + ['started', 'finished'].includes(jobStatus); + +/** + * Maps a setup status to the possibility that results have already been + * produced before this state was reached. + */ +export const isSetupStatusWithResults = (setupStatus: SetupStatus) => + ['skipped', 'hiddenAfterSuccess', 'skippedButReconfigurable', 'skippedButUpdatable'].includes( + setupStatus + ); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts new file mode 100644 index 0000000000000..06229a26afd19 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export * from './log_analysis_job_problem_indicator'; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx new file mode 100644 index 0000000000000..aa6cf080abfaa --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -0,0 +1,35 @@ +/* + * 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 React from 'react'; +import { EuiCallOut, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const JobConfigurationOutdatedCallout: React.FC<{ + onRecreateMlJob: () => void; +}> = ({ onRecreateMlJob }) => ( + + + + + + +); + +const jobConfigurationOutdatedTitle = i18n.translate( + 'xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle', + { + defaultMessage: 'ML job configuration outdated', + } +); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx new file mode 100644 index 0000000000000..4173c9d807ffb --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx @@ -0,0 +1,35 @@ +/* + * 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 { EuiCallOut, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +export const JobDefinitionOutdatedCallout: React.FC<{ + onRecreateMlJob: () => void; +}> = ({ onRecreateMlJob }) => ( + + + + + + +); + +const jobDefinitionOutdatedTitle = i18n.translate( + 'xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle', + { + defaultMessage: 'ML job definition outdated', + } +); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx new file mode 100644 index 0000000000000..5ae4d7ac75819 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx @@ -0,0 +1,28 @@ +/* + * 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 React from 'react'; + +import { JobStatus, SetupStatus } from '../../../../common/log_analysis'; +import { JobConfigurationOutdatedCallout } from './job_configuration_outdated_callout'; +import { JobDefinitionOutdatedCallout } from './job_definition_outdated_callout'; + +export const LogAnalysisJobProblemIndicator: React.FC<{ + jobStatus: JobStatus; + setupStatus: SetupStatus; + onRecreateMlJobForReconfiguration: () => void; + onRecreateMlJobForUpdate: () => void; +}> = ({ jobStatus, setupStatus, onRecreateMlJobForReconfiguration, onRecreateMlJobForUpdate }) => { + if (jobStatus === 'stopped') { + return
restart
; + } else if (setupStatus === 'skippedButUpdatable') { + return ; + } else if (setupStatus === 'skippedButReconfigurable') { + return ; + } + + return null; // no problem to indicate +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts new file mode 100644 index 0000000000000..deb3d528e42c2 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts @@ -0,0 +1,16 @@ +/* + * 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 * as rt from 'io-ts'; + +export const jobCustomSettingsRT = rt.partial({ + job_revision: rt.number, + logs_source_config: rt.partial({ + indexPattern: rt.string, + timestampField: rt.string, + bucketSpan: rt.number, + }), +}); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 0b6e0981c7f9f..477e75fc3b90c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as rt from 'io-ts'; -import { kfetch } from 'ui/kfetch'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + +import { jobCustomSettingsRT } from './ml_api_types'; import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; import { getAllModuleJobIds } from '../../../../../common/log_analysis'; @@ -56,11 +58,14 @@ export const jobSummaryRT = rt.intersection([ datafeedIndices: rt.array(rt.string), datafeedState: datafeedStateRT, fullJob: rt.partial({ + custom_settings: jobCustomSettingsRT, finished_time: rt.number, }), }), ]); +export type JobSummary = rt.TypeOf; + export const fetchJobStatusResponsePayloadRT = rt.array(jobSummaryRT); export type FetchJobStatusResponsePayload = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts new file mode 100644 index 0000000000000..b58677ffa844e --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts @@ -0,0 +1,42 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { jobCustomSettingsRT } from './ml_api_types'; + +export const callGetMlModuleAPI = async (moduleId: string) => { + const response = await kfetch({ + method: 'GET', + pathname: `/api/ml/modules/get_module/${moduleId}`, + }); + + return pipe( + getMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const jobDefinitionRT = rt.type({ + id: rt.string, + config: rt.type({ + custom_settings: jobCustomSettingsRT, + }), +}); + +export type JobDefinition = rt.TypeOf; + +const getMlModuleResponsePayloadRT = rt.type({ + id: rt.string, + jobs: rt.array(jobDefinitionRT), +}); + +export type GetMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts index 722d19d99bd23..92078b23085c3 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts @@ -4,27 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as rt from 'io-ts'; -import { kfetch } from 'ui/kfetch'; - import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; import { getJobIdPrefix } from '../../../../../common/log_analysis'; - -const MODULE_ID = 'logs_ui_analysis'; - -// This is needed due to: https://github.com/elastic/kibana/issues/43671 -const removeSampleDataIndex = (indexPattern: string) => { - const SAMPLE_DATA_INDEX = 'kibana_sample_data_logs*'; - return indexPattern - .split(',') - .filter(index => index !== SAMPLE_DATA_INDEX) - .join(','); -}; +import { jobCustomSettingsRT } from './ml_api_types'; export const callSetupMlModuleAPI = async ( + moduleId: string, start: number | undefined, end: number | undefined, spaceId: string, @@ -35,23 +26,30 @@ export const callSetupMlModuleAPI = async ( ) => { const response = await kfetch({ method: 'POST', - pathname: `/api/ml/modules/setup/${MODULE_ID}`, + pathname: `/api/ml/modules/setup/${moduleId}`, body: JSON.stringify( setupMlModuleRequestPayloadRT.encode({ start, end, - indexPatternName: removeSampleDataIndex(indexPattern), + indexPatternName: indexPattern, prefix: getJobIdPrefix(spaceId, sourceId), startDatafeed: true, jobOverrides: [ { - job_id: 'log-entry-rate', + job_id: 'log-entry-rate' as const, analysis_config: { bucket_span: `${bucketSpan}ms`, }, data_description: { time_field: timeField, }, + custom_settings: { + logs_source_config: { + indexPattern, + timestampField: timeField, + bucketSpan, + }, + }, }, ], datafeedOverrides: [], @@ -70,11 +68,22 @@ const setupMlModuleTimeParamsRT = rt.partial({ end: rt.number, }); +const setupMlModuleLogEntryRateJobOverridesRT = rt.type({ + job_id: rt.literal('log-entry-rate'), + analysis_config: rt.type({ + bucket_span: rt.string, + }), + data_description: rt.type({ + time_field: rt.string, + }), + custom_settings: jobCustomSettingsRT, +}); + const setupMlModuleRequestParamsRT = rt.type({ indexPatternName: rt.string, prefix: rt.string, startDatafeed: rt.boolean, - jobOverrides: rt.array(rt.object), + jobOverrides: rt.array(setupMlModuleLogEntryRateJobOverridesRT), datafeedOverrides: rt.array(rt.object), }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx index a81927f70093b..83d4760259d9b 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx @@ -5,7 +5,9 @@ */ import createContainer from 'constate-latest'; -import { useMemo, useCallback } from 'react'; +import { useMemo, useCallback, useEffect } from 'react'; + +import { callGetMlModuleAPI } from './api/ml_get_module'; import { bucketSpan } from '../../../../common/log_analysis'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callJobsSummaryAPI } from './api/ml_get_jobs_summary_api'; @@ -13,6 +15,9 @@ import { callSetupMlModuleAPI, SetupMlModuleResponsePayload } from './api/ml_set import { useLogAnalysisCleanup } from './log_analysis_cleanup'; import { useStatusState } from './log_analysis_status_state'; +const MODULE_ID = 'logs_ui_analysis'; +const SAMPLE_DATA_INDEX = 'kibana_sample_data_logs*'; + export const useLogAnalysisJobs = ({ indexPattern, sourceId, @@ -24,8 +29,35 @@ export const useLogAnalysisJobs = ({ spaceId: string; timeField: string; }) => { + const filteredIndexPattern = useMemo(() => removeSampleDataIndex(indexPattern), [indexPattern]); const { cleanupMLResources } = useLogAnalysisCleanup({ sourceId, spaceId }); - const [statusState, dispatch] = useStatusState(); + const [statusState, dispatch] = useStatusState({ + bucketSpan, + indexPattern: filteredIndexPattern, + timestampField: timeField, + }); + + const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + dispatch({ type: 'fetchingModuleDefinition' }); + return await callGetMlModuleAPI(MODULE_ID); + }, + onResolve: response => { + dispatch({ + type: 'fetchedModuleDefinition', + spaceId, + sourceId, + moduleDefinition: response, + }); + }, + onReject: () => { + dispatch({ type: 'failedFetchingModuleDefinition' }); + }, + }, + [] + ); const [setupMlModuleRequest, setupMlModule] = useTrackedPromise( { @@ -33,11 +65,12 @@ export const useLogAnalysisJobs = ({ createPromise: async (start, end) => { dispatch({ type: 'startedSetup' }); return await callSetupMlModuleAPI( + MODULE_ID, start, end, spaceId, sourceId, - indexPattern, + filteredIndexPattern, timeField, bucketSpan ); @@ -49,7 +82,7 @@ export const useLogAnalysisJobs = ({ dispatch({ type: 'failedSetup' }); }, }, - [indexPattern, spaceId, sourceId, timeField, bucketSpan] + [filteredIndexPattern, spaceId, sourceId, timeField, bucketSpan] ); const [fetchJobStatusRequest, fetchJobStatus] = useTrackedPromise( @@ -66,18 +99,20 @@ export const useLogAnalysisJobs = ({ dispatch({ type: 'failedFetchingJobStatuses' }); }, }, - [indexPattern, spaceId, sourceId] + [filteredIndexPattern, spaceId, sourceId] ); - const isLoadingSetupStatus = useMemo(() => fetchJobStatusRequest.state === 'pending', [ - fetchJobStatusRequest.state, - ]); + const isLoadingSetupStatus = useMemo( + () => + fetchJobStatusRequest.state === 'pending' || fetchModuleDefinitionRequest.state === 'pending', + [fetchJobStatusRequest.state, fetchModuleDefinitionRequest.state] + ); const viewResults = useCallback(() => { dispatch({ type: 'viewedResults' }); }, []); - const retry = useCallback( + const cleanupAndSetup = useCallback( (start, end) => { dispatch({ type: 'startedSetup' }); cleanupMLResources() @@ -91,17 +126,38 @@ export const useLogAnalysisJobs = ({ [cleanupMLResources, setupMlModule] ); + const viewSetupForReconfiguration = useCallback(() => { + dispatch({ type: 'requestedJobConfigurationUpdate' }); + }, []); + + const viewSetupForUpdate = useCallback(() => { + dispatch({ type: 'requestedJobDefinitionUpdate' }); + }, []); + + useEffect(() => { + fetchModuleDefinition(); + }, [fetchModuleDefinition]); + return { fetchJobStatus, - setupMlModuleRequest, - jobStatus: statusState.jobStatus, isLoadingSetupStatus, + jobStatus: statusState.jobStatus, + cleanupAndSetup, setup: setupMlModule, - retry, + setupMlModuleRequest, setupStatus: statusState.setupStatus, + viewSetupForReconfiguration, + viewSetupForUpdate, viewResults, - fetchJobStatus, }; }; export const LogAnalysisJobs = createContainer(useLogAnalysisJobs); +// +// This is needed due to: https://github.com/elastic/kibana/issues/43671 +const removeSampleDataIndex = (indexPattern: string) => { + return indexPattern + .split(',') + .filter(index => index !== SAMPLE_DATA_INDEX) + .join(','); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx index 91e70bf756559..0508a59c2391a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx @@ -5,26 +5,31 @@ */ import { useState, useCallback } from 'react'; +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; + interface Props { - setupModule: (startTime?: number | undefined, endTime?: number | undefined) => void; - retrySetup: (startTime?: number | undefined, endTime?: number | undefined) => void; + cleanupAndSetupModule: SetupHandler; + setupModule: SetupHandler; } -export const useAnalysisSetupState = ({ setupModule, retrySetup }: Props) => { +export const useAnalysisSetupState = ({ setupModule, cleanupAndSetupModule }: Props) => { const [startTime, setStartTime] = useState(undefined); const [endTime, setEndTime] = useState(undefined); + const setup = useCallback(() => { return setupModule(startTime, endTime); }, [setupModule, startTime, endTime]); - const retry = useCallback(() => { - return retrySetup(startTime, endTime); - }, [retrySetup, startTime, endTime]); + + const cleanupAndSetup = useCallback(() => { + return cleanupAndSetupModule(startTime, endTime); + }, [cleanupAndSetupModule, startTime, endTime]); + return { - setup, - retry, - setStartTime, + cleanupAndSetup, + endTime, setEndTime, + setStartTime, + setup, startTime, - endTime, }; }; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index 5cd7c2588d5e8..bd297f0f5be40 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -9,16 +9,22 @@ import { useReducer } from 'react'; import { getDatafeedId, getJobId, + isHealthyJobStatus, JobStatus, JobType, SetupStatus, + jobTypeRT, } from '../../../../common/log_analysis'; -import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api'; +import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; interface StatusReducerState { + jobDefinitions: JobDefinition[]; jobStatus: Record; + jobSummaries: JobSummary[]; setupStatus: SetupStatus; + sourceConfiguration: JobSourceConfiguration; } type StatusReducerAction = @@ -39,19 +45,39 @@ type StatusReducerAction = payload: FetchJobStatusResponsePayload; } | { type: 'failedFetchingJobStatuses' } + | { type: 'fetchingModuleDefinition' } + | { + type: 'fetchedModuleDefinition'; + spaceId: string; + sourceId: string; + moduleDefinition: GetMlModuleResponsePayload; + } + | { type: 'failedFetchingModuleDefinition' } + | { + type: 'updatedSourceConfiguration'; + spaceId: string; + sourceId: string; + sourceConfiguration: JobSourceConfiguration; + } + | { type: 'requestedJobConfigurationUpdate' } + | { type: 'requestedJobDefinitionUpdate' } | { type: 'viewedResults' }; -const initialState: StatusReducerState = { +const createInitialState = (sourceConfiguration: JobSourceConfiguration): StatusReducerState => ({ + jobDefinitions: [], jobStatus: { 'log-entry-rate': 'unknown', }, + jobSummaries: [], setupStatus: 'initializing', -}; + sourceConfiguration, +}); function statusReducer(state: StatusReducerState, action: StatusReducerAction): StatusReducerState { switch (action.type) { case 'startedSetup': { return { + ...state, jobStatus: { 'log-entry-rate': 'initializing', }, @@ -76,12 +102,14 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): ? 'succeeded' : 'failed'; return { + ...state, jobStatus: nextJobStatus, setupStatus: nextSetupStatus, }; } case 'failedSetup': { return { + ...state, jobStatus: { ...state.jobStatus, 'log-entry-rate': 'failed', @@ -89,6 +117,7 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): setupStatus: 'failed', }; } + case 'fetchingModuleDefinition': case 'fetchingJobStatuses': { return { ...state, @@ -96,23 +125,32 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): }; } case 'fetchedJobStatuses': { - const { payload, spaceId, sourceId } = action; + const { payload: jobSummaries, spaceId, sourceId } = action; + const { jobDefinitions, setupStatus, sourceConfiguration } = state; + const nextJobStatus = { ...state.jobStatus, - 'log-entry-rate': getJobStatus(getJobId(spaceId, sourceId, 'log-entry-rate'))(payload), + 'log-entry-rate': getJobStatus(getJobId(spaceId, sourceId, 'log-entry-rate'))(jobSummaries), }; - const nextSetupStatus = Object.values(nextJobStatus).every(jobState => - ['started', 'finished'].includes(jobState) - ) - ? 'skipped' - : 'required'; + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + nextJobStatus, + jobDefinitions, + jobSummaries + )(setupStatus); + return { + ...state, + jobSummaries, jobStatus: nextJobStatus, setupStatus: nextSetupStatus, }; } case 'failedFetchingJobStatuses': { return { + ...state, setupStatus: 'unknown', jobStatus: { ...state.jobStatus, @@ -120,6 +158,56 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): }, }; } + case 'fetchedModuleDefinition': { + const { spaceId, sourceId, moduleDefinition } = action; + const { jobStatus, jobSummaries, setupStatus, sourceConfiguration } = state; + + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + jobStatus, + moduleDefinition.jobs, + jobSummaries + )(setupStatus); + + return { + ...state, + jobDefinitions: moduleDefinition.jobs, + setupStatus: nextSetupStatus, + }; + } + case 'updatedSourceConfiguration': { + const { spaceId, sourceId, sourceConfiguration } = action; + const { jobDefinitions, jobStatus, jobSummaries, setupStatus } = state; + + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + jobStatus, + jobDefinitions, + jobSummaries + )(setupStatus); + + return { + ...state, + setupStatus: nextSetupStatus, + sourceConfiguration, + }; + } + case 'requestedJobConfigurationUpdate': { + return { + ...state, + setupStatus: 'requiredForReconfiguration', + }; + } + case 'requestedJobDefinitionUpdate': { + return { + ...state, + setupStatus: 'requiredForUpdate', + }; + } case 'viewedResults': { return { ...state, @@ -181,6 +269,96 @@ const getJobStatus = (jobId: string) => (jobSummaries: FetchJobStatusResponsePay } )[0] || 'missing'; -export const useStatusState = () => { - return useReducer(statusReducer, initialState); +const getSetupStatus = ( + spaceId: string, + sourceId: string, + sourceConfiguration: JobSourceConfiguration, + everyJobStatus: Record, + jobDefinitions: JobDefinition[], + jobSummaries: JobSummary[] +) => (previousSetupStatus: SetupStatus) => + Object.entries(everyJobStatus).reduce((setupStatus, [jobType, jobStatus]) => { + if (!jobTypeRT.is(jobType)) { + return setupStatus; + } + + const jobId = getJobId(spaceId, sourceId, jobType); + const jobDefinition = jobDefinitions.filter(({ id }) => id === jobType)[0]; + + if (jobStatus === 'missing') { + return 'required'; + } else if ( + setupStatus === 'required' || + setupStatus === 'requiredForUpdate' || + setupStatus === 'requiredForReconfiguration' + ) { + return setupStatus; + } else if ( + setupStatus === 'skippedButUpdatable' || + (jobDefinition && + !isJobRevisionCurrent(jobId, jobDefinition.config.custom_settings.job_revision || 0)( + jobSummaries + )) + ) { + return 'skippedButUpdatable'; + } else if ( + setupStatus === 'skippedButReconfigurable' || + !isJobConfigurationConsistent(jobId, sourceConfiguration)(jobSummaries) + ) { + return 'skippedButReconfigurable'; + } else if (setupStatus === 'hiddenAfterSuccess') { + return setupStatus; + } else if (setupStatus === 'skipped' || isHealthyJobStatus(jobStatus)) { + return 'skipped'; + } + + return setupStatus; + }, previousSetupStatus); + +const isJobRevisionCurrent = (jobId: string, currentRevision: number) => ( + jobSummaries: FetchJobStatusResponsePayload +): boolean => + jobSummaries + .filter(jobSummary => jobSummary.id === jobId) + .every( + jobSummary => + jobSummary.fullJob && + jobSummary.fullJob.custom_settings && + jobSummary.fullJob.custom_settings.job_revision && + jobSummary.fullJob.custom_settings.job_revision >= currentRevision + ); + +const isJobConfigurationConsistent = ( + jobId: string, + sourceConfiguration: { + bucketSpan: number; + indexPattern: string; + timestampField: string; + } +) => (jobSummaries: FetchJobStatusResponsePayload): boolean => + jobSummaries + .filter(jobSummary => jobSummary.id === jobId) + .every(jobSummary => { + if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { + return false; + } + + const jobConfiguration = jobSummary.fullJob.custom_settings.logs_source_config; + + return ( + jobConfiguration && + jobConfiguration.bucketSpan === sourceConfiguration.bucketSpan && + jobConfiguration.indexPattern === sourceConfiguration.indexPattern && + jobConfiguration.timestampField === sourceConfiguration.timestampField + ); + }); + +export const useStatusState = (sourceConfiguration: JobSourceConfiguration) => { + return useReducer(statusReducer, sourceConfiguration, createInitialState); }; + +interface JobSourceConfiguration { + bucketSpan: number; + indexPattern: string; + timestampField: string; +} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx index f36eb7cc4b87c..2b83007e2ab99 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import React, { useContext, useEffect } from 'react'; +import { isSetupStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { LogAnalysisCapabilities, LogAnalysisJobs } from '../../../containers/logs/log_analysis'; import { Source } from '../../../containers/source'; @@ -19,7 +20,7 @@ export const AnalysisPageContent = () => { const { sourceId, source } = useContext(Source.Context); const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); - const { setup, retry, setupStatus, viewResults, fetchJobStatus } = useContext( + const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus } = useContext( LogAnalysisJobs.Context ); @@ -39,7 +40,7 @@ export const AnalysisPageContent = () => { ); } else if (setupStatus === 'unknown') { return ; - } else if (setupStatus === 'skipped' || setupStatus === 'hiddenAfterSuccess') { + } else if (isSetupStatusWithResults(setupStatus)) { return ( { return ( { fetchJobStatus(); @@ -169,6 +176,12 @@ export const AnalysisResultsContent = ({ setTimeRange={handleChartTimeRangeChange} timeRange={queryTimeRange} /> + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx index f483c399b3a8a..16da61bd1d9c1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx @@ -22,20 +22,22 @@ import { SetupStatus } from '../../../../common/log_analysis'; import { useTrackPageview } from '../../../hooks/use_track_metric'; import { AnalysisSetupSteps } from './setup/steps'; +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; + interface AnalysisSetupContentProps { - setup: (startTime?: number | undefined, endTime?: number | undefined) => void; - retry: (startTime?: number | undefined, endTime?: number | undefined) => void; + cleanupAndSetup: SetupHandler; indexPattern: string; - viewResults: () => void; + setup: SetupHandler; setupStatus: SetupStatus; + viewResults: () => void; } export const AnalysisSetupContent: React.FunctionComponent = ({ - setup, + cleanupAndSetup, indexPattern, - viewResults, - retry, + setup, setupStatus, + viewResults, }) => { useTrackPageview({ app: 'infra_logs', path: 'analysis_setup' }); useTrackPageview({ app: 'infra_logs', path: 'analysis_setup', delay: 15000 }); @@ -70,7 +72,7 @@ export const AnalysisSetupContent: React.FunctionComponent void; +}> = ({ onClick }) => { + return ( + <> + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx index a0eed0c2711a3..3343121f5b6cd 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx @@ -13,24 +13,33 @@ import { useAnalysisSetupState } from '../../../../../containers/logs/log_analys import { InitialConfiguration } from './initial_configuration'; import { SetupProcess } from './setup_process'; +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; + interface AnalysisSetupStepsProps { - setup: (startTime?: number | undefined, endTime?: number | undefined) => void; - retry: (startTime?: number | undefined, endTime?: number | undefined) => void; - viewResults: () => void; + cleanupAndSetup: SetupHandler; indexPattern: string; + setup: SetupHandler; setupStatus: SetupStatus; + viewResults: () => void; } export const AnalysisSetupSteps: React.FunctionComponent = ({ - setup: setupModule, - retry: retrySetup, - viewResults, + cleanupAndSetup: cleanupAndSetupModule, indexPattern, + setup: setupModule, setupStatus, + viewResults, }: AnalysisSetupStepsProps) => { - const { setup, retry, setStartTime, setEndTime, startTime, endTime } = useAnalysisSetupState({ + const { + setup, + cleanupAndSetup, + setStartTime, + setEndTime, + startTime, + endTime, + } = useAnalysisSetupState({ setupModule, - retrySetup, + cleanupAndSetupModule, }); const steps = [ @@ -56,7 +65,7 @@ export const AnalysisSetupSteps: React.FunctionComponent ), diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx index 0e71ba3e8d214..1decf3d34848a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx @@ -17,11 +17,12 @@ import React from 'react'; import { SetupStatus } from '../../../../../../common/log_analysis'; import { CreateMLJobsButton } from '../create_ml_jobs_button'; +import { RecreateMLJobsButton } from '../recreate_ml_jobs_button'; interface Props { viewResults: () => void; setup: () => void; - retry: () => void; + cleanupAndSetup: () => void; indexPattern: string; setupStatus: SetupStatus; } @@ -29,7 +30,7 @@ interface Props { export const SetupProcess: React.FunctionComponent = ({ viewResults, setup, - retry, + cleanupAndSetup, indexPattern, setupStatus, }: Props) => { @@ -58,7 +59,7 @@ export const SetupProcess: React.FunctionComponent = ({ }} /> - + = ({ /> + ) : setupStatus === 'requiredForUpdate' || setupStatus === 'requiredForReconfiguration' ? ( + ) : ( )} From 1813c80156e5b4b95dd1ca9119e2c3c8dcdd0bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 11 Oct 2019 19:37:27 +0200 Subject: [PATCH 29/89] Adjust translation labels --- .../pages/logs/analysis/setup/recreate_ml_jobs_button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx index ecbecb44f2afb..b55f8f4654256 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx @@ -14,13 +14,13 @@ export const RecreateMLJobsButton: React.FunctionComponent<{ return ( <> From ced21c8cfd93616e180cdf03b7a13c22b4d949fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 11 Oct 2019 19:38:23 +0200 Subject: [PATCH 30/89] Add stopped state callout --- .../job_stopped_callout.tsx | 24 +++++++++++++++++++ .../log_analysis_job_problem_indicator.tsx | 3 ++- .../log_analysis_status_state.tsx | 6 ++--- 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx new file mode 100644 index 0000000000000..7f9031bbbc5e9 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx @@ -0,0 +1,24 @@ +/* + * 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 React from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const JobStoppedCallout: React.FC = () => ( + + + +); + +const jobStoppedTitle = i18n.translate('xpack.infra.logs.analysis.jobStoppedCalloutTitle', { + defaultMessage: 'ML job stopped', +}); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx index 5ae4d7ac75819..018c5f5e0570d 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { JobStatus, SetupStatus } from '../../../../common/log_analysis'; import { JobConfigurationOutdatedCallout } from './job_configuration_outdated_callout'; import { JobDefinitionOutdatedCallout } from './job_definition_outdated_callout'; +import { JobStoppedCallout } from './job_stopped_callout'; export const LogAnalysisJobProblemIndicator: React.FC<{ jobStatus: JobStatus; @@ -17,7 +18,7 @@ export const LogAnalysisJobProblemIndicator: React.FC<{ onRecreateMlJobForUpdate: () => void; }> = ({ jobStatus, setupStatus, onRecreateMlJobForReconfiguration, onRecreateMlJobForUpdate }) => { if (jobStatus === 'stopped') { - return
restart
; + return ; } else if (setupStatus === 'skippedButUpdatable') { return ; } else if (setupStatus === 'skippedButReconfigurable') { diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index bd297f0f5be40..378eaf5a7196d 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -9,11 +9,11 @@ import { useReducer } from 'react'; import { getDatafeedId, getJobId, - isHealthyJobStatus, + isJobStatusWithResults, JobStatus, JobType, - SetupStatus, jobTypeRT, + SetupStatus, } from '../../../../common/log_analysis'; import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; @@ -308,7 +308,7 @@ const getSetupStatus = ( return 'skippedButReconfigurable'; } else if (setupStatus === 'hiddenAfterSuccess') { return setupStatus; - } else if (setupStatus === 'skipped' || isHealthyJobStatus(jobStatus)) { + } else if (setupStatus === 'skipped' || isJobStatusWithResults(jobStatus)) { return 'skipped'; } From 992a7c56520c5c0073b43af4863a789f25295d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 11 Oct 2019 19:38:39 +0200 Subject: [PATCH 31/89] Add more callout icons --- .../job_configuration_outdated_callout.tsx | 2 +- .../log_analysis_job_status/job_definition_outdated_callout.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx index aa6cf080abfaa..4842d1aedc96f 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -12,7 +12,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; export const JobConfigurationOutdatedCallout: React.FC<{ onRecreateMlJob: () => void; }> = ({ onRecreateMlJob }) => ( - + void; }> = ({ onRecreateMlJob }) => ( - + Date: Fri, 11 Oct 2019 21:53:12 +0200 Subject: [PATCH 32/89] Fix api integration tests --- x-pack/test/api_integration/apis/infra/log_analysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/infra/log_analysis.ts b/x-pack/test/api_integration/apis/infra/log_analysis.ts index fe7d55649d1d6..2e57678d1d4c2 100644 --- a/x-pack/test/api_integration/apis/infra/log_analysis.ts +++ b/x-pack/test/api_integration/apis/infra/log_analysis.ts @@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext) => { expect(logEntryRateBuckets.data.histogramBuckets).to.not.be.empty(); expect( logEntryRateBuckets.data.histogramBuckets.some(bucket => { - return bucket.dataSets.some(dataSet => dataSet.anomalies.length > 0); + return bucket.partitions.some(partition => partition.anomalies.length > 0); }) ).to.be(true); }); From 3e181b020031fcb8f288a4eddd0fbb58af1eaae0 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 14 Oct 2019 11:04:47 +0100 Subject: [PATCH 33/89] Use "pretty" numbers for "Number of logs" stats --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 3 ++- .../public/pages/logs/analysis/sections/anomalies/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index effaa6227171c..185ec074005bb 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -6,6 +6,7 @@ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; +import numeral from '@elastic/numeral'; import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; import { AnomaliesChart } from './chart'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; @@ -62,7 +63,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{
Date: Mon, 14 Oct 2019 13:30:38 +0200 Subject: [PATCH 34/89] Improve status message wording --- .../job_configuration_outdated_callout.tsx | 2 +- .../log_analysis_job_status/job_definition_outdated_callout.tsx | 2 +- .../logging/log_analysis_job_status/job_stopped_callout.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx index 4842d1aedc96f..817abaec78cb7 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -15,7 +15,7 @@ export const JobConfigurationOutdatedCallout: React.FC<{ diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx index f5dc506e4886c..54a599c9cb704 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx @@ -15,7 +15,7 @@ export const JobDefinitionOutdatedCallout: React.FC<{ diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx index 7f9031bbbc5e9..33f0a5b1399a1 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx @@ -13,7 +13,7 @@ export const JobStoppedCallout: React.FC = () => ( From c92615d7507c10ae3cc9b575ffd5b2da16cdc25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 14 Oct 2019 13:32:47 +0200 Subject: [PATCH 35/89] Change recreate job button color back to default --- .../pages/logs/analysis/setup/recreate_ml_jobs_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx index b55f8f4654256..0232c1167f194 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx @@ -18,7 +18,7 @@ export const RecreateMLJobsButton: React.FunctionComponent<{ defaultMessage="This removes previously detected anomalies." tagName="p" /> - + Date: Mon, 14 Oct 2019 12:42:19 +0100 Subject: [PATCH 36/89] Add toolbar text --- .../logs/analysis/page_results_content.tsx | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index e3c6d93524be5..b8ca893e566e2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -5,8 +5,18 @@ */ import datemath from '@elastic/datemath'; -import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel, EuiSuperDatePicker } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPanel, + EuiSuperDatePicker, + EuiBadge, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import numeral from '@elastic/numeral'; +import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import React, { useCallback, useMemo, useState } from 'react'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; @@ -125,7 +135,36 @@ export const AnalysisResultsContent = ({ - + + + {!isLoading && logEntryRate ? ( + + + {numeral(logEntryRate.totalNumberOfLogs).format('0.00a')} + + ), + startTime: ( + + {moment(queryTimeRange.startTime).format('MMMM D, YYYY h:mm A')} + + ), + endTime: ( + + {moment(queryTimeRange.endTime).format('MMMM D, YYYY h:mm A')} + + ), + }} + /> + + ) : null} + Date: Mon, 14 Oct 2019 12:56:53 +0100 Subject: [PATCH 37/89] Format all y axis values to 3 digits --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index ec7e95f236a27..44a3ab25ae58c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -19,6 +19,7 @@ import { getAnnotationId, RectAnnotation, } from '@elastic/charts'; +import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React, { useCallback, useMemo } from 'react'; @@ -75,7 +76,7 @@ export const AnomaliesChart: React.FunctionComponent<{ Number(value).toFixed(0)} + tickFormat={value => numeral(value.toPrecision(3)).format('0[.][00]a')} // https://github.com/adamwdraper/Numeral-js/issues/194 /> Date: Mon, 14 Oct 2019 13:23:20 +0100 Subject: [PATCH 38/89] Remove "Overall anomaly score" and change all wording / calculations to "Max anomaly scores" --- .../sections/anomalies/expanded_row.tsx | 2 +- .../logs/analysis/sections/anomalies/index.tsx | 11 ++++------- .../logs/analysis/sections/anomalies/table.tsx | 2 +- .../sections/helpers/data_formatters.tsx | 18 +++++------------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 185ec074005bb..33ae0cdafbb2c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -77,7 +77,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ description={i18n.translate( 'xpack.infra.logs.analysis.anomaliesExpandedRowTopAnomalyScoreDescription', { - defaultMessage: 'Top anomaly score', + defaultMessage: 'Max anomaly score', } )} reverse diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index a0dfeeb48cbee..fa888dde52e3c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -152,7 +152,7 @@ export const AnomaliesResults = ({ description={i18n.translate( 'xpack.infra.logs.analysis.overallAnomaliesTopAnomalyScoreDescription', { - defaultMessage: 'Top anomaly score', + defaultMessage: 'Max anomaly score', } )} reverse @@ -168,23 +168,20 @@ export const AnomaliesResults = ({ }; interface ParsedAnnotationDetails { - overallAnomalyScore: number; anomalyScoresByPartition: Record; } const overallAnomalyScoreLabel = i18n.translate( - 'xpack.infra.logs.analysis.overallAnomalyChartOverallScoreLabel', + 'xpack.infra.logs.analysis.overallAnomalyChartMaxScoresLabel', { - defaultMessage: 'Overall anomaly score', + defaultMessage: 'Max anomaly scores:', } ); const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ details }) => { const parsedDetails: ParsedAnnotationDetails = JSON.parse(details); return (
- - {`${overallAnomalyScoreLabel}: `} {parsedDetails.overallAnomalyScore} - + {overallAnomalyScoreLabel}
    {Object.entries(parsedDetails.anomalyScoresByPartition).map((entry, index) => { return ( diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 0a50a08e8bc6f..5c015be7748f5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -110,7 +110,7 @@ export const AnomaliesTable: React.FunctionComponent<{ }, { field: 'topAnomalyScore', - name: 'Top anomaly score', + name: 'Max anomaly score', sortable: true, truncateText: true, }, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index ffc3c45b3dea9..fcba6a883ec8d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -133,7 +133,7 @@ export const getAnnotationsForPartition = ( details: i18n.translate( 'xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel', { - defaultMessage: 'Anomaly score: {maxAnomalyScore}', + defaultMessage: 'Max anomaly score: {maxAnomalyScore}', values: { maxAnomalyScore: Number(partitionResults.maximumAnomalyScore).toFixed(0), }, @@ -189,20 +189,13 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl }, {} ); - const sumPartitionMaxAnomalyScores = Object.entries(maxAnomalyScoresByPartition).reduce( - (sumMaxAnomalyScore, entry) => { - return (sumMaxAnomalyScore += entry[1]); - }, - 0 - ); - if ( - sumPartitionMaxAnomalyScores === 0 || - sumPartitionMaxAnomalyScores < ML_SEVERITY_SCORES.warning - ) { + if (Object.keys(maxAnomalyScoresByPartition).length === 0) { return annotatedBucketsBySeverity; } - const severityCategory = getSeverityCategoryForScore(sumPartitionMaxAnomalyScores); + const severityCategory = getSeverityCategoryForScore( + Math.max(...Object.values(maxAnomalyScoresByPartition)) + ); return { ...annotatedBucketsBySeverity, [severityCategory]: [ @@ -213,7 +206,6 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl x1: bucket.startTime + results.bucketDuration, }, details: JSON.stringify({ - overallAnomalyScore: parseInt(Number(sumPartitionMaxAnomalyScores).toFixed(0), 10), anomalyScoresByPartition: maxAnomalyScoresByPartition, }), }, From 3031b6919cd9c438b414991a370644142adecb3f Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 14 Oct 2019 14:12:26 +0100 Subject: [PATCH 39/89] Sort anomaly maximum scores for the overall chart tooltip --- .../analysis/sections/anomalies/index.tsx | 26 +++++++----- .../sections/helpers/data_formatters.tsx | 40 ++++++++++--------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index fa888dde52e3c..9831832069e6a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -168,7 +168,7 @@ export const AnomaliesResults = ({ }; interface ParsedAnnotationDetails { - anomalyScoresByPartition: Record; + anomalyScoresByPartition: Array<{ partitionId: string; maximumAnomalyScore: number }>; } const overallAnomalyScoreLabel = i18n.translate( @@ -183,16 +183,20 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai
    {overallAnomalyScoreLabel}
      - {Object.entries(parsedDetails.anomalyScoresByPartition).map((entry, index) => { - return ( -
    • - - {`${entry[0]}: `} - {entry[1]} - -
    • - ); - })} + {parsedDetails.anomalyScoresByPartition.map( + ({ partitionId, maximumAnomalyScore }, index) => { + return ( +
    • + + {`${partitionId}: `} + {maximumAnomalyScore} + +
    • + ); + } + )}
    ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index fcba6a883ec8d..a20d240e7752a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -174,27 +174,31 @@ export const getTotalNumberOfLogsForPartition = ( export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { return results.histogramBuckets.reduce>( (annotatedBucketsBySeverity, bucket) => { - const maxAnomalyScoresByPartition = bucket.partitions.reduce>( - (bucketMaxAnomalyScoresByPartition, partition) => { - if (partition.maximumAnomalyScore < ML_SEVERITY_SCORES.warning) { - return bucketMaxAnomalyScoresByPartition; - } - return { - ...bucketMaxAnomalyScoresByPartition, - [partition.partitionId ? partition.partitionId : 'unknown']: parseInt( - Number(partition.maximumAnomalyScore).toFixed(0), - 10 - ), - }; - }, - {} - ); + const maxAnomalyScoresByPartition = bucket.partitions.reduce< + Array<{ partitionId: string; maximumAnomalyScore: number }> + >((bucketMaxAnomalyScoresByPartition, partition) => { + if (partition.maximumAnomalyScore < ML_SEVERITY_SCORES.warning) { + return bucketMaxAnomalyScoresByPartition; + } + return [ + ...bucketMaxAnomalyScoresByPartition, + { + partitionId: partition.partitionId ? partition.partitionId : 'unknown', + maximumAnomalyScore: parseInt(Number(partition.maximumAnomalyScore).toFixed(0), 10), + }, + ]; + }, []); - if (Object.keys(maxAnomalyScoresByPartition).length === 0) { + if (maxAnomalyScoresByPartition.length === 0) { return annotatedBucketsBySeverity; } const severityCategory = getSeverityCategoryForScore( - Math.max(...Object.values(maxAnomalyScoresByPartition)) + Math.max( + ...maxAnomalyScoresByPartition.map(partitionScore => partitionScore.maximumAnomalyScore) + ) + ); + const sortedMaxAnomalyScoresByPartition = maxAnomalyScoresByPartition.sort( + (a, b) => b.maximumAnomalyScore - a.maximumAnomalyScore ); return { ...annotatedBucketsBySeverity, @@ -206,7 +210,7 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl x1: bucket.startTime + results.bucketDuration, }, details: JSON.stringify({ - anomalyScoresByPartition: maxAnomalyScoresByPartition, + anomalyScoresByPartition: sortedMaxAnomalyScoresByPartition, }), }, ], From fbe41affec255c5a6ccedc7489dd5d86763b54dd Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 14 Oct 2019 14:32:33 +0100 Subject: [PATCH 40/89] Remove unused translations --- .../public/pages/logs/analysis/page_results_content.tsx | 6 ++---- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index b8ca893e566e2..224aa174f48c2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -140,10 +140,8 @@ export const AnalysisResultsContent = ({ {!isLoading && logEntryRate ? ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0f7fe7c0eefc0..617d508d7d6ac 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5242,8 +5242,6 @@ "xpack.infra.logs.analysis.logRateSectionNoDataBody": "時間範囲を調整する必要があるかもしれません。", "xpack.infra.logs.analysis.logRateSectionNoDataTitle": "表示するデータがありません。", "xpack.infra.logs.analysis.logRateSectionTitle": "ログレート", - "xpack.infra.logs.analysis.logRateSectionXaxisTitle": "時間", - "xpack.infra.logs.analysis.logRateSectionYaxisTitle": "15 分ごとのログエントリー", "xpack.infra.logs.analysisPage.loadingMessage": "分析ジョブのステータスを確認中…", "xpack.infra.logs.analysisPage.unavailable.mlAppButton": "機械学習を開く", "xpack.infra.logs.analysisPage.unavailable.mlAppLink": "機械学習アプリ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b485eef915d57..c8dc2a34c6f0d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5244,8 +5244,6 @@ "xpack.infra.logs.analysis.logRateSectionNoDataBody": "您可能想调整时间范围。", "xpack.infra.logs.analysis.logRateSectionNoDataTitle": "没有可显示的数据。", "xpack.infra.logs.analysis.logRateSectionTitle": "日志速率", - "xpack.infra.logs.analysis.logRateSectionXaxisTitle": "时间", - "xpack.infra.logs.analysis.logRateSectionYaxisTitle": "每 15 分钟日志条目数", "xpack.infra.logs.analysisPage.loadingMessage": "正在检查分析作业的状态......", "xpack.infra.logs.analysisPage.unavailable.mlAppButton": "打开 Machine Learning", "xpack.infra.logs.analysisPage.unavailable.mlAppLink": "Machine Learning 应用", From 5f851465c462b3478fbe992425ced02e6143f0fc Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 14 Oct 2019 15:13:15 +0100 Subject: [PATCH 41/89] Use white text with badge in toolbar --- .../infra/public/pages/logs/analysis/page_results_content.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 224aa174f48c2..f87e8d48927df 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -145,7 +145,9 @@ export const AnalysisResultsContent = ({ values={{ numberOfLogs: ( - {numeral(logEntryRate.totalNumberOfLogs).format('0.00a')} + + {numeral(logEntryRate.totalNumberOfLogs).format('0.00a')} + ), startTime: ( From f3291273b5cd54ea6b1a455123135749baa27947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 15 Oct 2019 12:10:02 +0200 Subject: [PATCH 42/89] Factor out a job recreation callout --- .../job_configuration_outdated_callout.tsx | 16 ++++--------- .../job_definition_outdated_callout.tsx | 14 ++++------- .../recreate_job_callout.tsx | 24 +++++++++++++++++++ 3 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx index 817abaec78cb7..13b7d1927f676 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -4,27 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { RecreateJobCallout } from './recreate_job_callout'; export const JobConfigurationOutdatedCallout: React.FC<{ onRecreateMlJob: () => void; }> = ({ onRecreateMlJob }) => ( - + - - - - + ); const jobConfigurationOutdatedTitle = i18n.translate( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx index 54a599c9cb704..5072fb09cdceb 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx @@ -4,27 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiCallOut, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; +import { RecreateJobCallout } from './recreate_job_callout'; + export const JobDefinitionOutdatedCallout: React.FC<{ onRecreateMlJob: () => void; }> = ({ onRecreateMlJob }) => ( - + - - - - + ); const jobDefinitionOutdatedTitle = i18n.translate( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx new file mode 100644 index 0000000000000..b95054bbd6a9b --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx @@ -0,0 +1,24 @@ +/* + * 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 React from 'react'; +import { EuiCallOut, EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const RecreateJobCallout: React.FC<{ + onRecreateMlJob: () => void; + title?: React.ReactNode; +}> = ({ children, onRecreateMlJob, title }) => ( + +

    {children}

    + + + +
    +); From 6e7f872c80f4b13ea58267517e5094fe82a28686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 15 Oct 2019 12:10:45 +0200 Subject: [PATCH 43/89] Replace `filter()[0]` with `find()` call --- .../containers/logs/log_analysis/log_analysis_status_state.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index 378eaf5a7196d..02606c223d86d 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -283,7 +283,7 @@ const getSetupStatus = ( } const jobId = getJobId(spaceId, sourceId, jobType); - const jobDefinition = jobDefinitions.filter(({ id }) => id === jobType)[0]; + const jobDefinition = jobDefinitions.find(({ id }) => id === jobType); if (jobStatus === 'missing') { return 'required'; From 40dbb590ce2fb605ce864f0d9ee9ad887366a68f Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 11:19:15 +0100 Subject: [PATCH 44/89] Amend key --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 44a3ab25ae58c..f3429089734f5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -136,7 +136,7 @@ const renderAnnotations = ( return Object.entries(annotations).map((entry, index) => { return ( Date: Tue, 15 Oct 2019 11:41:39 +0100 Subject: [PATCH 45/89] Use Math.round and introduce a formatAnomalyScore helper function --- .../logs/analysis/sections/anomalies/expanded_row.tsx | 3 ++- .../pages/logs/analysis/sections/anomalies/index.tsx | 3 ++- .../pages/logs/analysis/sections/anomalies/table.tsx | 4 ++-- .../logs/analysis/sections/helpers/data_formatters.tsx | 8 ++++++-- .../pages/logs/analysis/sections/log_rate/bar_chart.tsx | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 33ae0cdafbb2c..9a2b6309dbe32 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -15,6 +15,7 @@ import { getLogEntryRateSeriesForPartition, getAnnotationsForPartition, getTotalNumberOfLogsForPartition, + formatAnomalyScore, } from '../helpers/data_formatters'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ @@ -73,7 +74,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ reverse /> { return 'critical'; } }; + +export const formatAnomalyScore = (score: number) => { + return Math.round(score); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index d77996f10eb2f..81afd93035e67 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -22,6 +22,7 @@ import React, { useCallback, useMemo } from 'react'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting'; +import { formatAnomalyScore } from '../helpers/data_formatters'; export const LogEntryRateBarChart: React.FunctionComponent<{ setTimeRange: (timeRange: TimeRange) => void; @@ -68,7 +69,7 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ Number(value).toFixed(0)} + tickFormat={value => formatAnomalyScore(value).toString()} /> Date: Tue, 15 Oct 2019 11:46:10 +0100 Subject: [PATCH 46/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../public/pages/logs/analysis/sections/anomalies/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 9831832069e6a..f12781caf20db 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -142,7 +142,7 @@ export const AnomaliesResults = ({ description={i18n.translate( 'xpack.infra.logs.analysis.overallAnomaliesNumberOfLogsDescription', { - defaultMessage: 'Number of logs', + defaultMessage: 'Number of log entries', } )} reverse From 32833e68ae1de35c6b4c4adb8e4deaa628f320db Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 11:47:01 +0100 Subject: [PATCH 47/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../public/pages/logs/analysis/sections/log_rate/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index 1c7dda4eb46e5..d4ddd14bfaa28 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -33,7 +33,7 @@ export const LogRateResults = ({ timeRange: TimeRange; }) => { const title = i18n.translate('xpack.infra.logs.analysis.logRateSectionTitle', { - defaultMessage: 'Logs entries', + defaultMessage: 'Log entries', }); const loadingAriaLabel = i18n.translate( From e732f5d32feb71ef3730a5b1b65909285f1a92b8 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 11:54:36 +0100 Subject: [PATCH 48/89] Format y-axis of log entry rate chart the same as anomalies charts --- .../pages/logs/analysis/sections/log_rate/bar_chart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx index 81afd93035e67..de856bee90513 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/bar_chart.tsx @@ -17,12 +17,12 @@ import { DARK_THEME, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import numeral from '@elastic/numeral'; import moment from 'moment'; import React, { useCallback, useMemo } from 'react'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting'; -import { formatAnomalyScore } from '../helpers/data_formatters'; export const LogEntryRateBarChart: React.FunctionComponent<{ setTimeRange: (timeRange: TimeRange) => void; @@ -69,7 +69,7 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ formatAnomalyScore(value).toString()} + tickFormat={value => numeral(value.toPrecision(3)).format('0[.][00]a')} // https://github.com/adamwdraper/Numeral-js/issues/194 /> Date: Tue, 15 Oct 2019 11:54:44 +0100 Subject: [PATCH 49/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 33ae0cdafbb2c..1659c4186f79d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -67,7 +67,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ description={i18n.translate( 'xpack.infra.logs.analysis.anomaliesExpandedRowNumberOfLogsDescription', { - defaultMessage: 'Number of logs', + defaultMessage: 'Number of log entries', } )} reverse From b76bf02932d77ef81c34d3f2b969a9506b3df60d Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 11:59:18 +0100 Subject: [PATCH 50/89] Remove grow prop --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index e0637c3d89850..3aae386a5f7ea 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -62,7 +62,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ annotations={anomalyAnnotations} /> - + Date: Tue, 15 Oct 2019 12:02:01 +0100 Subject: [PATCH 51/89] Update x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../common/http_api/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts index f6dc224621b23..19a5c74d6dd67 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts @@ -42,7 +42,7 @@ export const logEntryRatePartitionRT = rt.type({ anomalies: rt.array(logEntryRateAnomaly), averageActualLogEntryRate: rt.number, maximumAnomalyScore: rt.number, - numberOfLogs: rt.number, + numberOfLogEntries: rt.number, partitionId: rt.string, }); From cadd37f6fcfd45968d55d51857790a3bf3758012 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:02:33 +0100 Subject: [PATCH 52/89] Update x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../common/http_api/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts index 19a5c74d6dd67..5a1412fd8f3d4 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts @@ -55,7 +55,7 @@ export const getLogEntryRateSuccessReponsePayloadRT = rt.type({ data: rt.type({ bucketDuration: rt.number, histogramBuckets: rt.array(logEntryRateHistogramBucket), - totalNumberOfLogs: rt.number, + totalNumberOfLogEntries: rt.number, }), }); From 79c4e215984b8750fcf6c1da6607874858f8ba22 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:07:48 +0100 Subject: [PATCH 53/89] Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../infra/server/routes/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index ededd10eb7304..6bd4f7d19decc 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -64,7 +64,7 @@ export const initLogAnalysisGetLogEntryRateRoute = ({ const getTotalNumberOfLogs = ( logEntryRateBuckets: GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets'] ) => { - return logEntryRateBuckets.reduce((sumNumberOfLogs, bucket) => { + return logEntryRateBuckets.reduce((sumNumberOfLogEntries, bucket) => { const sumPartitions = bucket.partitions.reduce((partitionsTotal, partition) => { return (partitionsTotal += partition.numberOfLogs); }, 0); From 63aca0b7fdcb68eab7f3f03196dad340f38b09e6 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:08:26 +0100 Subject: [PATCH 54/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../infra/public/pages/logs/analysis/page_results_content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index f87e8d48927df..58c7636837b56 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -146,7 +146,7 @@ export const AnalysisResultsContent = ({ numberOfLogs: ( - {numeral(logEntryRate.totalNumberOfLogs).format('0.00a')} + {numeral(logEntryRate.totalNumberOfLogEntries).format('0.00a')} ), From 7bd57083a71124a0afb7962f2b99a5e5ed535f9b Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:09:00 +0100 Subject: [PATCH 55/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 1659c4186f79d..482e196ae8ec9 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -14,7 +14,7 @@ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { getLogEntryRateSeriesForPartition, getAnnotationsForPartition, - getTotalNumberOfLogsForPartition, + getTotalNumberOfLogEntriesForPartition, } from '../helpers/data_formatters'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ From ee6008272c6f90d38380e55605a7f9faf42e0f97 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:09:17 +0100 Subject: [PATCH 56/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 482e196ae8ec9..94fada44a5c77 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -43,7 +43,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ }, [results, partitionId] ); - const totalNumberOfLogs = useMemo( + const totalNumberOfLogEntries = useMemo( () => results && results.histogramBuckets ? getTotalNumberOfLogsForPartition(results, partitionId) From 157ddd8c30f8cb8c43ebaffbd8c2e86fca421798 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:09:30 +0100 Subject: [PATCH 57/89] Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../infra/server/routes/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 6bd4f7d19decc..a171a8f986ec0 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -68,6 +68,6 @@ const getTotalNumberOfLogs = ( const sumPartitions = bucket.partitions.reduce((partitionsTotal, partition) => { return (partitionsTotal += partition.numberOfLogs); }, 0); - return (sumNumberOfLogs += sumPartitions); + return (sumNumberOfLogEntries += sumPartitions); }, 0); }; From e4ed1d174f6e638ab1a5bf1d807fe5561c66784a Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:09:51 +0100 Subject: [PATCH 58/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/helpers/data_formatters.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index a20d240e7752a..229832c81c3ee 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -152,7 +152,7 @@ export const getAnnotationsForPartition = ( ); }; -export const getTotalNumberOfLogsForPartition = ( +export const getTotalNumberOfLogEntriesForPartition = ( results: GetLogEntryRateSuccessResponsePayload['data'], partitionId: string ) => { From eb0b34afd14774a12a9115d8866ddba2d91ac452 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:10:12 +0100 Subject: [PATCH 59/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 94fada44a5c77..9d70f8007ade2 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -46,7 +46,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ const totalNumberOfLogEntries = useMemo( () => results && results.histogramBuckets - ? getTotalNumberOfLogsForPartition(results, partitionId) + ? getTotalNumberOfLogEntriesForPartition(results, partitionId) : undefined, [results, partitionId] ); From a685a435d2c02bf46bcad583b14d335454346367 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:10:37 +0100 Subject: [PATCH 60/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 9d70f8007ade2..adb5965adc2f9 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -65,7 +65,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ Date: Tue, 15 Oct 2019 12:10:56 +0100 Subject: [PATCH 61/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/anomalies/expanded_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index adb5965adc2f9..496f4c83a8b0b 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -63,7 +63,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ Date: Tue, 15 Oct 2019 12:11:25 +0100 Subject: [PATCH 62/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../public/pages/logs/analysis/sections/anomalies/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index f12781caf20db..cc5f5270d4bd5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -138,7 +138,7 @@ export const AnomaliesResults = ({ Date: Tue, 15 Oct 2019 12:11:57 +0100 Subject: [PATCH 63/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../public/pages/logs/analysis/sections/anomalies/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index cc5f5270d4bd5..75ac590445489 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -140,7 +140,7 @@ export const AnomaliesResults = ({ Date: Tue, 15 Oct 2019 12:12:59 +0100 Subject: [PATCH 64/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/helpers/data_formatters.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index 229832c81c3ee..b5c8226b938c7 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -156,7 +156,7 @@ export const getTotalNumberOfLogEntriesForPartition = ( results: GetLogEntryRateSuccessResponsePayload['data'], partitionId: string ) => { - return results.histogramBuckets.reduce((sumPartitionNumberOfLogs, bucket) => { + return results.histogramBuckets.reduce((sumPartitionNumberOfLogEntries, bucket) => { const partitionResults = bucket.partitions.find(partition => { return ( partition.partitionId === partitionId || From 1c587f1e2c9b165b4270fdcadb0102369d561b39 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:13:21 +0100 Subject: [PATCH 65/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/helpers/data_formatters.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index b5c8226b938c7..e4b8415ca766b 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -163,7 +163,7 @@ export const getTotalNumberOfLogEntriesForPartition = ( (partition.partitionId === '' && partitionId === 'unknown') ); }); - if (!partitionResults || !partitionResults.numberOfLogs) { + if (!partitionResults || !partitionResults.numberOfLogEntries) { return sumPartitionNumberOfLogs; } else { return (sumPartitionNumberOfLogs += partitionResults.numberOfLogs); From 8b7535cf95f54f3802b0a72232b227fe26225ff6 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:13:41 +0100 Subject: [PATCH 66/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/helpers/data_formatters.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index e4b8415ca766b..1d5f1ce745bc0 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -166,7 +166,7 @@ export const getTotalNumberOfLogEntriesForPartition = ( if (!partitionResults || !partitionResults.numberOfLogEntries) { return sumPartitionNumberOfLogs; } else { - return (sumPartitionNumberOfLogs += partitionResults.numberOfLogs); + return (sumPartitionNumberOfLogEntries += partitionResults.numberOfLogEntries); } }, 0); }; From b52d9c10240b04a17b06c02eece366eb2f61591b Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:13:54 +0100 Subject: [PATCH 67/89] Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../pages/logs/analysis/sections/helpers/data_formatters.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index 1d5f1ce745bc0..65deaf6d66efe 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -164,7 +164,7 @@ export const getTotalNumberOfLogEntriesForPartition = ( ); }); if (!partitionResults || !partitionResults.numberOfLogEntries) { - return sumPartitionNumberOfLogs; + return sumPartitionNumberOfLogEntries; } else { return (sumPartitionNumberOfLogEntries += partitionResults.numberOfLogEntries); } From 4bdfcfbc124560d8759254f8887f3d47be436063 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:14:10 +0100 Subject: [PATCH 68/89] Update x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../plugins/infra/server/lib/log_analysis/log_analysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts index 5ac22ddf6b793..6f4377e7e2679 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts @@ -92,7 +92,7 @@ export class InfraLogAnalysis { }>; averageActualLogEntryRate: number; maximumAnomalyScore: number; - numberOfLogs: number; + numberOfLogEntries: number; partitionId: string; }>; startTime: number; From a32f8e510555acd52b4980f51271b93564dde7ed Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:14:40 +0100 Subject: [PATCH 69/89] Update x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../plugins/infra/server/lib/log_analysis/log_analysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts index 6f4377e7e2679..d970a142c5c23 100644 --- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts @@ -114,7 +114,7 @@ export class InfraLogAnalysis { timestampPartitionBucket.filter_model_plot.average_actual.value || 0, maximumAnomalyScore: timestampPartitionBucket.filter_records.maximum_record_score.value || 0, - numberOfLogs: timestampPartitionBucket.filter_model_plot.sum_actual.value || 0, + numberOfLogEntries: timestampPartitionBucket.filter_model_plot.sum_actual.value || 0, partitionId: timestampPartitionBucket.key.partition, }; if ( From 8e31a6c6664b8b0ede7e86d22c15be3f3151f02f Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:15:05 +0100 Subject: [PATCH 70/89] Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../infra/server/routes/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index a171a8f986ec0..6f4928dfff8ee 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -53,7 +53,7 @@ export const initLogAnalysisGetLogEntryRateRoute = ({ data: { bucketDuration: payload.data.bucketDuration, histogramBuckets: logEntryRateBuckets, - totalNumberOfLogs: getTotalNumberOfLogs(logEntryRateBuckets), + totalNumberOfLogEntries: getTotalNumberOfLogEntries(logEntryRateBuckets), }, }) ); From 721b7f82b6d44f856864b8cc85ca8999b3f5cd2c Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:15:46 +0100 Subject: [PATCH 71/89] Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Felix Stürmer --- .../infra/server/routes/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 6f4928dfff8ee..e6780e43365ad 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -61,7 +61,7 @@ export const initLogAnalysisGetLogEntryRateRoute = ({ }); }; -const getTotalNumberOfLogs = ( +const getTotalNumberOfLogEntries = ( logEntryRateBuckets: GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets'] ) => { return logEntryRateBuckets.reduce((sumNumberOfLogEntries, bucket) => { From a491bff3b08c4b62d4f139b0c27af9a20c934055 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:22:47 +0100 Subject: [PATCH 72/89] Change path --- .../infra/public/pages/logs/analysis/page_results_content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 58c7636837b56..cabd55bcea17e 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -21,7 +21,7 @@ import moment from 'moment'; import React, { useCallback, useMemo, useState } from 'react'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; -import euiStyled from '../../../../../../../legacy/common/eui_styled_components'; +import euiStyled from '../../../../../../common/eui_styled_components'; import { LoadingPage } from '../../../components/loading_page'; import { StringTimeRange, From f7787bd08aa5e3ef45530203d9d69e95c64cd0b2 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:36:35 +0100 Subject: [PATCH 73/89] Amend property name --- .../infra/server/routes/log_analysis/results/log_entry_rate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index e6780e43365ad..fc06ea48f4353 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -66,7 +66,7 @@ const getTotalNumberOfLogEntries = ( ) => { return logEntryRateBuckets.reduce((sumNumberOfLogEntries, bucket) => { const sumPartitions = bucket.partitions.reduce((partitionsTotal, partition) => { - return (partitionsTotal += partition.numberOfLogs); + return (partitionsTotal += partition.numberOfLogEntries); }, 0); return (sumNumberOfLogEntries += sumPartitions); }, 0); From 305cdf4c4ee88abd471875f2d122c3804528921e Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:39:42 +0100 Subject: [PATCH 74/89] Use proper default value argument --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index f3429089734f5..767b2cefd42b1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -36,7 +36,7 @@ export const AnomaliesChart: React.FunctionComponent<{ annotations: Record; renderAnnotationTooltip?: (details?: string) => JSX.Element; }> = ({ chartId, series, annotations, setTimeRange, timeRange, renderAnnotationTooltip }) => { - const [dateFormat] = useKibanaUiSetting('dateFormat'); + const [dateFormat] = useKibanaUiSetting('dateFormat', 'Y-MM-DD HH:mm:ss.SSS'); const [isDarkMode] = useKibanaUiSetting('theme:darkMode'); const chartDateFormatter = useMemo( @@ -48,8 +48,7 @@ export const AnomaliesChart: React.FunctionComponent<{ const tooltipProps = useMemo( () => ({ - headerFormatter: (tooltipData: TooltipValue) => - moment(tooltipData.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'), + headerFormatter: (tooltipData: TooltipValue) => moment(tooltipData.value).format(dateFormat), }), [dateFormat] ); From 7f468b4107723a6e59db08457a93e8820e4b8989 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 12:52:20 +0100 Subject: [PATCH 75/89] Use Kibana dateFormat setting for toolbar formatting --- .../pages/logs/analysis/page_results_content.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index cabd55bcea17e..f4d7845753528 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -32,6 +32,7 @@ import { useTrackPageview } from '../../../hooks/use_track_metric'; import { FirstUseCallout } from './first_use'; import { LogRateResults } from './sections/log_rate'; import { AnomaliesResults } from './sections/anomalies'; +import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; export const AnalysisResultsContent = ({ sourceId, @@ -43,6 +44,8 @@ export const AnalysisResultsContent = ({ useTrackPageview({ app: 'infra_logs', path: 'analysis_results' }); useTrackPageview({ app: 'infra_logs', path: 'analysis_results', delay: 15000 }); + const [dateFormat] = useKibanaUiSetting('dateFormat', 'MMMM D, YYYY h:mm A'); + const { timeRange: selectedTimeRange, setTimeRange: setSelectedTimeRange, @@ -151,15 +154,9 @@ export const AnalysisResultsContent = ({ ), startTime: ( - - {moment(queryTimeRange.startTime).format('MMMM D, YYYY h:mm A')} - - ), - endTime: ( - - {moment(queryTimeRange.endTime).format('MMMM D, YYYY h:mm A')} - + {moment(queryTimeRange.startTime).format(dateFormat)} ), + endTime: {moment(queryTimeRange.endTime).format(dateFormat)}, }} />
    From 7fa26ed1f2f81007d56a90e8df4ed0022a62b52e Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:13:56 +0100 Subject: [PATCH 76/89] Change logic for calculating severity score categories --- .../sections/helpers/data_formatters.tsx | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx index cd7f93f780384..105604507b425 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx @@ -113,14 +113,13 @@ export const getAnnotationsForPartition = ( (partition.partitionId === '' && partitionId === 'unknown') ); }); - if ( - !partitionResults || - !partitionResults.maximumAnomalyScore || - partitionResults.maximumAnomalyScore < ML_SEVERITY_SCORES.warning - ) { + const severityCategory = partitionResults + ? getSeverityCategoryForScore(partitionResults.maximumAnomalyScore) + : null; + if (!partitionResults || !partitionResults.maximumAnomalyScore || !severityCategory) { return annotatedBucketsBySeverity; } - const severityCategory = getSeverityCategoryForScore(partitionResults.maximumAnomalyScore); + return { ...annotatedBucketsBySeverity, [severityCategory]: [ @@ -177,7 +176,7 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl const maxAnomalyScoresByPartition = bucket.partitions.reduce< Array<{ partitionId: string; maximumAnomalyScore: number }> >((bucketMaxAnomalyScoresByPartition, partition) => { - if (partition.maximumAnomalyScore < ML_SEVERITY_SCORES.warning) { + if (!getSeverityCategoryForScore(partition.maximumAnomalyScore)) { return bucketMaxAnomalyScoresByPartition; } return [ @@ -197,6 +196,9 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl ...maxAnomalyScoresByPartition.map(partitionScore => partitionScore.maximumAnomalyScore) ) ); + if (!severityCategory) { + return annotatedBucketsBySeverity; + } const sortedMaxAnomalyScoresByPartition = maxAnomalyScoresByPartition.sort( (a, b) => b.maximumAnomalyScore - a.maximumAnomalyScore ); @@ -237,15 +239,18 @@ export const getTopAnomalyScoreAcrossAllPartitions = ( return Math.max(...allMaxScores); }; -const getSeverityCategoryForScore = (score: number) => { - if (score >= ML_SEVERITY_SCORES.warning && score < ML_SEVERITY_SCORES.minor) { - return 'warning'; - } else if (score >= ML_SEVERITY_SCORES.minor && score < ML_SEVERITY_SCORES.major) { - return 'minor'; - } else if (score >= ML_SEVERITY_SCORES.major && score < ML_SEVERITY_SCORES.critical) { +const getSeverityCategoryForScore = (score: number): MLSeverityScoreCategories | undefined => { + if (score >= ML_SEVERITY_SCORES.critical) { + return 'critical'; + } else if (score >= ML_SEVERITY_SCORES.major) { return 'major'; + } else if (score >= ML_SEVERITY_SCORES.minor) { + return 'minor'; + } else if (score >= ML_SEVERITY_SCORES.warning) { + return 'warning'; } else { - return 'critical'; + // Category is too low to include + return undefined; } }; From d01e5f1d1732561cc60c6ca39569c4da2db4b591 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:24:01 +0100 Subject: [PATCH 77/89] Add missing translations --- .../analysis/sections/anomalies/table.tsx | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 179a703ea29e4..02443a7be4c02 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -6,6 +6,7 @@ import React, { useMemo, useState, useCallback } from 'react'; import { EuiBasicTable, EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; @@ -18,6 +19,28 @@ interface TableItem { topAnomalyScore: number; } +const collapseAriaLabel = i18n.translate('xpack.infra.logs.analysis.anomaliesTableCollapseLabel', { + defaultMessage: 'Collapse', +}); + +const expandAriaLabel = i18n.translate('xpack.infra.logs.analysis.anomaliesTableExpandLabel', { + defaultMessage: 'Expand', +}); + +const partitionColumnName = i18n.translate( + 'xpack.infra.logs.analysis.anomaliesTablePartitionColumnName', + { + defaultMessage: 'Partition', + } +); + +const maxAnomalyScoreColumnName = i18n.translate( + 'xpack.infra.logs.analysis.anomaliesTableMaxAnomalyScoreColumnName', + { + defaultMessage: 'Max anomaly score', + } +); + export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; @@ -104,13 +127,13 @@ export const AnomaliesTable: React.FunctionComponent<{ const columns = [ { field: 'partition', - name: 'Partition', + name: partitionColumnName, sortable: true, truncateText: true, }, { field: 'topAnomalyScore', - name: 'Max anomaly score', + name: maxAnomalyScoreColumnName, sortable: true, truncateText: true, }, @@ -121,7 +144,7 @@ export const AnomaliesTable: React.FunctionComponent<{ render: (item: TableItem) => ( toggleExpandedItems(item)} - aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'} + aria-label={itemIdToExpandedRowMap[item.id] ? collapseAriaLabel : expandAriaLabel} iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'} /> ), From a1493ced171fb228150babe17ec54f4de887e6e3 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:25:03 +0100 Subject: [PATCH 78/89] Add interface to top of file --- .../logs/analysis/sections/anomalies/table.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 02443a7be4c02..b0ad9afc49aaf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -19,6 +19,13 @@ interface TableItem { topAnomalyScore: number; } +interface SortingOptions { + sort: { + field: string; + direction: string; + }; +} + const collapseAriaLabel = i18n.translate('xpack.infra.logs.analysis.anomaliesTableCollapseLabel', { defaultMessage: 'Collapse', }); @@ -60,13 +67,6 @@ export const AnomaliesTable: React.FunctionComponent<{ Record >({}); - interface SortingOptions { - sort: { - field: string; - direction: string; - }; - } - const [sorting, setSorting] = useState({ sort: { field: 'topAnomalyScore', From 8197183af4f3bc56a2486356a3b230a1141b4573 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:29:46 +0100 Subject: [PATCH 79/89] Add no-wrap to tooltip --- .../logs/analysis/sections/anomalies/index.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 8bf5b8bb22355..4e8dfc3022fed 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -18,6 +18,7 @@ import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import euiStyled from '../../../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; @@ -181,8 +182,10 @@ const overallAnomalyScoreLabel = i18n.translate( const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ details }) => { const parsedDetails: ParsedAnnotationDetails = JSON.parse(details); return ( -
    - {overallAnomalyScoreLabel} + + + {overallAnomalyScoreLabel} +
      {parsedDetails.anomalyScoresByPartition.map( ({ partitionId, maximumAnomalyScore }, index) => { @@ -199,7 +202,7 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai } )}
    -
    + ); }; @@ -210,3 +213,7 @@ const renderAnnotationTooltip = (details?: string) => { } return ; }; + +const TooltipWrapper = euiStyled('div')` + white-space: nowrap; +`; From 5bcad0dd23e05df521c1f8c540d4ec61b6821eeb Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:43:23 +0100 Subject: [PATCH 80/89] Use more idomatic code --- .../pages/logs/analysis/sections/anomalies/index.tsx | 4 +--- .../pages/logs/analysis/sections/anomalies/table.tsx | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 4e8dfc3022fed..c4024e67dc1a0 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -190,9 +190,7 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai {parsedDetails.anomalyScoresByPartition.map( ({ partitionId, maximumAnomalyScore }, index) => { return ( -
  • +
  • {`${partitionId}: `} {maximumAnomalyScore} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index b0ad9afc49aaf..2a8ac44d09083 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -100,11 +100,8 @@ export const AnomaliesTable: React.FunctionComponent<{ const toggleExpandedItems = useCallback( item => { if (itemIdToExpandedRowMap[item.id]) { - const newItemIdToExpandedRowMap = { - ...itemIdToExpandedRowMap, - }; - delete newItemIdToExpandedRowMap[item.id]; - setItemIdToExpandedRowMap(newItemIdToExpandedRowMap); + const { [item.id]: toggledItem, ...remainingExpandedRowMap } = itemIdToExpandedRowMap; + setItemIdToExpandedRowMap(remainingExpandedRowMap); } else { const newItemIdToExpandedRowMap = { ...itemIdToExpandedRowMap, From a5d6e450958b092eb97a5808f60b57eb2eb40d83 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 13:46:02 +0100 Subject: [PATCH 81/89] Use static value for series styles --- .../public/pages/logs/analysis/sections/anomalies/chart.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx index 767b2cefd42b1..73adcd13e2441 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/chart.tsx @@ -87,7 +87,7 @@ export const AnomaliesChart: React.FunctionComponent<{ xAccessor={'time'} yAccessors={['value']} data={series} - barSeriesStyle={{ rect: { fill: '#D3DAE6', opacity: 0.6 } }} // TODO: Acquire this from "theme" as euiColorLightShade + barSeriesStyle={barSeriesStyle} /> {renderAnnotations(annotations, chartId, renderAnnotationTooltip)} Date: Tue, 15 Oct 2019 17:06:01 +0200 Subject: [PATCH 82/89] Move the callouts into the right location --- .../logs/analysis/page_results_content.tsx | 11 ++--- .../analysis/sections/anomalies/index.tsx | 43 +++++++++++++------ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 9c9be53ae4c7b..38f6aa9036c19 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -24,7 +24,6 @@ import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; -import { LogAnalysisJobProblemIndicator } from '../../../components/logging/log_analysis_job_status'; import { LogAnalysisJobs, StringTimeRange, @@ -203,20 +202,18 @@ export const AnalysisResultsContent = ({ setTimeRange={handleChartTimeRangeChange} timeRange={queryTimeRange} /> - diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index c4024e67dc1a0..4b3d80c00bbc3 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -10,35 +10,45 @@ import { EuiFlexItem, EuiLoadingChart, EuiSpacer, - EuiTitle, EuiStat, + EuiTitle, } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; -import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import euiStyled from '../../../../../../../../common/eui_styled_components'; +import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; -import { AnomaliesChart } from './chart'; -import { AnomaliesTable } from './table'; +import { JobStatus, SetupStatus } from '../../../../../../common/log_analysis'; import { - getLogEntryRateCombinedSeries, + formatAnomalyScore, getAnnotationsForAll, + getLogEntryRateCombinedSeries, getTopAnomalyScoreAcrossAllPartitions, - formatAnomalyScore, } from '../helpers/data_formatters'; +import { AnomaliesChart } from './chart'; +import { AnomaliesTable } from './table'; +import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status'; -export const AnomaliesResults = ({ - isLoading, - results, - setTimeRange, - timeRange, -}: { +export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; + jobStatus: JobStatus; results: GetLogEntryRateSuccessResponsePayload['data'] | null; setTimeRange: (timeRange: TimeRange) => void; + setupStatus: SetupStatus; timeRange: TimeRange; + viewSetupForReconfiguration: () => void; + viewSetupForUpdate: () => void; +}> = ({ + isLoading, + jobStatus, + results, + setTimeRange, + setupStatus, + timeRange, + viewSetupForReconfiguration, + viewSetupForUpdate, }) => { const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { defaultMessage: 'Anomalies', @@ -89,7 +99,14 @@ export const AnomaliesResults = ({

    {title}

    - + + + {isLoading ? ( From ae7de65de1f2db5daae80d8b21fd17e38d43d4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 15 Oct 2019 17:06:48 +0200 Subject: [PATCH 83/89] Fix linter warning --- .../analysis/sections/anomalies/index.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 4b3d80c00bbc3..88e401e05541a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -204,18 +204,16 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai {overallAnomalyScoreLabel}
      - {parsedDetails.anomalyScoresByPartition.map( - ({ partitionId, maximumAnomalyScore }, index) => { - return ( -
    • - - {`${partitionId}: `} - {maximumAnomalyScore} - -
    • - ); - } - )} + {parsedDetails.anomalyScoresByPartition.map(({ partitionId, maximumAnomalyScore }) => { + return ( +
    • + + {`${partitionId}: `} + {maximumAnomalyScore} + +
    • + ); + })}
    ); From 949ffa144ddb111fdb9e6dbad80511a6628435c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 15 Oct 2019 17:21:18 +0200 Subject: [PATCH 84/89] Add non-functional ML link button --- .../analysis/sections/anomalies/index.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 88e401e05541a..1bba8995f0c76 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -5,6 +5,7 @@ */ import { + EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, @@ -15,6 +16,7 @@ import { } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; import euiStyled from '../../../../../../../../common/eui_styled_components'; @@ -96,9 +98,21 @@ export const AnomaliesResults: React.FunctionComponent<{ return ( <> - -

    {title}

    -
    + + + +

    {title}

    +
    +
    + + + + + +
    Date: Tue, 15 Oct 2019 19:19:20 +0100 Subject: [PATCH 85/89] Add "Analyze in ML" buttons --- .../logs/log_analysis/log_analysis_jobs.tsx | 9 ++++++++- .../pages/logs/analysis/page_content.tsx | 3 ++- .../logs/analysis/page_results_content.tsx | 16 +++++++++++++--- .../sections/anomalies/expanded_row.tsx | 13 +++++++++++-- .../logs/analysis/sections/anomalies/index.tsx | 18 +++++++++++------- .../logs/analysis/sections/anomalies/table.tsx | 5 ++++- .../public/utils/use_kibana_injected_var.ts | 1 - 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx index 83d4760259d9b..aa66febc5b7c4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx @@ -8,7 +8,7 @@ import createContainer from 'constate-latest'; import { useMemo, useCallback, useEffect } from 'react'; import { callGetMlModuleAPI } from './api/ml_get_module'; -import { bucketSpan } from '../../../../common/log_analysis'; +import { bucketSpan, getJobId } from '../../../../common/log_analysis'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callJobsSummaryAPI } from './api/ml_get_jobs_summary_api'; import { callSetupMlModuleAPI, SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; @@ -138,6 +138,12 @@ export const useLogAnalysisJobs = ({ fetchModuleDefinition(); }, [fetchModuleDefinition]); + const jobIds = useMemo(() => { + return { + 'log-entry-rate': getJobId(spaceId, sourceId, 'log-entry-rate'), + }; + }, [sourceId, spaceId]); + return { fetchJobStatus, isLoadingSetupStatus, @@ -149,6 +155,7 @@ export const useLogAnalysisJobs = ({ viewSetupForReconfiguration, viewSetupForUpdate, viewResults, + jobIds, }; }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx index 2b83007e2ab99..0a5d9932a32d3 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx @@ -20,7 +20,7 @@ export const AnalysisPageContent = () => { const { sourceId, source } = useContext(Source.Context); const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); - const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus } = useContext( + const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus, jobIds } = useContext( LogAnalysisJobs.Context ); @@ -45,6 +45,7 @@ export const AnalysisPageContent = () => { ); } else { diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 38f6aa9036c19..f9b1c44296910 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -19,7 +19,7 @@ import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import React, { useCallback, useContext, useMemo, useState } from 'react'; - +import chrome from 'ui/chrome'; import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; @@ -32,24 +32,28 @@ import { } from '../../../containers/logs/log_analysis'; import { useInterval } from '../../../hooks/use_interval'; import { useTrackPageview } from '../../../hooks/use_track_metric'; -import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; import { FirstUseCallout } from './first_use'; import { AnomaliesResults } from './sections/anomalies'; import { LogRateResults } from './sections/log_rate'; +import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; +import { getMlLinkFormatter } from './sections/helpers/ml_links'; const JOB_STATUS_POLLING_INTERVAL = 30000; export const AnalysisResultsContent = ({ sourceId, isFirstUse, + jobId, }: { sourceId: string; isFirstUse: boolean; + jobId: string; }) => { useTrackPageview({ app: 'infra_logs', path: 'analysis_results' }); useTrackPageview({ app: 'infra_logs', path: 'analysis_results', delay: 15000 }); const [dateFormat] = useKibanaUiSetting('dateFormat', 'MMMM D, YYYY h:mm A'); + const basePath = chrome.getBasePath(); const { timeRange: selectedTimeRange, @@ -61,7 +65,12 @@ export const AnalysisResultsContent = ({ const [queryTimeRange, setQueryTimeRange] = useState( stringToNumericTimeRange(selectedTimeRange) ); - + const getMlLink = getMlLinkFormatter({ + basePath, + jobId, + startTime: queryTimeRange.startTime, + endTime: queryTimeRange.endTime, + }); const bucketDuration = useMemo(() => { // This function takes the current time range in ms, // works out the bucket interval we'd need to always @@ -215,6 +224,7 @@ export const AnalysisResultsContent = ({ setTimeRange={handleChartTimeRangeChange} setupStatus={setupStatus} timeRange={queryTimeRange} + getMlLink={getMlLink} /> diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index a6f2ca71068c2..3f1a8eb8134bf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -7,7 +7,7 @@ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import numeral from '@elastic/numeral'; -import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiSpacer } from '@elastic/eui'; import { AnomaliesChart } from './chart'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; @@ -17,6 +17,8 @@ import { formatAnomalyScore, getTotalNumberOfLogEntriesForPartition, } from '../helpers/data_formatters'; +import { GetMlLink } from '../helpers/ml_links'; +import { AnalyzeInMlButton } from '../analyze_in_ml_button'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ partitionId: string; @@ -24,7 +26,8 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; -}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId }) => { + getMlLink: GetMlLink; +}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId, getMlLink }) => { const logEntryRateSeries = useMemo( () => results && results.histogramBuckets @@ -83,6 +86,12 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ )} reverse /> + + + + + + ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 1bba8995f0c76..723ecdf93c218 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -29,9 +29,11 @@ import { getLogEntryRateCombinedSeries, getTopAnomalyScoreAcrossAllPartitions, } from '../helpers/data_formatters'; +import { GetMlLink } from '../helpers/ml_links'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status'; +import { AnalyzeInMlButton } from '../analyze_in_ml_button'; export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; @@ -42,6 +44,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange: TimeRange; viewSetupForReconfiguration: () => void; viewSetupForUpdate: () => void; + getMlLink: GetMlLink; }> = ({ isLoading, jobStatus, @@ -51,6 +54,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange, viewSetupForReconfiguration, viewSetupForUpdate, + getMlLink, }) => { const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { defaultMessage: 'Anomalies', @@ -105,12 +109,7 @@ export const AnomaliesResults: React.FunctionComponent<{ - - - + @@ -193,7 +192,12 @@ export const AnomaliesResults: React.FunctionComponent<{ - + )} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 2a8ac44d09083..9e8a92f5241f7 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -12,6 +12,7 @@ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { AnomaliesTableExpandedRow } from './expanded_row'; import { getTopAnomalyScoresByPartition, formatAnomalyScore } from '../helpers/data_formatters'; +import { GetMlLink } from '../helpers/ml_links'; interface TableItem { id: string; @@ -52,7 +53,8 @@ export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; -}> = ({ results, timeRange, setTimeRange }) => { + getMlLink: GetMlLink; +}> = ({ results, timeRange, setTimeRange, getMlLink }) => { const tableItems: TableItem[] = useMemo(() => { return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { @@ -112,6 +114,7 @@ export const AnomaliesTable: React.FunctionComponent<{ topAnomalyScore={item.topAnomalyScore} setTimeRange={setTimeRange} timeRange={timeRange} + getMlLink={getMlLink} /> ), }; diff --git a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts b/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts index bcd36f72bef07..4afcb4fd88430 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts @@ -14,6 +14,5 @@ import { npSetup } from 'ui/new_platform'; */ export const useKibanaInjectedVar = (name: string, defaultValue?: unknown) => { const injectedMetadata = npSetup.core.injectedMetadata; - return injectedMetadata.getInjectedVar(name, defaultValue); }; From 8d72d24394653c037367530213573c1cb74181bd Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Tue, 15 Oct 2019 19:35:02 +0100 Subject: [PATCH 86/89] Add "Analyze in ML" buttons (2) --- .../sections/analyze_in_ml_button.tsx | 24 ++++++++++ .../analysis/sections/helpers/ml_links.tsx | 48 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx new file mode 100644 index 0000000000000..4b67775895678 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx @@ -0,0 +1,24 @@ +/* + * 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 React from 'react'; +import { EuiButton, ButtonColor } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const AnalyzeInMlButton: React.FunctionComponent<{ + color?: ButtonColor; + fill?: boolean; + href: string; +}> = ({ color = 'primary', fill = true, href }) => { + return ( + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx new file mode 100644 index 0000000000000..acbcebbccf5b8 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx @@ -0,0 +1,48 @@ +/* + * 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 { encode } from 'rison-node'; + +export type GetMlLink = (partitionId?: string | undefined) => string; + +export const getMlLinkFormatter = ({ + basePath, + startTime, + endTime, + jobId, +}: { + basePath: string; + startTime: number; + endTime: number; + jobId: string; +}) => (partitionId?: string) => { + const startTimeParam = new Date(startTime).toISOString(); + const endTimeParam = new Date(endTime).toISOString(); + + const _g = encode({ + ml: { + jobIds: [jobId], + }, + time: { + from: startTimeParam, + to: endTimeParam, + mode: 'absolute', + }, + }); + + const baseLink = `${basePath}/app/ml#/timeseriesexplorer?_g=${_g}`; + + if (partitionId) { + const _a = encode({ + mlTimeSeriesExplorer: { + entities: { 'event.dataset': partitionId }, + }, + }); + return `${baseLink}&_a=${encodeURIComponent(_a)}`; + } else { + return baseLink; + } +}; From 1f530d10c9da7c853207a3c97396cd86fc89e50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 16 Oct 2019 02:13:27 +0200 Subject: [PATCH 87/89] Remove superfluous imports --- .../public/pages/logs/analysis/sections/anomalies/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 723ecdf93c218..fb19348932bcf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -5,7 +5,6 @@ */ import { - EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, @@ -16,7 +15,6 @@ import { } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; import euiStyled from '../../../../../../../../common/eui_styled_components'; From d483ed747c448aeac3141a0931883f566e772207 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 16 Oct 2019 12:28:46 +0100 Subject: [PATCH 88/89] Tweak hierarchy of button and link to "Anomaly explorer" view for overall link --- .../pages/logs/analysis/page_content.tsx | 3 +- .../logs/analysis/page_results_content.tsx | 15 +-- .../sections/analyze_in_ml_button.tsx | 98 ++++++++++++++++--- .../sections/anomalies/expanded_row.tsx | 7 +- .../analysis/sections/anomalies/index.tsx | 9 +- .../analysis/sections/anomalies/table.tsx | 7 +- .../analysis/sections/helpers/ml_links.tsx | 48 --------- 7 files changed, 101 insertions(+), 86 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx index 0a5d9932a32d3..2b83007e2ab99 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx @@ -20,7 +20,7 @@ export const AnalysisPageContent = () => { const { sourceId, source } = useContext(Source.Context); const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); - const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus, jobIds } = useContext( + const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus } = useContext( LogAnalysisJobs.Context ); @@ -45,7 +45,6 @@ export const AnalysisPageContent = () => { ); } else { diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 0bc4816ab6d45..e846d4e9e4ac5 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -19,7 +19,6 @@ import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import React, { useCallback, useContext, useMemo, useState } from 'react'; -import chrome from 'ui/chrome'; import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; @@ -36,24 +35,20 @@ import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; import { FirstUseCallout } from './first_use'; import { AnomaliesResults } from './sections/anomalies'; import { LogRateResults } from './sections/log_rate'; -import { getMlLinkFormatter } from './sections/helpers/ml_links'; const JOB_STATUS_POLLING_INTERVAL = 30000; export const AnalysisResultsContent = ({ sourceId, isFirstUse, - jobId, }: { sourceId: string; isFirstUse: boolean; - jobId: string; }) => { useTrackPageview({ app: 'infra_logs', path: 'analysis_results' }); useTrackPageview({ app: 'infra_logs', path: 'analysis_results', delay: 15000 }); const [dateFormat] = useKibanaUiSetting('dateFormat', 'MMMM D, YYYY h:mm A'); - const basePath = chrome.getBasePath(); const { timeRange: selectedTimeRange, @@ -65,12 +60,7 @@ export const AnalysisResultsContent = ({ const [queryTimeRange, setQueryTimeRange] = useState( stringToNumericTimeRange(selectedTimeRange) ); - const getMlLink = getMlLinkFormatter({ - basePath, - jobId, - startTime: queryTimeRange.startTime, - endTime: queryTimeRange.endTime, - }); + const bucketDuration = useMemo(() => { // This function takes the current time range in ms, // works out the bucket interval we'd need to always @@ -144,6 +134,7 @@ export const AnalysisResultsContent = ({ setupStatus, viewSetupForReconfiguration, viewSetupForUpdate, + jobIds, } = useContext(LogAnalysisJobs.Context); useInterval(() => { @@ -224,7 +215,7 @@ export const AnalysisResultsContent = ({ setTimeRange={handleChartTimeRangeChange} setupStatus={setupStatus} timeRange={queryTimeRange} - getMlLink={getMlLink} + jobId={jobIds['log-entry-rate']} /> diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx index 4b67775895678..53822b0e865fd 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx @@ -5,20 +5,96 @@ */ import React from 'react'; -import { EuiButton, ButtonColor } from '@elastic/eui'; +import url from 'url'; +import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import chrome from 'ui/chrome'; +import { encode } from 'rison-node'; +import { TimeRange } from '../../../../../common/http_api/shared/time_range'; export const AnalyzeInMlButton: React.FunctionComponent<{ - color?: ButtonColor; - fill?: boolean; - href: string; -}> = ({ color = 'primary', fill = true, href }) => { - return ( - - + jobId: string; + partition?: string; + timeRange: TimeRange; +}> = ({ jobId, partition, timeRange }) => { + const pathname = chrome.addBasePath('/app/ml'); + const buttonLabel = ( + + ); + return partition ? ( + + {buttonLabel} + + ) : ( + + {buttonLabel} ); }; + +const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRange: TimeRange) => { + const { from, to } = convertTimeRangeToParams(timeRange); + + const _g = encode({ + ml: { + jobIds: [jobId], + }, + time: { + from, + to, + }, + }); + + const hash = `/explorer?_g=${_g}`; + + return url.format({ + pathname, + hash, + }); +}; + +const getPartitionSpecificSingleMetricViewerLink = ( + pathname: string, + jobId: string, + partition: string, + timeRange: TimeRange +) => { + const { from, to } = convertTimeRangeToParams(timeRange); + + const _g = encode({ + ml: { + jobIds: [jobId], + }, + time: { + from, + to, + mode: 'absolute', + }, + }); + + const _a = encode({ + mlTimeSeriesExplorer: { + entities: { 'event.dataset': partition }, + }, + }); + + const hash = `/timeseriesexplorer?_g=${_g}&_a=${encodeURIComponent(_a)}`; + + return url.format({ + pathname, + hash, + }); +}; + +const convertTimeRangeToParams = (timeRange: TimeRange): { from: string; to: string } => { + return { + from: new Date(timeRange.startTime).toISOString(), + to: new Date(timeRange.endTime).toISOString(), + }; +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index 3f1a8eb8134bf..43cfbb306717d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -17,7 +17,6 @@ import { formatAnomalyScore, getTotalNumberOfLogEntriesForPartition, } from '../helpers/data_formatters'; -import { GetMlLink } from '../helpers/ml_links'; import { AnalyzeInMlButton } from '../analyze_in_ml_button'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ @@ -26,8 +25,8 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; - getMlLink: GetMlLink; -}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId, getMlLink }) => { + jobId: string; +}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId, jobId }) => { const logEntryRateSeries = useMemo( () => results && results.histogramBuckets @@ -89,7 +88,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ - + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index fb19348932bcf..5aa5891d7981d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -27,7 +27,6 @@ import { getLogEntryRateCombinedSeries, getTopAnomalyScoreAcrossAllPartitions, } from '../helpers/data_formatters'; -import { GetMlLink } from '../helpers/ml_links'; import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status'; @@ -42,7 +41,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange: TimeRange; viewSetupForReconfiguration: () => void; viewSetupForUpdate: () => void; - getMlLink: GetMlLink; + jobId: string; }> = ({ isLoading, jobStatus, @@ -52,7 +51,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange, viewSetupForReconfiguration, viewSetupForUpdate, - getMlLink, + jobId, }) => { const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { defaultMessage: 'Anomalies', @@ -107,7 +106,7 @@ export const AnomaliesResults: React.FunctionComponent<{ - + @@ -194,7 +193,7 @@ export const AnomaliesResults: React.FunctionComponent<{ results={results} setTimeRange={setTimeRange} timeRange={timeRange} - getMlLink={getMlLink} + jobId={jobId} /> )} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 9e8a92f5241f7..88ffcae6e9dea 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -12,7 +12,6 @@ import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { AnomaliesTableExpandedRow } from './expanded_row'; import { getTopAnomalyScoresByPartition, formatAnomalyScore } from '../helpers/data_formatters'; -import { GetMlLink } from '../helpers/ml_links'; interface TableItem { id: string; @@ -53,8 +52,8 @@ export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; - getMlLink: GetMlLink; -}> = ({ results, timeRange, setTimeRange, getMlLink }) => { + jobId: string; +}> = ({ results, timeRange, setTimeRange, jobId }) => { const tableItems: TableItem[] = useMemo(() => { return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { @@ -114,7 +113,7 @@ export const AnomaliesTable: React.FunctionComponent<{ topAnomalyScore={item.topAnomalyScore} setTimeRange={setTimeRange} timeRange={timeRange} - getMlLink={getMlLink} + jobId={jobId} /> ), }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx deleted file mode 100644 index acbcebbccf5b8..0000000000000 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/ml_links.tsx +++ /dev/null @@ -1,48 +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 { encode } from 'rison-node'; - -export type GetMlLink = (partitionId?: string | undefined) => string; - -export const getMlLinkFormatter = ({ - basePath, - startTime, - endTime, - jobId, -}: { - basePath: string; - startTime: number; - endTime: number; - jobId: string; -}) => (partitionId?: string) => { - const startTimeParam = new Date(startTime).toISOString(); - const endTimeParam = new Date(endTime).toISOString(); - - const _g = encode({ - ml: { - jobIds: [jobId], - }, - time: { - from: startTimeParam, - to: endTimeParam, - mode: 'absolute', - }, - }); - - const baseLink = `${basePath}/app/ml#/timeseriesexplorer?_g=${_g}`; - - if (partitionId) { - const _a = encode({ - mlTimeSeriesExplorer: { - entities: { 'event.dataset': partitionId }, - }, - }); - return `${baseLink}&_a=${encodeURIComponent(_a)}`; - } else { - return baseLink; - } -}; From 9dab86dff0ecb504206b7fd5c707bc48fc33843f Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 16 Oct 2019 13:29:58 +0100 Subject: [PATCH 89/89] Use QueryString.encode instead of encodeURIComponent --- .../pages/logs/analysis/sections/analyze_in_ml_button.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx index 53822b0e865fd..852ce20e37d41 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx @@ -9,6 +9,7 @@ import url from 'url'; import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import chrome from 'ui/chrome'; +import { QueryString } from 'ui/utils/query_string'; import { encode } from 'rison-node'; import { TimeRange } from '../../../../../common/http_api/shared/time_range'; @@ -51,7 +52,7 @@ const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRang }, }); - const hash = `/explorer?_g=${_g}`; + const hash = `/explorer?${QueryString.encode({ _g })}`; return url.format({ pathname, @@ -84,7 +85,7 @@ const getPartitionSpecificSingleMetricViewerLink = ( }, }); - const hash = `/timeseriesexplorer?_g=${_g}&_a=${encodeURIComponent(_a)}`; + const hash = `/timeseriesexplorer?${QueryString.encode({ _g, _a })}`; return url.format({ pathname,