Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Uptime] Ml detection of duration anomalies #59785

Merged
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
b8f4e50
add flyout
shahzad31 Feb 11, 2020
3a9d5ff
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 11, 2020
eedef4f
add state
shahzad31 Feb 11, 2020
315008e
update state
shahzad31 Feb 12, 2020
28e0bcc
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 12, 2020
3822ad5
ad job
shahzad31 Feb 12, 2020
6990a0e
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 13, 2020
c3d8d69
update
shahzad31 Feb 13, 2020
3601e2f
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 13, 2020
8c5412f
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 14, 2020
dedfc71
updat
shahzad31 Feb 14, 2020
f41b944
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 20, 2020
ecc1453
add ml analyze button
shahzad31 Feb 21, 2020
aa85eca
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 21, 2020
fad528e
update api
shahzad31 Feb 21, 2020
76dea0c
use differential colors for duration chart
shahzad31 Feb 21, 2020
7db5295
remove duration chart gql
shahzad31 Feb 24, 2020
b0bd131
update type
shahzad31 Feb 24, 2020
251941b
type fix
shahzad31 Feb 24, 2020
448bcd8
fix tyoe
shahzad31 Feb 24, 2020
03e130e
Merge branch 'master' into feature/improve-duration-chart
shahzad31 Feb 24, 2020
646570e
Merge branch 'master' into feature/improve-duration-chart
shahzad31 Feb 25, 2020
be5cbe9
update translation
shahzad31 Feb 25, 2020
0170326
update test
shahzad31 Feb 25, 2020
8d81cb0
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 25, 2020
b50c13b
Merge branch 'master' into feature/improve-duration-chart
shahzad31 Feb 25, 2020
a582794
update conflicts
shahzad31 Feb 25, 2020
43022f0
Merge branch 'feature/improve-duration-chart' into ml-detecttion-of-d…
shahzad31 Feb 25, 2020
ebf18bd
update anomaly record
shahzad31 Feb 25, 2020
3f2cd84
chart
shahzad31 Feb 26, 2020
a0b71a6
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Feb 27, 2020
b86d4ce
added annotations
shahzad31 Mar 1, 2020
4fe5e9c
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 2, 2020
925ec4d
update error handling
shahzad31 Mar 2, 2020
d6210d3
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 3, 2020
c344852
update
shahzad31 Mar 3, 2020
d0d95fe
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 3, 2020
2e89bf6
update types
shahzad31 Mar 4, 2020
07c4134
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 4, 2020
e203366
fixed types
shahzad31 Mar 5, 2020
0ee23d2
fix types
shahzad31 Mar 5, 2020
91ad10b
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 5, 2020
cbf8681
update types
shahzad31 Mar 5, 2020
dc7b612
update
shahzad31 Mar 6, 2020
25989b5
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 6, 2020
80ce3c4
update
shahzad31 Mar 6, 2020
7d36063
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 10, 2020
3b0280b
remove unnecessary change
shahzad31 Mar 10, 2020
b50c0fd
remove unnecessary change
shahzad31 Mar 10, 2020
9e4a9d2
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 10, 2020
bd0df8e
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 11, 2020
bc18845
fix type
shahzad31 Mar 11, 2020
540312b
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 11, 2020
d5ddb2e
update
shahzad31 Mar 13, 2020
9552458
save
shahzad31 Mar 13, 2020
f500fb5
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 16, 2020
21ec018
update pr
shahzad31 Mar 17, 2020
6d697ba
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 17, 2020
e9dd066
update tets
shahzad31 Mar 17, 2020
877c5c1
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 18, 2020
35a4efc
update job deletion
shahzad31 Mar 18, 2020
ab0f14d
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 18, 2020
e2ff2bf
update
shahzad31 Mar 18, 2020
203d3f9
update tets
shahzad31 Mar 19, 2020
42578ef
upadte tests
shahzad31 Mar 19, 2020
3266764
fix types
shahzad31 Mar 19, 2020
7aed440
update title text
shahzad31 Mar 19, 2020
29912ed
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 19, 2020
36d1955
update types
shahzad31 Mar 19, 2020
f85def8
fixed tests
shahzad31 Mar 19, 2020
45d5e14
update tests and types
shahzad31 Mar 20, 2020
2b1b63e
updated types
shahzad31 Mar 20, 2020
ee2cba5
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 20, 2020
3460492
fix PR feedback
shahzad31 Mar 22, 2020
c5e52d1
unit test
shahzad31 Mar 22, 2020
6223e21
update more types
shahzad31 Mar 22, 2020
64e07f6
update test and manage job
shahzad31 Mar 23, 2020
8248469
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 23, 2020
b323b8c
resolve conflicts
shahzad31 Mar 23, 2020
029449f
types
shahzad31 Mar 23, 2020
68cfc26
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 23, 2020
a7af917
remove unnecessary change
shahzad31 Mar 23, 2020
b6212d8
revert ml code
shahzad31 Mar 23, 2020
3e50e16
revert ml code
shahzad31 Mar 23, 2020
8d3feec
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 23, 2020
6eade46
fixed formatting issues pointed by pr feedback
shahzad31 Mar 23, 2020
2750e60
Merge branch 'master' into ml-detecttion-of-duration-anamolies
shahzad31 Mar 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/plugins/kibana_react/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export * from './field_icon';
export * from './table_list_view';
export * from './split_panel';
export { ValidatedDualRange } from './validated_range';
export * from './notifications';
export { Markdown, MarkdownSimple } from './markdown';
export { reactToUiComponent, uiToReactComponent } from './adapters';
export { useUrlTracker } from './use_url_tracker';
Expand Down
6 changes: 6 additions & 0 deletions x-pack/legacy/plugins/uptime/common/constants/rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ export enum API_URLS {
PING_HISTOGRAM = `/api/uptime/ping/histogram`,
SNAPSHOT_COUNT = `/api/uptime/snapshot/count`,
FILTERS = `/api/uptime/filters`,

ML_MODULE_JOBS = `/api/ml/modules/jobs_exist/`,
ML_SETUP_MODULE = '/api/ml/modules/setup/',
ML_DELETE_JOB = `/api/ml/jobs/delete_jobs`,
ML_CAPABILITIES = '/api/ml/ml_capabilities',
ML_ANOMALIES_RESULT = `/api/ml/results/anomalies_table_data`,
}
4 changes: 4 additions & 0 deletions x-pack/legacy/plugins/uptime/common/constants/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export enum STATUS {
DOWN = 'down',
}

