Skip to content

Commit

Permalink
[8.x] [Alert details page] Add view logs to each condition for the cu…
Browse files Browse the repository at this point in the history
…stom threshold rule (#193839) (#194487)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Alert details page] Add view logs to each condition for the custom
threshold rule (#193839)](#193839)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Maryam
Saeidi","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-09-30T20:35:21Z","message":"[Alert
details page] Add view logs to each condition for the custom threshold
rule (#193839)\n\nCloses #188091\r\n\r\n## Summary\r\n\r\nThis PR moves
the view log link to each condition
chart:\r\n\r\n|Before|After|\r\n|---|---|\r\n\r\n|![image](https://github.com/user-attachments/assets/534f607b-a780-4d1e-b6e2-f5db3220547d)|![image](https://github.com/user-attachments/assets/b413234f-0ee8-48fd-83d2-8ad20e35f1c9))|","sha":"d89cac22d243d4461fcba3ce87b913ac582f4a44","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-management","Feature:
Custom threshold","rca"],"title":"[Alert details page] Add view logs to
each condition for the custom threshold
rule","number":193839,"url":"https://github.com/elastic/kibana/pull/193839","mergeCommit":{"message":"[Alert
details page] Add view logs to each condition for the custom threshold
rule (#193839)\n\nCloses #188091\r\n\r\n## Summary\r\n\r\nThis PR moves
the view log link to each condition
chart:\r\n\r\n|Before|After|\r\n|---|---|\r\n\r\n|![image](https://github.com/user-attachments/assets/534f607b-a780-4d1e-b6e2-f5db3220547d)|![image](https://github.com/user-attachments/assets/b413234f-0ee8-48fd-83d2-8ad20e35f1c9))|","sha":"d89cac22d243d4461fcba3ce87b913ac582f4a44"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193839","number":193839,"mergeCommit":{"message":"[Alert
details page] Add view logs to each condition for the custom threshold
rule (#193839)\n\nCloses #188091\r\n\r\n## Summary\r\n\r\nThis PR moves
the view log link to each condition
chart:\r\n\r\n|Before|After|\r\n|---|---|\r\n\r\n|![image](https://github.com/user-attachments/assets/534f607b-a780-4d1e-b6e2-f5db3220547d)|![image](https://github.com/user-attachments/assets/b413234f-0ee8-48fd-83d2-8ad20e35f1c9))|","sha":"d89cac22d243d4461fcba3ce87b913ac582f4a44"}}]}]
BACKPORT-->

Co-authored-by: Maryam Saeidi <[email protected]>
  • Loading branch information
kibanamachine and maryam-saeidi authored Sep 30, 2024
1 parent 84d4a5a commit f20444a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 128 deletions.

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 @@ -6,7 +6,6 @@
*/

import React from 'react';
import { EuiLink } from '@elastic/eui';
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import { coreMock as mockCoreMock } from '@kbn/core/public/mocks';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
Expand Down Expand Up @@ -77,7 +76,6 @@ jest.mock('../../../../utils/kibana_react', () => ({

describe('AlertDetailsAppSection', () => {
const queryClient = new QueryClient();
const mockedSetAlertSummaryFields = jest.fn();
const mockedSetRelatedAlertsKuery = jest.fn();

const renderComponent = (
Expand All @@ -90,7 +88,6 @@ describe('AlertDetailsAppSection', () => {
<AlertDetailsAppSection
alert={buildCustomThresholdAlert(alert, alertFields)}
rule={buildCustomThresholdRule()}
setAlertSummaryFields={mockedSetAlertSummaryFields}
setRelatedAlertsKuery={mockedSetRelatedAlertsKuery}
/>
</QueryClientProvider>
Expand All @@ -109,27 +106,6 @@ describe('AlertDetailsAppSection', () => {
expect(result.getByTestId('thresholdRule-2000-2500')).toBeTruthy();
});

it('should render additional alert summary fields', async () => {
renderComponent();

expect(mockedSetAlertSummaryFields).toBeCalledTimes(2);
expect(mockedSetAlertSummaryFields).toBeCalledWith([
{
label: 'Related logs',
value: (
<span>
<EuiLink
data-test-subj="o11yCustomThresholdAlertDetailsViewRelatedLogs"
href="/view-in-app-url"
>
View related logs
</EuiLink>
</span>
),
},
]);
});

it('should render annotations', async () => {
const mockedRuleConditionChart = jest.fn(() => <div data-test-subj="RuleConditionChart" />);
(RuleConditionChart as jest.Mock).mockImplementation(mockedRuleConditionChart);
Expand All @@ -145,7 +121,7 @@ describe('AlertDetailsAppSection', () => {
it('should set relatedAlertsKuery', async () => {
renderComponent();

expect(mockedSetAlertSummaryFields).toBeCalledTimes(2);
expect(mockedSetRelatedAlertsKuery).toBeCalledTimes(1);
expect(mockedSetRelatedAlertsKuery).toHaveBeenLastCalledWith(
'(tags: "tag 1" or tags: "tag 2") or (host.name: "host-1" or kibana.alert.group.value: "host-1")'
);
Expand All @@ -155,22 +131,33 @@ describe('AlertDetailsAppSection', () => {
const result = renderComponent();

expect(result.getByTestId('chartTitle-0').textContent).toBe(
'Equation result for count (all documents)'
'Equation result for count (host.name: host-1)'
);
expect((result.getByTestId('viewLogs-0') as any).href).toBe('http://localhost/view-in-app-url');

expect(result.getByTestId('chartTitle-1').textContent).toBe(
'Equation result for max (system.cpu.user.pct)'
);
expect((result.getByTestId('viewLogs-1') as any).href).toBe('http://localhost/view-in-app-url');

expect(result.getByTestId('chartTitle-2').textContent).toBe(
'Equation result for min (system.memory.used.pct)'
);
expect((result.getByTestId('viewLogs-2') as any).href).toBe('http://localhost/view-in-app-url');

expect(result.getByTestId('chartTitle-3').textContent).toBe(
'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct...'
);
expect((result.getByTestId('viewLogs-3') as any).href).toBe('http://localhost/view-in-app-url');

expect(result.getByTestId('chartTitle-4').textContent).toBe(
'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct)'
);
expect((result.getByTestId('viewLogs-4') as any).href).toBe('http://localhost/view-in-app-url');

expect(result.getByTestId('chartTitle-5').textContent).toBe(
'Equation result for min (system.memory.used.pct) + min (system.memory.used.pct) + min (system.memory.used.pct)'
);
expect((result.getByTestId('viewLogs-5') as any).href).toBe('http://localhost/view-in-app-url');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import React, { useEffect, useState } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiLink,
EuiPanel,
EuiSpacer,
Expand All @@ -36,7 +37,6 @@ import { getRelatedAlertKuery } from '../../../../../common/utils/alerting/get_r
import { useLicense } from '../../../../hooks/use_license';
import { useKibana } from '../../../../utils/kibana_react';
import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter';
import { AlertSummaryField } from '../../../..';
import { AlertParams } from '../../types';
import { Threshold } from '../custom_threshold';
import { CustomThresholdRule, CustomThresholdAlert } from '../types';
Expand All @@ -49,15 +49,13 @@ import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and
interface AppSectionProps {
alert: CustomThresholdAlert;
rule: CustomThresholdRule;
setAlertSummaryFields: React.Dispatch<React.SetStateAction<AlertSummaryField[] | undefined>>;
setRelatedAlertsKuery: React.Dispatch<React.SetStateAction<string | undefined>>;
}

// eslint-disable-next-line import/no-default-export
export default function AlertDetailsAppSection({
alert,
rule,
setAlertSummaryFields,
setRelatedAlertsKuery,
}: AppSectionProps) {
const services = useKibana().services;
Expand All @@ -73,7 +71,6 @@ export default function AlertDetailsAppSection({
const hasLogRateAnalysisLicense = hasAtLeast('platinum');
const [dataView, setDataView] = useState<DataView>();
const [, setDataViewError] = useState<Error>();
const [viewInAppUrl, setViewInAppUrl] = useState<string>();
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-15m', to: 'now' });
const ruleParams = rule.params as RuleTypeParams & AlertParams;
const chartProps = {
Expand Down Expand Up @@ -123,48 +120,6 @@ export default function AlertDetailsAppSection({
setTimeRange(getPaddedAlertTimeRange(alertStart!, alertEnd));
}, [alertStart, alertEnd]);

useEffect(() => {
const appUrl = getViewInAppUrl({
dataViewId: dataView?.id,
groups,
logsExplorerLocator: locators.get<LogsExplorerLocatorParams>(LOGS_EXPLORER_LOCATOR_ID),
metrics: ruleParams.criteria[0]?.metrics,
searchConfiguration:
ruleParams.searchConfiguration as SearchConfigurationWithExtractedReferenceType,
startedAt: alertStart,
endedAt: alertEnd,
});

setViewInAppUrl(appUrl);
}, [dataView, alertStart, alertEnd, groups, ruleParams, locators]);

useEffect(() => {
const alertSummaryFields = [];

alertSummaryFields.push({
label: i18n.translate(
'xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.relatedLogs',
{
defaultMessage: 'Related logs',
}
),
value: (
<span>
<EuiLink
data-test-subj="o11yCustomThresholdAlertDetailsViewRelatedLogs"
href={viewInAppUrl}
>
{i18n.translate('xpack.observability.alertDetailsAppSection.a.viewRelatedLogsLabel', {
defaultMessage: 'View related logs',
})}
</EuiLink>
</span>
),
});

setAlertSummaryFields(alertSummaryFields);
}, [viewInAppUrl, setAlertSummaryFields]);

useEffect(() => {
const initDataView = async () => {
const ruleSearchConfiguration = ruleParams.searchConfiguration;
Expand All @@ -186,57 +141,88 @@ export default function AlertDetailsAppSection({

return (
<EuiFlexGroup direction="column" data-test-subj="thresholdAlertOverviewSection">
{ruleParams.criteria.map((criterion, index) => (
<EuiFlexItem key={`criterion-${index}`}>
<EuiPanel hasBorder hasShadow={false}>
<EuiToolTip content={chartTitleAndTooltip[index].tooltip}>
<EuiTitle size="xs">
<h4 data-test-subj={`chartTitle-${index}`}>{chartTitleAndTooltip[index].title}</h4>
</EuiTitle>
</EuiToolTip>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem style={{ minHeight: 150, minWidth: 160 }} grow={1}>
<Threshold
chartProps={chartProps}
id={`threshold-${index}`}
threshold={criterion.threshold}
value={alert.fields[ALERT_EVALUATION_VALUES]![index]}
valueFormatter={(d) =>
metricValueFormatter(
d,
criterion.metrics[0] ? criterion.metrics[0].name : undefined
)
}
title={i18n.translate(
'xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle',
{
defaultMessage: 'Threshold breached',
{ruleParams.criteria.map((criterion, index) => {
const appUrl = getViewInAppUrl({
dataViewId: dataView?.id,
groups,
logsExplorerLocator: locators.get<LogsExplorerLocatorParams>(LOGS_EXPLORER_LOCATOR_ID),
metrics: criterion?.metrics,
searchConfiguration:
ruleParams.searchConfiguration as SearchConfigurationWithExtractedReferenceType,
startedAt: alertStart,
endedAt: alertEnd,
});

return (
<EuiFlexItem key={`criterion-${index}`}>
<EuiPanel hasBorder hasShadow={false}>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiToolTip content={chartTitleAndTooltip[index].tooltip}>
<EuiTitle size="xs">
<h4 data-test-subj={`chartTitle-${index}`}>
{chartTitleAndTooltip[index].title}
</h4>
</EuiTitle>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj={`viewLogs-${index}`} href={appUrl} color="text">
<EuiIcon type="sortRight" />
&nbsp;
{i18n.translate(
'xpack.observability.customThreshold.rule.alertDetailsAppSection.openInDiscoverLabel',
{
defaultMessage: 'Open in Discover',
}
)}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem style={{ minHeight: 150, minWidth: 160 }} grow={1}>
<Threshold
chartProps={chartProps}
id={`threshold-${index}`}
threshold={criterion.threshold}
value={alert.fields[ALERT_EVALUATION_VALUES]![index]}
valueFormatter={(d) =>
metricValueFormatter(
d,
criterion.metrics[0] ? criterion.metrics[0].name : undefined
)
}
)}
comparator={criterion.comparator}
/>
</EuiFlexItem>
<EuiFlexItem grow={5}>
<RuleConditionChart
additionalFilters={getGroupFilters(groups)}
annotations={annotations}
chartOptions={{
// For alert details page, the series type needs to be changed to 'bar_stacked'
// due to https://github.com/elastic/elastic-charts/issues/2323
seriesType: 'bar_stacked',
}}
dataView={dataView}
groupBy={ruleParams.groupBy}
metricExpression={criterion}
searchConfiguration={ruleParams.searchConfiguration}
timeRange={timeRange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
))}
title={i18n.translate(
'xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle',
{
defaultMessage: 'Threshold breached',
}
)}
comparator={criterion.comparator}
/>
</EuiFlexItem>
<EuiFlexItem grow={5}>
<RuleConditionChart
additionalFilters={getGroupFilters(groups)}
annotations={annotations}
chartOptions={{
// For alert details page, the series type needs to be changed to 'bar_stacked'
// due to https://github.com/elastic/elastic-charts/issues/2323
seriesType: 'bar_stacked',
}}
dataView={dataView}
groupBy={ruleParams.groupBy}
metricExpression={criterion}
searchConfiguration={ruleParams.searchConfiguration}
timeRange={timeRange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
);
})}
{hasLogRateAnalysisLicense && (
<LogRateAnalysis alert={alert} dataView={dataView} rule={rule} services={services} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const buildCustomThresholdRule = (
{
name: 'A',
aggType: Aggregators.COUNT,
filter: 'host.name: host-1',
},
],
threshold: [2000],
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -32087,7 +32087,6 @@
"xpack.observability.alertDetails.tab.overviewLabel": "Aperçu",
"xpack.observability.alertDetails.untrackAlert": "Marquer comme non suivi",
"xpack.observability.alertDetails.viewRuleDetails": "Accéder aux détails de la règle",
"xpack.observability.alertDetailsAppSection.a.viewRelatedLogsLabel": "Afficher les logs associés",
"xpack.observability.alertDetailsPage.alertHistory.alertsTriggered": "Alertes déclenchées",
"xpack.observability.alertDetailsPage.alertHistory.avgTimeToRecover": "Temps moyen de récupération",
"xpack.observability.alertDetailsPage.alertHistory.chartTitle": "Historique des alertes",
Expand Down Expand Up @@ -32200,7 +32199,6 @@
"xpack.observability.customThreshold.rule.aggregators.p99": "99e centile de {metric}",
"xpack.observability.customThreshold.rule.aggregators.rate": "Taux de {metric}",
"xpack.observability.customThreshold.rule.aggregators.sum": "Somme de {metric}",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.relatedLogs": "Logs associés",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "Seuil dépassé",
"xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "Lien vers l’affichage de résolution des problèmes d’alerte pour voir plus de contextes et de détails. La chaîne sera vide si server.publicBaseUrl n'est pas configuré.",
"xpack.observability.customThreshold.rule.alertFlyout.addCondition": "Ajouter une condition",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -31831,7 +31831,6 @@
"xpack.observability.alertDetails.tab.overviewLabel": "概要",
"xpack.observability.alertDetails.untrackAlert": "未追跡に設定",
"xpack.observability.alertDetails.viewRuleDetails": "ルール詳細に移動",
"xpack.observability.alertDetailsAppSection.a.viewRelatedLogsLabel": "関連ログを表示",
"xpack.observability.alertDetailsPage.alertHistory.alertsTriggered": "アラートがトリガーされました",
"xpack.observability.alertDetailsPage.alertHistory.avgTimeToRecover": "回復までの平均時間",
"xpack.observability.alertDetailsPage.alertHistory.chartTitle": "アラート履歴",
Expand Down Expand Up @@ -31944,7 +31943,6 @@
"xpack.observability.customThreshold.rule.aggregators.p99": "{metric}の99パーセンタイル",
"xpack.observability.customThreshold.rule.aggregators.rate": "{metric}の比率",
"xpack.observability.customThreshold.rule.aggregators.sum": "{metric}の合計",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.relatedLogs": "関連ログ",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "しきい値を超えました",
"xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "アラートトラブルシューティングビューにリンクして、さらに詳しい状況や詳細を確認できます。server.publicBaseUrlが構成されていない場合は、空の文字列になります。",
"xpack.observability.customThreshold.rule.alertFlyout.addCondition": "条件を追加",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -31874,7 +31874,6 @@
"xpack.observability.alertDetails.tab.overviewLabel": "概览",
"xpack.observability.alertDetails.untrackAlert": "标记为已取消跟踪",
"xpack.observability.alertDetails.viewRuleDetails": "前往规则详情",
"xpack.observability.alertDetailsAppSection.a.viewRelatedLogsLabel": "查看相关日志",
"xpack.observability.alertDetailsPage.alertHistory.alertsTriggered": "已触发告警",
"xpack.observability.alertDetailsPage.alertHistory.avgTimeToRecover": "恢复的平均时间",
"xpack.observability.alertDetailsPage.alertHistory.chartTitle": "告警历史记录",
Expand Down Expand Up @@ -31987,7 +31986,6 @@
"xpack.observability.customThreshold.rule.aggregators.p99": "{metric} 的第 99 个百分位",
"xpack.observability.customThreshold.rule.aggregators.rate": "{metric} 的比率",
"xpack.observability.customThreshold.rule.aggregators.sum": "{metric} 的总和",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.relatedLogs": "相关日志",
"xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "超出阈值",
"xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "链接到告警故障排除视图获取进一步的上下文和详情。如果未配置 server.publicBaseUrl,这将为空字符串。",
"xpack.observability.customThreshold.rule.alertFlyout.addCondition": "添加条件",
Expand Down

0 comments on commit f20444a

Please sign in to comment.