[Logs UI] Improve Analysis onboarding experience (#44188) (#44404)

* Improve the initial first use / first render of results screen

* Sets the x-axis domain to the time range rather than the min and max of the data points provided
This commit is contained in:
Kerry Gallagher 2019-08-29 20:38:35 +01:00 committed by GitHub
parent 235483d5f6
commit 62f6c28eb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 12 deletions

View file

@ -10,3 +10,5 @@ export const timeRangeRT = rt.type({
startTime: rt.number,
endTime: rt.number,
});
export type TimeRange = rt.TypeOf<typeof timeRangeRT>;

View file

@ -44,6 +44,7 @@ export const useLogAnalysisJobs = ({
timeField: string;
}) => {
const [jobStatus, setJobStatus] = useState<AllJobStatuses>(getInitialJobStatuses());
const [hasCompletedSetup, setHasCompletedSetup] = useState<boolean>(false);
const [setupMlModuleRequest, setupMlModule] = useTrackedPromise(
{
@ -78,6 +79,8 @@ export const useLogAnalysisJobs = ({
: 'failed'
: 'failed',
}));
setHasCompletedSetup(true);
},
},
[indexPattern, spaceId, sourceId]
@ -139,6 +142,7 @@ export const useLogAnalysisJobs = ({
setupMlModuleRequest,
isSettingUpMlModule,
didSetupFail,
hasCompletedSetup,
};
};

View file

@ -0,0 +1,30 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
export const FirstUseCallout = () => {
return (
<>
<EuiCallOut
color="success"
title={i18n.translate('xpack.infra.logs.logsAnalysisResults.onboardingSuccessTitle', {
defaultMessage: 'Success!',
})}
>
<p>
{i18n.translate('xpack.infra.logs.logsAnalysisResults.onboardingSuccessContent', {
defaultMessage:
'Please allow a few minutes for our machine learning robots to begin collecting data.',
})}
</p>
</EuiCallOut>
<EuiSpacer />
</>
);
};

View file

@ -27,6 +27,7 @@ export const AnalysisPage = () => {
setupMlModule,
isSettingUpMlModule,
didSetupFail,
hasCompletedSetup,
} = useLogAnalysisJobs({
indexPattern: source ? source.configuration.logAlias : '',
sourceId,
@ -63,7 +64,7 @@ export const AnalysisPage = () => {
indexPattern={source ? source.configuration.logAlias : ''}
/>
) : (
<AnalysisResultsContent sourceId={sourceId} />
<AnalysisResultsContent sourceId={sourceId} isFirstUse={hasCompletedSetup} />
)}
</ColumnarPage>
</AnalysisPageProviders>

View file

@ -29,6 +29,7 @@ import { useLogAnalysisResults } from '../../../containers/logs/log_analysis';
import { useLogAnalysisResultsUrlState } from '../../../containers/logs/log_analysis';
import { LoadingPage } from '../../../components/loading_page';
import { LogRateResults } from './sections/log_rate';
import { FirstUseCallout } from './first_use';
const DATE_PICKER_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
@ -42,7 +43,13 @@ const getLoadingState = () => {
);
};
export const AnalysisResultsContent = ({ sourceId }: { sourceId: string }) => {
export const AnalysisResultsContent = ({
sourceId,
isFirstUse,
}: {
sourceId: string;
isFirstUse: boolean;
}) => {
useTrackPageview({ app: 'infra_logs', path: 'analysis_results' });
useTrackPageview({ app: 'infra_logs', path: 'analysis_results', delay: 15000 });
@ -89,6 +96,9 @@ export const AnalysisResultsContent = ({ sourceId }: { sourceId: string }) => {
endTime: timeRange.endTime,
bucketDuration,
});
const hasResults = useMemo(() => logEntryRate && logEntryRate.histogramBuckets.length > 0, [
logEntryRate,
]);
const handleTimeRangeChange = useCallback(
({ start, end }: { start: string; end: string }) => {
const parsedStart = dateMath.parse(start);
@ -182,7 +192,12 @@ export const AnalysisResultsContent = ({ sourceId }: { sourceId: string }) => {
<EuiPageBody>
<EuiPageContent>
<EuiPageContentBody>
<LogRateResults isLoading={isLoading} results={logEntryRate} />
{isFirstUse && !hasResults ? <FirstUseCallout /> : null}
<LogRateResults
isLoading={isLoading}
results={logEntryRate}
timeRange={timeRange}
/>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>

View file

@ -6,7 +6,6 @@
import React, { useMemo, useCallback, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { first, last } from 'lodash';
import moment from 'moment';
import {
Axis,
@ -24,15 +23,17 @@ import { getColorsMap, isDarkMode, getChartTheme } from '../../chart_helpers';
import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate';
import { useLogEntryRateGraphData } from '../../../../../containers/logs/log_analysis/log_analysis_graph_data/log_entry_rate';
import { useKibanaUiSetting } from '../../../../../utils/use_kibana_ui_setting';
import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
const areaSeriesColour = 'rgb(224, 237, 255)';
const lineSeriesColour = 'rgb(49, 133, 252)';
interface Props {
data: GetLogEntryRateSuccessResponsePayload['data'] | null;
timeRange: TimeRange;
}
export const ChartView = ({ data }: Props) => {
export const ChartView = ({ data, timeRange }: Props) => {
const showModelBoundsLabel = i18n.translate(
'xpack.infra.logs.analysis.logRateSectionModelBoundsCheckboxLabel',
{ defaultMessage: 'Show model bounds' }
@ -43,9 +44,9 @@ export const ChartView = ({ data }: Props) => {
const dateFormatter = useMemo(
() =>
lineSeries.length > 0
? niceTimeFormatter([first(lineSeries)[0], last(lineSeries)[0]])
? niceTimeFormatter([timeRange.startTime, timeRange.endTime])
: (value: number) => `${value}`,
[lineSeries]
[lineSeries, timeRange]
);
const areaSpecId = getSpecId('modelBounds');
@ -63,7 +64,6 @@ export const ChartView = ({ data }: Props) => {
};
const [isShowingModelBounds, setIsShowingModelBounds] = useState<boolean>(true);
return (
<>
<EuiFlexGroup justifyContent="flexEnd">
@ -171,7 +171,11 @@ export const ChartView = ({ data }: Props) => {
}
customSeriesColors={!isDarkMode() ? getColorsMap('red', anomalySpecId) : undefined}
/>
<Settings tooltip={tooltipProps} theme={getChartTheme()} />
<Settings
tooltip={tooltipProps}
theme={getChartTheme()}
xDomain={{ min: timeRange.startTime, max: timeRange.endTime }}
/>
</Chart>
</div>
</>

View file

@ -19,13 +19,16 @@ import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/
import { ChartView } from './chart';
import { isValidLogRateView, LogRateView, LogRateViewSwitcher } from './log_rate_view_switcher';
import { TableView } from './table';
import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
export const LogRateResults = ({
isLoading,
results,
timeRange,
}: {
isLoading: boolean;
results: GetLogEntryRateSuccessResponsePayload['data'] | null;
timeRange: TimeRange;
}) => {
const title = i18n.translate('xpack.infra.logs.analysis.logRateSectionTitle', {
defaultMessage: 'Log rate',
@ -63,8 +66,7 @@ export const LogRateResults = ({
body={
<p>
{i18n.translate('xpack.infra.logs.analysis.logRateSectionNoDataBody', {
defaultMessage:
'Please allow a few minutes for our machine learning robots to begin collecting data. If you expect data to be here already, you may want to adjust your time range.',
defaultMessage: 'You may want to adjust your time range.',
})}
</p>
}
@ -80,7 +82,11 @@ export const LogRateResults = ({
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="l" />
{viewMode === 'chart' ? <ChartView data={results} /> : <TableView data={results} />}
{viewMode === 'chart' ? (
<ChartView data={results} timeRange={timeRange} />
) : (
<TableView data={results} />
)}
</>
)}
</>