export const ML_JOB_ID = 'high_latency_by_geo';

export const ML_MODULE_ID = 'uptime_heartbeat';

export const UNNAMED_LOCATION = 'Unnamed-location';

export const SHORT_TS_LOCALE = 'en-short-locale';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,80 @@
import React, { useContext, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useUrlParams } from '../../../hooks';
import { getMonitorDurationAction } from '../../../state/actions';
import {
getAnomalyRecordsAction,
getMLCapabilitiesAction,
getMonitorDurationAction,
} from '../../../state/actions';
import { DurationChartComponent } from '../../functional/charts';
import { selectDurationLines } from '../../../state/selectors';
import {
anomaliesSelector,
hasMLFeatureAvailable,
hasMLJobSelector,
selectDurationLines,
} from '../../../state/selectors';
import { UptimeRefreshContext } from '../../../contexts';
import { getMLJobId } from '../../../state/api/ml_anomaly';
import { JobStat } from '../../../../../../../plugins/ml/common/types/anomaly_detection_jobs';

interface Props {
monitorId: string;
}

export const DurationChart: React.FC<Props> = ({ monitorId }: Props) => {
const [getUrlParams] = useUrlParams();
const { dateRangeStart, dateRangeEnd } = getUrlParams();
const {
dateRangeStart,
dateRangeEnd,
absoluteDateRangeStart,
absoluteDateRangeEnd,
} = getUrlParams();

const { monitor_duration, loading } = useSelector(selectDurationLines);
const { durationLines, loading } = useSelector(selectDurationLines);

const isMLAvailable = useSelector(hasMLFeatureAvailable);

const { data: mlJobs } = useSelector(hasMLJobSelector);

const hasMLJob =
!!mlJobs?.jobsExist &&
!!mlJobs.jobs.find((job: JobStat) => job.id === getMLJobId(monitorId as string));

const anomalies = useSelector(anomaliesSelector);

const dispatch = useDispatch();

const { lastRefresh } = useContext(UptimeRefreshContext);

useEffect(() => {
dispatch(
getMonitorDurationAction({ monitorId, dateStart: dateRangeStart, dateEnd: dateRangeEnd })
);
if (isMLAvailable) {
const anomalyParams = {
listOfMonitorIds: [monitorId],
dateStart: absoluteDateRangeStart,
dateEnd: absoluteDateRangeEnd,
};

dispatch(getAnomalyRecordsAction.get(anomalyParams));
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dateRangeStart, dateRangeEnd, dispatch, lastRefresh, monitorId, isMLAvailable]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}, [dateRangeStart, dateRangeEnd, dispatch, lastRefresh, monitorId, isMLAvailable]);
}, [absoluteDateRangeStart, absoluteDateRangeEnd, dispatch, lastRefresh, monitorId, isMLAvailable]);

