-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[APM] Service maps anomaly detection integration by environment (#70932)
* Closes #69480 & #70419. - Adds anomaly detection integration to service maps backed by apm ML jobs per environment - Loads transaction stats and anomalies for each transaction types - Renders a selector in the popop to choose a transaction type to view stats * - implements original anomaly detection integration design for service maps popover - only aggregates transaction KPIs and anomaly scores for transaction.type = "request" or "page-load" - supports environment filter 'All' option to display data from all APM anomaly detection jobs - handle case where popover metrics don't exist for services outside the current environment filter * fixes some CI errors * Simplified messaging for service popop with not data in the current environment * PR feedback, renamed max anomalies -> service anomalies including the file name * - defines custom_settings.job_tags.apm_ml_version in ML job creation, then filters for it when returing valid APM ML jobs * changes shape of of service anomalies from an array to a object keyed by serviceName * removes the url encoding from ML job link href to how it was previously. * PR feedback * Popover no data state simplified: - renders the "no data" message as plain text instead of in a callout - hides the 'Anomaly detection' section if there is not anomaly data. * Fixes filtering bug when user selects 'Environment: Not defined'. Now filters properly by filtering for docs where service.environment does not exist * filters jobs fetched in the settings page by `job.custom_settings.job_tags.apm_ml_version` * Fixed bad import from last upstream merge Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
- Loading branch information
1 parent
77a97fc
commit b302565
Showing
19 changed files
with
659 additions
and
120 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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 interface ServiceAnomalyStats { | ||
transactionType?: string; | ||
anomalyScore?: number; | ||
actualValue?: number; | ||
jobId?: string; | ||
} |
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
158 changes: 158 additions & 0 deletions
158
x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.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,158 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import styled from 'styled-components'; | ||
import { | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiTitle, | ||
EuiIconTip, | ||
EuiHealth, | ||
} from '@elastic/eui'; | ||
import { useTheme } from '../../../../hooks/useTheme'; | ||
import { fontSize, px } from '../../../../style/variables'; | ||
import { asInteger, asDuration } from '../../../../utils/formatters'; | ||
import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink'; | ||
import { getSeverityColor, popoverWidth } from '../cytoscapeOptions'; | ||
import { getSeverity } from '../../../../../common/ml_job_constants'; | ||
import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types'; | ||
import { ServiceAnomalyStats } from '../../../../../common/anomaly_detection'; | ||
|
||
const HealthStatusTitle = styled(EuiTitle)` | ||
display: inline; | ||
text-transform: uppercase; | ||
`; | ||
|
||
const VerticallyCentered = styled.div` | ||
display: flex; | ||
align-items: center; | ||
`; | ||
|
||
const SubduedText = styled.span` | ||
color: ${({ theme }) => theme.eui.euiTextSubduedColor}; | ||
`; | ||
|
||
const EnableText = styled.section` | ||
color: ${({ theme }) => theme.eui.euiTextSubduedColor}; | ||
line-height: 1.4; | ||
font-size: ${fontSize}; | ||
width: ${px(popoverWidth)}; | ||
`; | ||
|
||
export const ContentLine = styled.section` | ||
line-height: 2; | ||
`; | ||
|
||
interface Props { | ||
serviceName: string; | ||
serviceAnomalyStats: ServiceAnomalyStats | undefined; | ||
} | ||
export function AnomalyDetection({ serviceName, serviceAnomalyStats }: Props) { | ||
const theme = useTheme(); | ||
|
||
const anomalyScore = serviceAnomalyStats?.anomalyScore; | ||
const anomalySeverity = getSeverity(anomalyScore); | ||
const actualValue = serviceAnomalyStats?.actualValue; | ||
const mlJobId = serviceAnomalyStats?.jobId; | ||
const transactionType = | ||
serviceAnomalyStats?.transactionType ?? TRANSACTION_REQUEST; | ||
const hasAnomalyDetectionScore = anomalyScore !== undefined; | ||
|
||
return ( | ||
<> | ||
<section> | ||
<HealthStatusTitle size="xxs"> | ||
<h3>{ANOMALY_DETECTION_TITLE}</h3> | ||
</HealthStatusTitle> | ||
| ||
<EuiIconTip type="iInCircle" content={ANOMALY_DETECTION_TOOLTIP} /> | ||
{!mlJobId && <EnableText>{ANOMALY_DETECTION_DISABLED_TEXT}</EnableText>} | ||
</section> | ||
{hasAnomalyDetectionScore && ( | ||
<ContentLine> | ||
<EuiFlexGroup> | ||
<EuiFlexItem> | ||
<VerticallyCentered> | ||
<EuiHealth color={getSeverityColor(theme, anomalySeverity)} /> | ||
<SubduedText>{ANOMALY_DETECTION_SCORE_METRIC}</SubduedText> | ||
</VerticallyCentered> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<div> | ||
{getDisplayedAnomalyScore(anomalyScore as number)} | ||
{actualValue && ( | ||
<SubduedText> ({asDuration(actualValue)})</SubduedText> | ||
)} | ||
</div> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</ContentLine> | ||
)} | ||
{mlJobId && !hasAnomalyDetectionScore && ( | ||
<EnableText>{ANOMALY_DETECTION_NO_DATA_TEXT}</EnableText> | ||
)} | ||
{mlJobId && ( | ||
<ContentLine> | ||
<MLJobLink | ||
external | ||
jobId={mlJobId} | ||
serviceName={serviceName} | ||
transactionType={transactionType} | ||
> | ||
{ANOMALY_DETECTION_LINK} | ||
</MLJobLink> | ||
</ContentLine> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
function getDisplayedAnomalyScore(score: number) { | ||
if (score > 0 && score < 1) { | ||
return '< 1'; | ||
} | ||
return asInteger(score); | ||
} | ||
|
||
const ANOMALY_DETECTION_TITLE = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverTitle', | ||
{ defaultMessage: 'Anomaly Detection' } | ||
); | ||
|
||
const ANOMALY_DETECTION_TOOLTIP = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverTooltip', | ||
{ | ||
defaultMessage: | ||
'Service health indicators are powered by the anomaly detection feature in machine learning', | ||
} | ||
); | ||
|
||
const ANOMALY_DETECTION_SCORE_METRIC = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverScoreMetric', | ||
{ defaultMessage: 'Score (max.)' } | ||
); | ||
|
||
const ANOMALY_DETECTION_LINK = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverLink', | ||
{ defaultMessage: 'View anomalies' } | ||
); | ||
|
||
const ANOMALY_DETECTION_DISABLED_TEXT = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverDisabled', | ||
{ | ||
defaultMessage: | ||
'Display service health indicators by enabling anomaly detection in APM settings.', | ||
} | ||
); | ||
|
||
const ANOMALY_DETECTION_NO_DATA_TEXT = i18n.translate( | ||
'xpack.apm.serviceMap.anomalyDetectionPopoverNoData', | ||
{ | ||
defaultMessage: `We couldn't find an anomaly score within the selected time range. See details in the anomaly explorer.`, | ||
} | ||
); |
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
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
Oops, something went wrong.