mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[AO] - Add chart series filter capability for the Logs Alert Details page (#154335)
## Summary It closes #154262 ### Before:  ### After, we can filter the shown series on the chart alert-based 
This commit is contained in:
parent
62868e7fc0
commit
e57c853329
5 changed files with 44 additions and 4 deletions
10
x-pack/plugins/infra/common/utils/get_chart_group_names.ts
Normal file
10
x-pack/plugins/infra/common/utils/get_chart_group_names.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
// It's simple function to be shared, but it is required on both sides server and frontend
|
||||
// We need to get consistent group names when any changes occurs.
|
||||
export const getChartGroupNames = (fields: string[]) => fields.join(', ');
|
|
@ -6,8 +6,10 @@
|
|||
*/
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { ALERT_DURATION, ALERT_END } from '@kbn/rule-data-utils';
|
||||
import compact from 'lodash/compact';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { getChartGroupNames } from '../../../../../common/utils/get_chart_group_names';
|
||||
import { type PartialCriterion } from '../../../../../common/alerting/logs/log_threshold';
|
||||
import { CriterionPreview } from '../expression_editor/criterion_preview_chart';
|
||||
import { AlertAnnotation } from './components/alert_annotation';
|
||||
|
@ -21,6 +23,25 @@ const AlertDetailsAppSection = ({ rule, alert }: AlertDetailsAppSectionProps) =>
|
|||
.asMilliseconds();
|
||||
const alertDurationMS = alert.fields[ALERT_DURATION]! / 1000;
|
||||
const TWENTY_TIMES_RULE_WINDOW_MS = 20 * ruleWindowSizeMS;
|
||||
|
||||
/**
|
||||
* The `CriterionPreview` chart shows all the series/data stacked when there is a GroupBy in the rule parameters.
|
||||
* e.g., `host.name`, the chart will show stacks of data by hostname.
|
||||
* We only need the chart to show the series that is related to the selected alert.
|
||||
* The chart series are built based on the GroupBy in the rule params
|
||||
* Each series have an id which is the just a joining of fields value of the GroupBy `getChartGroupNames`
|
||||
* We filter down the series using this group name
|
||||
*/
|
||||
const alertFieldsFromGroupBy = compact(
|
||||
rule.params.groupBy?.map((fieldNameGroupBy) => {
|
||||
const field = Object.keys(alert.fields).find(
|
||||
(alertFiledName) => alertFiledName === fieldNameGroupBy
|
||||
);
|
||||
if (field) return alert.fields[field];
|
||||
})
|
||||
);
|
||||
const selectedSeries = getChartGroupNames(alertFieldsFromGroupBy);
|
||||
|
||||
/**
|
||||
* This is part or the requirements (RFC).
|
||||
* If the alert is less than 20 units of `FOR THE LAST <x> <units>` then we should draw a time range of 20 units.
|
||||
|
@ -52,6 +73,7 @@ const AlertDetailsAppSection = ({ rule, alert }: AlertDetailsAppSectionProps) =>
|
|||
showThreshold={true}
|
||||
executionTimeRange={{ gte: rangeFrom, lte: rangeTo }}
|
||||
annotations={[<AlertAnnotation alertStarted={alert.start} />]}
|
||||
filterSeriesByGroupName={[selectedSeries]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
|
|
@ -11,5 +11,5 @@ import { PartialRuleParams } from '../../../../../common/alerting/logs/log_thres
|
|||
|
||||
export interface AlertDetailsAppSectionProps {
|
||||
rule: Rule<PartialRuleParams>;
|
||||
alert: TopAlert;
|
||||
alert: TopAlert<Record<string, any>>;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ interface Props {
|
|||
showThreshold: boolean;
|
||||
executionTimeRange?: ExecutionTimeRange;
|
||||
annotations?: Array<ReactElement<typeof RectAnnotation | typeof LineAnnotation>>;
|
||||
filterSeriesByGroupName?: string[];
|
||||
}
|
||||
|
||||
export const CriterionPreview: React.FC<Props> = ({
|
||||
|
@ -69,6 +70,7 @@ export const CriterionPreview: React.FC<Props> = ({
|
|||
showThreshold,
|
||||
executionTimeRange,
|
||||
annotations,
|
||||
filterSeriesByGroupName,
|
||||
}) => {
|
||||
const chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset | null = useMemo(() => {
|
||||
const { field, comparator, value } = chartCriterion;
|
||||
|
@ -114,6 +116,7 @@ export const CriterionPreview: React.FC<Props> = ({
|
|||
showThreshold={showThreshold}
|
||||
executionTimeRange={executionTimeRange}
|
||||
annotations={annotations}
|
||||
filterSeriesByGroupName={filterSeriesByGroupName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -126,6 +129,7 @@ interface ChartProps {
|
|||
showThreshold: boolean;
|
||||
executionTimeRange?: ExecutionTimeRange;
|
||||
annotations?: Array<ReactElement<typeof RectAnnotation | typeof LineAnnotation>>;
|
||||
filterSeriesByGroupName?: string[];
|
||||
}
|
||||
|
||||
const CriterionPreviewChart: React.FC<ChartProps> = ({
|
||||
|
@ -136,6 +140,7 @@ const CriterionPreviewChart: React.FC<ChartProps> = ({
|
|||
showThreshold,
|
||||
executionTimeRange,
|
||||
annotations,
|
||||
filterSeriesByGroupName,
|
||||
}) => {
|
||||
const { uiSettings } = useKibana().services;
|
||||
const isDarkMode = uiSettings?.get('theme:darkMode') || false;
|
||||
|
@ -184,7 +189,9 @@ const CriterionPreviewChart: React.FC<ChartProps> = ({
|
|||
if (!isGrouped) {
|
||||
return series;
|
||||
}
|
||||
|
||||
if (filterSeriesByGroupName && filterSeriesByGroupName.length) {
|
||||
return series.filter((item) => filterSeriesByGroupName.includes(item.id));
|
||||
}
|
||||
const sortedByMax = series.sort((a, b) => {
|
||||
const aMax = Math.max(...a.points.map((point) => point.value));
|
||||
const bMax = Math.max(...b.points.map((point) => point.value));
|
||||
|
@ -192,7 +199,7 @@ const CriterionPreviewChart: React.FC<ChartProps> = ({
|
|||
});
|
||||
const sortedSeries = (!isAbove && !isBelow) || isAbove ? sortedByMax : sortedByMax.reverse();
|
||||
return sortedSeries.slice(0, GROUP_LIMIT);
|
||||
}, [series, isGrouped, isAbove, isBelow]);
|
||||
}, [isGrouped, filterSeriesByGroupName, series, isAbove, isBelow]);
|
||||
|
||||
const barSeries = useMemo(() => {
|
||||
return filteredSeries.reduce<Array<{ timestamp: number; value: number; groupBy: string }>>(
|
||||
|
|
|
@ -27,6 +27,7 @@ import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
|||
import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common';
|
||||
import { ParsedExperimentalFields } from '@kbn/rule-registry-plugin/common/parse_experimental_fields';
|
||||
import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map';
|
||||
import { getChartGroupNames } from '../../../../common/utils/get_chart_group_names';
|
||||
import {
|
||||
RuleParams,
|
||||
ruleParamsRT,
|
||||
|
@ -484,7 +485,7 @@ const getReducedGroupByResults = (
|
|||
): ReducedGroupByResults => {
|
||||
const getGroupName = (
|
||||
key: GroupedSearchQueryResponse['aggregations']['groups']['buckets'][0]['key']
|
||||
) => Object.values(key).join(', ');
|
||||
) => getChartGroupNames(Object.values(key));
|
||||
|
||||
const reducedGroupByResults: ReducedGroupByResults = [];
|
||||
if (isOptimizedGroupedSearchQueryResponse(results)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue