-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[APM] Adds 'Anomaly detection' settings page to create ML jobs per en…
…vironment (#70560) * Adds 'Anomaly detection' settings page along with require API endpoints to list and create the apm anomaly detection jobs per environment. Some test data is hardcoded while the the required changes in the ML plugin are in flight. * Converts the environment name to a compatible ML id string and persist in groups array. Also adds random token to the job ID to prevent collisions for job ids where diffferent environment names convert to the same string * - Improve job creation with latest updates for the `apm_transaction` ML module - Implements job list in settings by reading from `custom_settings.job_tags['service.environment']` - Add ML module method `createModuleItem` for job configuration - Don't allow user to type in duplicate environments * Update x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx Co-authored-by: Casper Hübertz <[email protected]> * Update x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx Co-authored-by: Casper Hübertz <[email protected]> * UX feedback, adds i18n, and handles failed state for ML jobs fetch. * - Moves get_all_environments from agent_configuration dir to common dir - makes the 'all' environment name ALL_OPTION_VALUE agent configuration-specific - replace field literals with constants * PR feedback * Adds support to create jobs for environment which are not defined. * Fixes description copy, rearranges settings links, and makes sure the 'Not defined' option is disabled if it already exists. * Only show "Not defined" in environment selector if there are actually documents without service.environment set * get the indexPatternName for the ML job from the set of user-definned indices * updated job_tags type definition Co-authored-by: Casper Hübertz <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
- Loading branch information
1 parent
aa99a70
commit 7d44d02
Showing
21 changed files
with
906 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/* | ||
* 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, { useState } from 'react'; | ||
import { | ||
EuiPanel, | ||
EuiTitle, | ||
EuiText, | ||
EuiSpacer, | ||
EuiButton, | ||
EuiButtonEmpty, | ||
EuiComboBox, | ||
EuiComboBoxOptionOption, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiFormRow, | ||
} from '@elastic/eui'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher'; | ||
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; | ||
import { createJobs } from './create_jobs'; | ||
import { ENVIRONMENT_NOT_DEFINED } from '../../../../../common/environment_filter_values'; | ||
|
||
interface Props { | ||
currentEnvironments: string[]; | ||
onCreateJobSuccess: () => void; | ||
onCancel: () => void; | ||
} | ||
export const AddEnvironments = ({ | ||
currentEnvironments, | ||
onCreateJobSuccess, | ||
onCancel, | ||
}: Props) => { | ||
const { toasts } = useApmPluginContext().core.notifications; | ||
const { data = [], status } = useFetcher( | ||
(callApmApi) => | ||
callApmApi({ | ||
pathname: `/api/apm/settings/anomaly-detection/environments`, | ||
}), | ||
[], | ||
{ preservePreviousData: false } | ||
); | ||
|
||
const environmentOptions = data.map((env) => ({ | ||
label: env === ENVIRONMENT_NOT_DEFINED ? NOT_DEFINED_OPTION_LABEL : env, | ||
value: env, | ||
disabled: currentEnvironments.includes(env), | ||
})); | ||
|
||
const [selectedOptions, setSelected] = useState< | ||
Array<EuiComboBoxOptionOption<string>> | ||
>([]); | ||
|
||
const isLoading = | ||
status === FETCH_STATUS.PENDING || status === FETCH_STATUS.LOADING; | ||
return ( | ||
<EuiPanel> | ||
<EuiTitle> | ||
<h2> | ||
{i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.titleText', | ||
{ | ||
defaultMessage: 'Select environments', | ||
} | ||
)} | ||
</h2> | ||
</EuiTitle> | ||
<EuiSpacer size="l" /> | ||
<EuiText> | ||
{i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.descriptionText', | ||
{ | ||
defaultMessage: | ||
'Select the service environments that you want to enable anomaly detection in. Anomalies will surface for all services and transaction types within the selected environments.', | ||
} | ||
)} | ||
</EuiText> | ||
<EuiSpacer size="l" /> | ||
<EuiFormRow | ||
label={i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.selectorLabel', | ||
{ | ||
defaultMessage: 'Environments', | ||
} | ||
)} | ||
fullWidth | ||
> | ||
<EuiComboBox | ||
isLoading={isLoading} | ||
placeholder={i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.selectorPlaceholder', | ||
{ | ||
defaultMessage: 'Select or add environments', | ||
} | ||
)} | ||
options={environmentOptions} | ||
selectedOptions={selectedOptions} | ||
onChange={(nextSelectedOptions) => { | ||
setSelected(nextSelectedOptions); | ||
}} | ||
onCreateOption={(searchValue) => { | ||
if (currentEnvironments.includes(searchValue)) { | ||
return; | ||
} | ||
const newOption = { | ||
label: searchValue, | ||
value: searchValue, | ||
}; | ||
setSelected([...selectedOptions, newOption]); | ||
}} | ||
isClearable={true} | ||
/> | ||
</EuiFormRow> | ||
<EuiFlexGroup justifyContent="flexEnd"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButtonEmpty aria-label="Cancel" onClick={onCancel}> | ||
{i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.cancelButtonText', | ||
{ | ||
defaultMessage: 'Cancel', | ||
} | ||
)} | ||
</EuiButtonEmpty> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton | ||
fill | ||
disabled={selectedOptions.length === 0} | ||
onClick={async () => { | ||
const selectedEnvironments = selectedOptions.map( | ||
({ value }) => value as string | ||
); | ||
const success = await createJobs({ | ||
environments: selectedEnvironments, | ||
toasts, | ||
}); | ||
if (success) { | ||
onCreateJobSuccess(); | ||
} | ||
}} | ||
> | ||
{i18n.translate( | ||
'xpack.apm.settings.anomalyDetection.addEnvironments.createJobsButtonText', | ||
{ | ||
defaultMessage: 'Create Jobs', | ||
} | ||
)} | ||
</EuiButton> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
<EuiSpacer size="l" /> | ||
</EuiPanel> | ||
); | ||
}; | ||
|
||
const NOT_DEFINED_OPTION_LABEL = i18n.translate( | ||
'xpack.apm.filter.environment.notDefinedLabel', | ||
{ | ||
defaultMessage: 'Not defined', | ||
} | ||
); |
64 changes: 64 additions & 0 deletions
64
x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* 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 { i18n } from '@kbn/i18n'; | ||
import { NotificationsStart } from 'kibana/public'; | ||
import { callApmApi } from '../../../../services/rest/createCallApmApi'; | ||
|
||
export async function createJobs({ | ||
environments, | ||
toasts, | ||
}: { | ||
environments: string[]; | ||
toasts: NotificationsStart['toasts']; | ||
}) { | ||
try { | ||
await callApmApi({ | ||
pathname: '/api/apm/settings/anomaly-detection/jobs', | ||
method: 'POST', | ||
params: { | ||
body: { environments }, | ||
}, | ||
}); | ||
|
||
toasts.addSuccess({ | ||
title: i18n.translate( | ||
'xpack.apm.anomalyDetection.createJobs.succeeded.title', | ||
{ defaultMessage: 'Anomaly detection jobs created' } | ||
), | ||
text: i18n.translate( | ||
'xpack.apm.anomalyDetection.createJobs.succeeded.text', | ||
{ | ||
defaultMessage: | ||
'Anomaly detection jobs successfully created for APM service environments [{environments}]. It will take some time for machine learning to start analyzing traffic for anomalies.', | ||
values: { environments: environments.join(', ') }, | ||
} | ||
), | ||
}); | ||
return true; | ||
} catch (error) { | ||
toasts.addDanger({ | ||
title: i18n.translate( | ||
'xpack.apm.anomalyDetection.createJobs.failed.title', | ||
{ | ||
defaultMessage: 'Anomaly detection jobs could not be created', | ||
} | ||
), | ||
text: i18n.translate( | ||
'xpack.apm.anomalyDetection.createJobs.failed.text', | ||
{ | ||
defaultMessage: | ||
'Something went wrong when creating one ore more anomaly detection jobs for APM service environments [{environments}]. Error: "{errorMessage}"', | ||
values: { | ||
environments: environments.join(', '), | ||
errorMessage: error.message, | ||
}, | ||
} | ||
), | ||
}); | ||
return false; | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* 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, { useState } from 'react'; | ||
import { EuiTitle, EuiSpacer, EuiText } from '@elastic/eui'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { JobsList } from './jobs_list'; | ||
import { AddEnvironments } from './add_environments'; | ||
import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher'; | ||
|
||
export const AnomalyDetection = () => { | ||
const [viewAddEnvironments, setViewAddEnvironments] = useState(false); | ||
|
||
const { refetch, data = [], status } = useFetcher( | ||
(callApmApi) => | ||
callApmApi({ pathname: `/api/apm/settings/anomaly-detection` }), | ||
[], | ||
{ preservePreviousData: false } | ||
); | ||
|
||
const isLoading = | ||
status === FETCH_STATUS.PENDING || status === FETCH_STATUS.LOADING; | ||
const hasFetchFailure = status === FETCH_STATUS.FAILURE; | ||
|
||
return ( | ||
<> | ||
<EuiTitle size="l"> | ||
<h1> | ||
{i18n.translate('xpack.apm.settings.anomalyDetection.titleText', { | ||
defaultMessage: 'Anomaly detection', | ||
})} | ||
</h1> | ||
</EuiTitle> | ||
<EuiSpacer size="l" /> | ||
<EuiText> | ||
{i18n.translate('xpack.apm.settings.anomalyDetection.descriptionText', { | ||
defaultMessage: | ||
'The Machine Learning anomaly detection integration enables application health status indicators in the Service map by identifying transaction duration anomalies.', | ||
})} | ||
</EuiText> | ||
<EuiSpacer size="l" /> | ||
{viewAddEnvironments ? ( | ||
<AddEnvironments | ||
currentEnvironments={data.map(({ environment }) => environment)} | ||
onCreateJobSuccess={() => { | ||
refetch(); | ||
setViewAddEnvironments(false); | ||
}} | ||
onCancel={() => { | ||
setViewAddEnvironments(false); | ||
}} | ||
/> | ||
) : ( | ||
<JobsList | ||
isLoading={isLoading} | ||
hasFetchFailure={hasFetchFailure} | ||
anomalyDetectionJobsByEnv={data} | ||
onAddEnvironments={() => { | ||
setViewAddEnvironments(true); | ||
}} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; |
Oops, something went wrong.