We don't seem to need dateRangeStart and dateRangeEnd in here anymore.

I'm assuming adding getAnomalyRecordsAction causes a render loop if it's included? Would it help to wrap the dispatch in a useCallback?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i have added them dateRangeStart, dateRangeEnd to make sure date change is captured and can't add absoluteDateRangeStart, absoluteDateRangeEnd, because it will result in endless loop.


useEffect(() => {
const params = { monitorId, dateStart: dateRangeStart, dateEnd: dateRangeEnd };
dispatch(getMonitorDurationAction(params));
}, [dateRangeStart, dateRangeEnd, dispatch, lastRefresh, monitorId]);

useEffect(() => {
dispatch(getMLCapabilitiesAction.get());
}, [dispatch]);

return (
<DurationChartComponent
locationDurationLines={monitor_duration?.locationDurationLines ?? []}
anomalies={anomalies}
hasMLJob={hasMLJob}
loading={loading}
locationDurationLines={durationLines?.locationDurationLines ?? []}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { indexStatusSelector } from '../../../state/selectors';
import { EmptyStateComponent } from '../../functional/empty_state/empty_state';

export const EmptyState: React.FC = ({ children }) => {
const { data, loading, errors } = useSelector(indexStatusSelector);
const { data, loading, error } = useSelector(indexStatusSelector);

const dispatch = useDispatch();

Expand All @@ -23,7 +23,7 @@ export const EmptyState: React.FC = ({ children }) => {
<EmptyStateComponent
statesIndexStatus={data}
loading={loading}
errors={errors}
errors={error ? [error] : undefined}
children={children as React.ReactElement}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ interface StateProps {
}

interface DispatchProps {
loadMonitorStatus: (dateStart: string, dateEnd: string, monitorId: string) => void;
loadMonitorStatus: typeof getMonitorStatusAction;
loadSelectedMonitor: typeof getSelectedMonitorAction;
}

interface OwnProps {
Expand All @@ -33,6 +34,7 @@ type Props = OwnProps & StateProps & DispatchProps;

const Container: React.FC<Props> = ({
loadMonitorStatus,
loadSelectedMonitor,
monitorId,
monitorStatus,
monitorLocations,
Expand All @@ -43,8 +45,9 @@ const Container: React.FC<Props> = ({
const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams();

useEffect(() => {
loadMonitorStatus(dateStart, dateEnd, monitorId);
}, [monitorId, dateStart, dateEnd, loadMonitorStatus, lastRefresh]);
loadMonitorStatus({ dateStart, dateEnd, monitorId });
loadSelectedMonitor({ monitorId });
}, [monitorId, dateStart, dateEnd, loadMonitorStatus, lastRefresh, loadSelectedMonitor]);

return (
<MonitorStatusBarComponent
Expand All @@ -61,20 +64,8 @@ const mapStateToProps = (state: AppState, ownProps: OwnProps) => ({
});

const mapDispatchToProps = (dispatch: Dispatch<any>): DispatchProps => ({
loadMonitorStatus: (dateStart: string, dateEnd: string, monitorId: string) => {
dispatch(
getMonitorStatusAction({
monitorId,
dateStart,
dateEnd,
})
);
dispatch(
getSelectedMonitorAction({
monitorId,
})
);
},
loadSelectedMonitor: params => dispatch(getSelectedMonitorAction(params)),
loadMonitorStatus: params => dispatch(getMonitorStatusAction(params)),
});

// @ts-ignore TODO: Investigate typescript issues here
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ describe('MonitorCharts component', () => {
it('renders the component without errors', () => {
const component = shallowWithRouter(
<DurationChartComponent
locationDurationLines={chartResponse.monitorChartsData.locationDurationLines}
loading={false}
hasMLJob={false}
anomalies={[]}
locationDurationLines={chartResponse.monitorChartsData.locationDurationLines}
/>
);
expect(component).toMatchSnapshot();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 moment from 'moment';
import styled from 'styled-components';

const Header = styled.div`
font-weight: bold;
padding-left: 4px;
`;

const RecordSeverity = styled.div`
font-weight: bold;
border-left: 4px solid ${props => props.color};
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
padding-left: 2px;
`;

const TimeDiv = styled.div`
font-weight: 500;
border-bottom: 1px solid gray;
padding-bottom: 2px;
`;

export const AnnotationTooltip = ({ details }: { details: string }) => {
const data = JSON.parse(details);

function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

return (
<>
<TimeDiv>{moment(data.time).format('lll')}</TimeDiv>
<Header>Score: {data.score.toFixed(2)}</Header>
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
<RecordSeverity color={data.color}>
Severity: {capitalizeFirstLetter(data.severity)}
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
</RecordSeverity>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Axis, Chart, Position, timeFormatter, Settings } from '@elastic/charts';
import { EuiPanel, EuiTitle } from '@elastic/eui';
import React from 'react';
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import moment from 'moment';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
import { Axis, Chart, Position, timeFormatter, Settings } from '@elastic/charts';
import { SeriesIdentifier } from '@elastic/charts/dist/chart_types/xy_chart/utils/series';
import { getChartDateLabel } from '../../../lib/helper';
import { LocationDurationLine } from '../../../../common/types';
import { DurationLineSeriesList } from './duration_line_series_list';
import { ChartWrapper } from './chart_wrapper';
import { useUrlParams } from '../../../hooks';
import { getTickFormat } from './get_tick_format';
import { ChartEmptyState } from './chart_empty_state';
import { DurationAnomaliesBar } from './duration_line_bar_list';
import { MLIntegrationComponent } from '../../monitor_details/ml/ml_integeration';

interface DurationChartProps {
/**
Expand All @@ -29,6 +32,10 @@ interface DurationChartProps {
* To represent the loading spinner on chart
*/
loading: boolean;

hasMLJob: boolean;

anomalies: any;
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -37,29 +44,66 @@ interface DurationChartProps {
* milliseconds.
* @param props The props required for this component to render properly
*/
export const DurationChartComponent = ({ locationDurationLines, loading }: DurationChartProps) => {
export const DurationChartComponent = ({
locationDurationLines,
anomalies,
loading,
hasMLJob,
}: DurationChartProps) => {
const hasLines = locationDurationLines.length > 0;
const [getUrlParams, updateUrlParams] = useUrlParams();
const { absoluteDateRangeStart: min, absoluteDateRangeEnd: max } = getUrlParams();

const [hiddenLegends, setHiddenLegends] = useState<string[]>([]);

const onBrushEnd = (minX: number, maxX: number) => {
updateUrlParams({
dateRangeStart: moment(minX).toISOString(),
dateRangeEnd: moment(maxX).toISOString(),
});
};

const legendToggleVisibility = (legendItem: SeriesIdentifier | null) => {
if (legendItem) {
setHiddenLegends(prevState => {
if (prevState.includes(legendItem.specId)) {
return [...prevState.filter(item => item !== legendItem.specId)];
} else {
return [...prevState, legendItem.specId];
}
});
}
};

return (
<>
<EuiPanel paddingSize="m">
<EuiTitle size="xs">
<h4>
<FormattedMessage
id="xpack.uptime.monitorCharts.monitorDuration.titleLabel"
defaultMessage="Monitor duration"
description="The 'ms' is an abbreviation for milliseconds."
/>
</h4>
</EuiTitle>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="xs">
<h4>
{hasMLJob ? (
<FormattedMessage
id="xpack.uptime.monitorCharts.monitorDuration.titleLabelWithAnomaly"
defaultMessage="Monitor duration (Anomalies: {noOfAnomalies})"
description="The 'ms' is an abbreviation for milliseconds."
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
values={{ noOfAnomalies: anomalies?.anomalies?.length ?? 0 }}
/>
) : (
<FormattedMessage
id="xpack.uptime.monitorCharts.monitorDuration.titleLabel"
defaultMessage="Monitor duration"
description="The 'ms' is an abbreviation for milliseconds."
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<MLIntegrationComponent />
</EuiFlexItem>
</EuiFlexGroup>

<ChartWrapper height="400px" loading={loading}>
{hasLines ? (
<Chart>
Expand All @@ -68,6 +112,7 @@ export const DurationChartComponent = ({ locationDurationLines, loading }: Durat
showLegend={true}
legendPosition={Position.Bottom}
onBrushEnd={onBrushEnd}
onLegendItemClick={legendToggleVisibility}
/>
<Axis
id="bottom"
Expand All @@ -88,6 +133,7 @@ export const DurationChartComponent = ({ locationDurationLines, loading }: Durat
})}
/>
<DurationLineSeriesList lines={locationDurationLines} />
<DurationAnomaliesBar anomalies={anomalies} hiddenLegends={hiddenLegends} />
</Chart>
) : (
<ChartEmptyState
Expand Down
Loading