Fix the charts and group by section on the Log Threshold alert detail page (#155327)

## Summary

This PR fixes #155083 with the following changes:

- Create a new field to store the action context for an alert under
`ALERT_CONTEXT` (`kibana.alert.context`) for Log Threshold Rule.
- Change the alert detail page to reference the `groupByKeys` under
`ALERT_CONTEXT` for the group by section
- Change the history chart to only display `12h` buckets

I plan to do a follow up PR to add the ALERT_CONTEXT to the other
Observability Rules which we will also need for our alert details pages.

### How to test

1. Index data using:
https://github.com/elastic/high-cardinality-cluster/tree/main/high_cardinality_indexer
by running the following command:
```
DATASET="fake_stack" EVENTS_PER_CYCLE=1 INDEX_INTERVAL=60000 ELASTICSEARCH_HOSTS=http://localhost:9200 node src/run.js
```
2. Create a DataView for named "Admin Console" with the index pattern of
`high-cardinality-data-fake_stack.admin-console-*` and the timestamp
field set to `@timestamp`
3. Go to the Log Stream in Observability and change the index pattern to
"Admin Console"
4. Create a rule that looks like:

<img width="600" alt="image"
src="https://user-images.githubusercontent.com/41702/232578891-e65a3f1a-457c-459a-8d7f-cadc85e7067c.png">

5. Create a rule WITHOUT a group by that will trigger and check the
alert detail page
6. Create a rule with a ratio WITHOUT a group by that will trigger and
check the alert detail page
7. Create a rule with a ratio WITH a group by that will trigger and
check the alert detail page

---------

Co-authored-by: Kevin Delemme <kdelemme@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Chris Cowan 2023-05-11 09:54:35 -06:00 committed by GitHub
parent 937912b056
commit 78671f113c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 30 additions and 7 deletions

View file

@ -7,6 +7,7 @@
*/
import {
ALERT_CONTEXT,
ALERT_EVALUATION_THRESHOLD,
ALERT_EVALUATION_VALUE,
ALERT_EVALUATION_VALUES,
@ -19,6 +20,7 @@ export const legacyExperimentalFieldMap = {
required: false,
},
[ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false },
[ALERT_CONTEXT]: { type: 'object', array: false, required: false },
[ALERT_EVALUATION_VALUES]: {
type: 'scaled_float',
scaling_factor: 100,

View file

@ -85,6 +85,7 @@ const EVENT_MODULE = 'event.module' as const;
const ALERT_BUILDING_BLOCK_TYPE = `${ALERT_NAMESPACE}.building_block_type` as const;
const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const;
const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const;
const ALERT_CONTEXT = `${ALERT_NAMESPACE}.context` as const;
const ALERT_EVALUATION_VALUES = `${ALERT_NAMESPACE}.evaluation.values` as const;
// Fields pertaining to the rule associated with the alert
@ -133,6 +134,7 @@ const fields = {
ALERT_RULE_CONSUMER,
ALERT_RULE_PRODUCER,
ALERT_REASON,
ALERT_CONTEXT,
ALERT_RISK_SCORE,
ALERT_CASE_IDS,
ALERT_RULE_AUTHOR,
@ -194,6 +196,7 @@ export {
ALERT_BUILDING_BLOCK_TYPE,
ALERT_EVALUATION_THRESHOLD,
ALERT_EVALUATION_VALUE,
ALERT_CONTEXT,
ALERT_EVALUATION_VALUES,
ALERT_RULE_EXCEPTIONS_LIST,
ALERT_RULE_NAMESPACE_FIELD,

View file

@ -131,6 +131,7 @@ const LogsRatioChart: React.FC<ChartProps> = ({
const barSeries = useMemo(() => {
return series.flatMap(({ points, id }) => points.map((point) => ({ ...point, groupBy: id })));
}, [series]);
if (isLoading) {
return <LoadingState />;
} else if (hasError) {

View file

@ -8,7 +8,12 @@ import React, { useEffect, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { LIGHT_THEME } from '@elastic/charts';
import { EuiPanel } from '@elastic/eui';
import { ALERT_END, ALERT_EVALUATION_VALUE, ALERT_START } from '@kbn/rule-data-utils';
import {
ALERT_CONTEXT,
ALERT_END,
ALERT_EVALUATION_VALUE,
ALERT_START,
} from '@kbn/rule-data-utils';
import moment from 'moment';
import { useTheme } from '@emotion/react';
import { EuiTitle } from '@elastic/eui';
@ -20,6 +25,7 @@ import {
} from '@kbn/observability-alert-details';
import { useEuiTheme } from '@elastic/eui';
import { UI_SETTINGS } from '@kbn/data-plugin/public';
import { get } from 'lodash';
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
import { getChartGroupNames } from '../../../../../common/utils/get_chart_group_names';
import {
@ -61,7 +67,9 @@ const AlertDetailsAppSection = ({
rule.params.groupBy?.reduce(
(selectedFields: Record<string, any>, field) => ({
...selectedFields,
...{ [field]: alert.fields[field] },
...{
[field]: get(alert.fields[ALERT_CONTEXT], ['groupByKeys', ...field.split('.')], null),
},
}),
{}
) || {};
@ -232,7 +240,9 @@ const AlertDetailsAppSection = ({
rule &&
rule.params.criteria.length === 1 && (
<EuiFlexItem>
<LogsHistoryChart rule={rule} />
<LogsHistoryChart
rule={{ ...rule, params: { ...rule.params, timeSize: 12, timeUnit: 'h' } }}
/>
</EuiFlexItem>
)
);

View file

@ -68,7 +68,7 @@ export const useChartPreviewData = ({
let seriesQueryB = ratio[1].data.series[0].points;
let seriesId = 'ratio';
// When groupBy and a filter is applied, return the ratio only for the filtered grouped-by
if (ruleParams.groupBy.length && filterSeriesByGroupName) {
if (ruleParams.groupBy?.length && filterSeriesByGroupName) {
seriesId = filterSeriesByGroupName;
seriesQueryA =
ratio[0].data.series.find((series) => series.id === filterSeriesByGroupName)

View file

@ -8,6 +8,7 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { i18n } from '@kbn/i18n';
import {
ALERT_CONTEXT,
ALERT_EVALUATION_THRESHOLD,
ALERT_EVALUATION_VALUE,
ALERT_REASON,
@ -86,7 +87,7 @@ export type LogThresholdAlertFactory = (
value: number,
threshold: number,
actions?: Array<{ actionGroup: LogThresholdActionGroups; context: AlertContext }>,
additionalContext?: AdditionalContext
rootLevelContext?: AdditionalContext
) => LogThresholdAlert;
export type LogThresholdAlertLimit = RuleExecutorServices<
LogThresholdAlertState,
@ -134,15 +135,21 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
value,
threshold,
actions,
additionalContext
rootLevelContext
) => {
const alertContext =
actions != null
? actions.reduce((next, action) => ({ ...next, ...action.context }), {})
: {};
const alert = alertWithLifecycle({
id,
fields: {
[ALERT_EVALUATION_THRESHOLD]: threshold,
[ALERT_EVALUATION_VALUE]: value,
[ALERT_REASON]: reason,
...flattenAdditionalContext(additionalContext),
[ALERT_CONTEXT]: alertContext,
...flattenAdditionalContext(rootLevelContext),
},
});