[8.x] [ML][AIOps] Log rate analysis: ensure ability to sort on Log rate change (#193501) (#194862)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[ML][AIOps] Log rate analysis: ensure ability to sort on Log rate
change (#193501)](https://github.com/elastic/kibana/pull/193501)

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

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

<!--BACKPORT [{"author":{"name":"Melissa
Alvarez","email":"melissa.alvarez@elastic.co"},"sourceCommit":{"committedDate":"2024-10-03T16:40:10Z","message":"[ML][AIOps]
Log rate analysis: ensure ability to sort on Log rate change
(#193501)\n\n## Summary\r\n\r\nThis PR \r\n- updates the
`LogRateAnalysisResultsTable` to use `EuiInMemoryTable` to\r\nsimplify
sorting and pagination\r\n- adds sorting to `Log rate change`
column\r\n- persists columns selected for viewing in the result
view\r\n\r\nRelated meta issue:
https://github.com/elastic/kibana/issues/187684\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [ ] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[ ] If a plugin configuration key changed, check if it needs to
be\r\nallowlisted in the cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[ ] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"c18184ae26c4af3f203256b582790f5bf4e0f595","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement",":ml","v9.0.0","backport:prev-minor","Feature:ML/AIOps","v8.16.0","backport:version"],"title":"[ML][AIOps]
Log rate analysis: ensure ability to sort on Log rate
change","number":193501,"url":"https://github.com/elastic/kibana/pull/193501","mergeCommit":{"message":"[ML][AIOps]
Log rate analysis: ensure ability to sort on Log rate change
(#193501)\n\n## Summary\r\n\r\nThis PR \r\n- updates the
`LogRateAnalysisResultsTable` to use `EuiInMemoryTable` to\r\nsimplify
sorting and pagination\r\n- adds sorting to `Log rate change`
column\r\n- persists columns selected for viewing in the result
view\r\n\r\nRelated meta issue:
https://github.com/elastic/kibana/issues/187684\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [ ] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[ ] If a plugin configuration key changed, check if it needs to
be\r\nallowlisted in the cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[ ] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"c18184ae26c4af3f203256b582790f5bf4e0f595"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193501","number":193501,"mergeCommit":{"message":"[ML][AIOps]
Log rate analysis: ensure ability to sort on Log rate change
(#193501)\n\n## Summary\r\n\r\nThis PR \r\n- updates the
`LogRateAnalysisResultsTable` to use `EuiInMemoryTable` to\r\nsimplify
sorting and pagination\r\n- adds sorting to `Log rate change`
column\r\n- persists columns selected for viewing in the result
view\r\n\r\nRelated meta issue:
https://github.com/elastic/kibana/issues/187684\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [ ] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [ ] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[ ] If a plugin configuration key changed, check if it needs to
be\r\nallowlisted in the cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[ ] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"c18184ae26c4af3f203256b582790f5bf4e0f595"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Melissa Alvarez <melissa.alvarez@elastic.co>
This commit is contained in:
Kibana Machine 2024-10-04 04:20:42 +10:00 committed by GitHub
parent 23e1bdf32e
commit 9af74d9e46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 137 additions and 85 deletions

View file

@ -166,6 +166,9 @@ export interface SignificantItem extends FieldValuePair {
/** Indicates if the significant item is unique within a group. */
unique?: boolean;
/** Calculates a numerical value based on bg_count and doc_count for which to sort log rate change */
logRateChangeSort?: number;
}
interface SignificantItemHistogramItemBase {

View file

@ -38,7 +38,10 @@ export function getLogRateChange(
}
);
return { message, factor: roundedFactor };
return {
message,
factor: roundedFactor,
};
} else {
return {
message: i18n.translate(
@ -66,7 +69,10 @@ export function getLogRateChange(
}
);
return { message, factor: roundedFactor };
return {
message,
factor: roundedFactor,
};
} else {
return {
message: i18n.translate(

View file

@ -37,6 +37,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { SignificantItem, SignificantItemGroup } from '@kbn/ml-agg-utils';
import { useStorage } from '@kbn/ml-local-storage';
import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants';
import type { AiopsLogRateAnalysisSchema } from '@kbn/aiops-log-rate-analysis/api/schema';
import type { AiopsLogRateAnalysisSchemaSignificantItem } from '@kbn/aiops-log-rate-analysis/api/schema_v3';
@ -53,6 +54,11 @@ import {
commonColumns,
significantItemColumns,
} from '../log_rate_analysis_results_table/use_columns';
import {
AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS,
type AiOpsKey,
type AiOpsStorageMapped,
} from '../../types/storage';
import {
getGroupTableItems,
@ -187,11 +193,10 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
);
const [shouldStart, setShouldStart] = useState(false);
const [toggleIdSelected, setToggleIdSelected] = useState(resultsGroupedOffId);
const [skippedColumns, setSkippedColumns] = useState<ColumnNames[]>([
'p-value',
'Baseline rate',
'Deviation rate',
]);
const [skippedColumns, setSkippedColumns] = useStorage<
AiOpsKey,
AiOpsStorageMapped<typeof AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS>
>(AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS, ['p-value', 'Baseline rate', 'Deviation rate']);
// null is used as the uninitialized state to identify the first load.
const [skippedFields, setSkippedFields] = useState<string[] | null>(null);

View file

@ -51,7 +51,15 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
const euiTheme = useEuiTheme();
const primaryBackgroundColor = useEuiBackgroundColor('primary');
const allSignificantItems = useAppSelector((s) => s.logRateAnalysisResults.significantItems);
const allItems = useAppSelector((s) => s.logRateAnalysisResults.significantItems);
const allSignificantItems = useMemo(() => {
return allItems.map((item) => ({
...item,
logRateChangeSort:
item.bg_count > 0 ? item.doc_count / item.bg_count : Number.POSITIVE_INFINITY,
}));
}, [allItems]);
const significantItems = useMemo(() => {
if (!groupFilter) {
@ -85,7 +93,6 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
);
const dispatch = useAppDispatch();
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<keyof SignificantItem>(
@ -135,8 +142,8 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
];
const sortDirections = [sortDirection];
// Only if the table is sorted by p-value, add a secondary sort by doc count.
if (sortField === 'pValue') {
// If the table is sorted by p-value or log rate change, add a secondary sort by doc count.
if (sortField === 'pValue' || sortField === 'logRateChangeSort') {
sortIteratees.push((item: SignificantItem) => item.doc_count);
sortDirections.push(sortDirection);
}
@ -242,12 +249,12 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
<EuiBasicTable
data-test-subj="aiopsLogRateAnalysisResultsTable"
compressed
columns={columns}
items={pageOfItems}
onChange={onChange}
columns={columns}
pagination={pagination.totalItemCount > pagination.pageSize ? pagination : undefined}
loading={false}
sorting={sorting as EuiTableSortingType<SignificantItem>}
loading={false}
onChange={onChange}
rowProps={(significantItem) => {
return {
'data-test-subj': `aiopsLogRateAnalysisResultsTableRow row-${significantItem.fieldName}-${significantItem.fieldValue}`,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useMemo } from 'react';
import React, { useMemo, useCallback } from 'react';
import {
type EuiBasicTableColumn,
EuiBadge,
@ -112,18 +112,6 @@ const groupLogRateHelpMessage = i18n.translate(
'A visual representation of the impact of the group on the message rate difference.',
}
);
const groupImpactMessage = i18n.translate(
'xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip',
{
defaultMessage: 'The level of impact of the group on the message rate difference',
}
);
const impactMessage = i18n.translate(
'xpack.aiops.logRateAnalysis.resultsTable.impactLabelColumnTooltip',
{
defaultMessage: 'The level of impact of the field on the message rate difference.',
}
);
const logRateChangeMessage = i18n.translate(
'xpack.aiops.logRateAnalysis.resultsTableGroups.logRateChangeLabelColumnTooltip',
{
@ -144,6 +132,69 @@ const deviationRateMessage = i18n.translate(
}
);
// EuiInMemoryTable stores column `name` in its own memory as an object and computed columns may be updating dynamically which means the reference is no longer ===.
// Need to declare name react node outside to keep it static and maintain reference equality.
const LogRateColumnName = (
<>
<FormattedMessage
id="xpack.aiops.logRateAnalysis.resultsTable.logRateChangeLabel"
defaultMessage="Log rate change"
/>
&nbsp;
<EuiIconTip
size="s"
position="top"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={logRateChangeMessage}
/>
</>
);
const ImpactColumnName = (
<>
<FormattedMessage
id="xpack.aiops.logRateAnalysis.resultsTable.impactLabel"
defaultMessage="Impact"
/>
&nbsp;
<EuiIconTip
size="s"
position="top"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.impactLabelColumnTooltip', {
defaultMessage: 'The level of impact of the field on the message rate difference.',
})}
/>
</>
);
const GroupImpactColumnName = (
<>
<FormattedMessage
id="xpack.aiops.logRateAnalysis.resultsTable.impactLabel"
defaultMessage="Impact"
/>
&nbsp;
<EuiIconTip
size="s"
position="top"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={i18n.translate(
'xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip',
{
defaultMessage: 'The level of impact of the group on the message rate difference',
}
)}
/>
</>
);
export const useColumns = (
tableType: LogRateAnalysisResultsTableType,
skippedColumns: string[],
@ -195,6 +246,31 @@ export const useColumns = (
return { baselineBuckets, deviationBuckets };
}, [currentAnalysisWindowParameters, interval]);
const logRateChangeNotAvailable = useMemo(
() =>
interval === 0 ||
currentAnalysisType === undefined ||
currentAnalysisWindowParameters === undefined ||
buckets === undefined ||
isGroupsTable,
[interval, currentAnalysisType, currentAnalysisWindowParameters, buckets, isGroupsTable]
);
const getLogRateChangeValues = useCallback(
(docCount: number, bgCount: number) => {
const { baselineBucketRate, deviationBucketRate } = getBaselineAndDeviationRates(
currentAnalysisType!,
buckets!.baselineBuckets,
buckets!.deviationBuckets,
docCount,
bgCount
);
return getLogRateChange(currentAnalysisType!, baselineBucketRate, deviationBucketRate);
},
[currentAnalysisType, buckets]
);
const columnsMap: Record<ColumnNames, EuiBasicTableColumn<SignificantItem>> = useMemo(
() => ({
['Field name']: {
@ -322,24 +398,8 @@ export const useColumns = (
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact',
width: '8%',
field: 'pValue',
name: (
<>
<FormattedMessage
id="xpack.aiops.logRateAnalysis.resultsTable.impactLabel"
defaultMessage="Impact"
/>
&nbsp;
<EuiIconTip
size="s"
position="top"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={isGroupsTable ? groupImpactMessage : impactMessage}
/>
</>
),
render: (_, { pValue }) => {
name: isGroupsTable ? GroupImpactColumnName : ImpactColumnName, // content={isGroupsTable ? groupImpactMessage : impactMessage}
render: (_, { pValue }: SignificantItem) => {
if (typeof pValue !== 'number') return NOT_AVAILABLE;
const label = getFailedTransactionsCorrelationImpactLabel(pValue);
return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
@ -435,46 +495,12 @@ export const useColumns = (
},
['Log rate change']: {
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnLogRateChange',
name: (
<>
<FormattedMessage
id="xpack.aiops.logRateAnalysis.resultsTable.logRateChangeLabel"
defaultMessage="Log rate change"
/>
&nbsp;
<EuiIconTip
size="s"
position="top"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={logRateChangeMessage}
/>
</>
),
render: ({ doc_count: docCount, bg_count: bgCount }: SignificantItem) => {
if (
interval === 0 ||
currentAnalysisType === undefined ||
currentAnalysisWindowParameters === undefined ||
buckets === undefined ||
isGroupsTable
)
return NOT_AVAILABLE;
const { baselineBucketRate, deviationBucketRate } = getBaselineAndDeviationRates(
currentAnalysisType,
buckets.baselineBuckets,
buckets.deviationBuckets,
docCount,
bgCount
);
const logRateChange = getLogRateChange(
currentAnalysisType,
baselineBucketRate,
deviationBucketRate
);
field: 'logRateChangeSort',
name: LogRateColumnName,
sortable: isGroupsTable ? false : true,
render: (_, { doc_count: docCount, bg_count: bgCount }: SignificantItem) => {
if (logRateChangeNotAvailable) return NOT_AVAILABLE;
const logRateChange = getLogRateChangeValues(docCount, bgCount);
return (
<>

View file

@ -19,12 +19,14 @@ export const AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE =
'aiops.randomSamplingProbabilityPreference';
export const AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE =
'aiops.patternAnalysisMinimumTimeRangePreference';
export const AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS = 'aiops.logRateAnalysisResultColumns';
export type AiOps = Partial<{
[AIOPS_FROZEN_TIER_PREFERENCE]: FrozenTierPreference;
[AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE]: RandomSamplerOption;
[AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE]: number;
[AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE]: MinimumTimeRangeOption;
[AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS]: string[];
}> | null;
export type AiOpsKey = keyof Exclude<AiOps, null>;
@ -37,6 +39,8 @@ export type AiOpsStorageMapped<T extends AiOpsKey> = T extends typeof AIOPS_FROZ
? RandomSamplerProbability
: T extends typeof AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE
? MinimumTimeRangeOption
: T extends typeof AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS
? string[]
: null;
export const AIOPS_STORAGE_KEYS = [
@ -44,4 +48,5 @@ export const AIOPS_STORAGE_KEYS = [
AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE,
AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE,
AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE,
AIOPS_LOG_RATE_ANALYSIS_RESULT_COLUMNS,
] as const;