mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ML] AIOps Log Rate Analysis: adds controls for controlling which columns will be visible (#184262)
## Summary
Related meta issue: https://github.com/elastic/kibana/issues/182714
This PR adds controls to the AIOps results table to show/hide columns.
<img width="1100" alt="image"
src="8e1f2913
-614b-4fe2-884f-aa53760646e4">
### Checklist
Delete any items that are not applicable to this PR.
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
70df50948d
commit
2c76ad0ce3
18 changed files with 655 additions and 559 deletions
|
@ -17,7 +17,7 @@ interface FieldFilterApplyButtonProps {
|
|||
tooltipContent?: string;
|
||||
}
|
||||
|
||||
export const FieldFilterApplyButton: FC<FieldFilterApplyButtonProps> = ({
|
||||
export const ItemFilterApplyButton: FC<FieldFilterApplyButtonProps> = ({
|
||||
disabled,
|
||||
onClick,
|
||||
tooltipContent,
|
|
@ -27,26 +27,40 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { FieldFilterApplyButton } from './field_filter_apply_button';
|
||||
import { ItemFilterApplyButton } from './item_filter_apply_button';
|
||||
|
||||
interface FieldFilterPopoverProps {
|
||||
interface ItemFilterPopoverProps {
|
||||
dataTestSubj: string;
|
||||
disabled?: boolean;
|
||||
disabledApplyButton?: boolean;
|
||||
uniqueFieldNames: string[];
|
||||
onChange: (skippedFields: string[]) => void;
|
||||
disabledApplyTooltipContent?: string;
|
||||
helpText: string;
|
||||
itemSearchAriaLabel: string;
|
||||
initialSkippedItems?: string[];
|
||||
popoverButtonTitle: string;
|
||||
selectedItemLimit?: number;
|
||||
uniqueItemNames: string[];
|
||||
onChange: (skippedItems: string[]) => void;
|
||||
}
|
||||
|
||||
// This component is mostly inspired by EUI's Data Grid Column Selector
|
||||
// https://github.com/elastic/eui/blob/main/src/components/datagrid/controls/column_selector.tsx
|
||||
export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
||||
export const ItemFilterPopover: FC<ItemFilterPopoverProps> = ({
|
||||
dataTestSubj,
|
||||
disabled,
|
||||
disabledApplyButton,
|
||||
uniqueFieldNames,
|
||||
disabledApplyTooltipContent,
|
||||
helpText,
|
||||
itemSearchAriaLabel,
|
||||
initialSkippedItems = [],
|
||||
popoverButtonTitle,
|
||||
selectedItemLimit = 2,
|
||||
uniqueItemNames,
|
||||
onChange,
|
||||
}) => {
|
||||
const euiThemeContext = useEuiTheme();
|
||||
// Inspired by https://github.com/elastic/eui/blob/main/src/components/datagrid/controls/_data_grid_column_selector.scss
|
||||
const fieldSelectPopover = useMemo(
|
||||
const itemSelectPopover = useMemo(
|
||||
() => css`
|
||||
${euiYScrollWithShadows(euiThemeContext, {})}
|
||||
max-height: 400px;
|
||||
|
@ -55,48 +69,49 @@ export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
|||
);
|
||||
|
||||
const [isTouched, setIsTouched] = useState(false);
|
||||
const [fieldSearchText, setFieldSearchText] = useState('');
|
||||
const [skippedFields, setSkippedFields] = useState<string[]>([]);
|
||||
const setFieldsFilter = (fieldNames: string[], checked: boolean) => {
|
||||
let updatedSkippedFields = [...skippedFields];
|
||||
const [itemSearchText, setItemSearchText] = useState('');
|
||||
const [skippedItems, setSkippedItems] = useState<string[]>(initialSkippedItems);
|
||||
const setItemsFilter = (itemNames: string[], checked: boolean) => {
|
||||
let updatedSkippedItems = [...skippedItems];
|
||||
if (!checked) {
|
||||
updatedSkippedFields.push(...fieldNames);
|
||||
updatedSkippedItems.push(...itemNames);
|
||||
} else {
|
||||
updatedSkippedFields = skippedFields.filter((d) => !fieldNames.includes(d));
|
||||
updatedSkippedItems = skippedItems.filter((d) => !itemNames.includes(d));
|
||||
}
|
||||
setSkippedFields(updatedSkippedFields);
|
||||
// Ensure there are no duplicates
|
||||
setSkippedItems([...new Set(updatedSkippedItems)]);
|
||||
setIsTouched(true);
|
||||
};
|
||||
|
||||
const [isFieldSelectionPopoverOpen, setIsFieldSelectionPopoverOpen] = useState(false);
|
||||
const onFieldSelectionButtonClick = () => setIsFieldSelectionPopoverOpen((isOpen) => !isOpen);
|
||||
const closePopover = () => setIsFieldSelectionPopoverOpen(false);
|
||||
const [isItemSelectionPopoverOpen, setIsItemSelectionPopoverOpen] = useState(false);
|
||||
const onItemSelectionButtonClick = () => setIsItemSelectionPopoverOpen((isOpen) => !isOpen);
|
||||
const closePopover = () => setIsItemSelectionPopoverOpen(false);
|
||||
|
||||
const filteredUniqueFieldNames = useMemo(() => {
|
||||
return uniqueFieldNames.filter(
|
||||
(d) => d.toLowerCase().indexOf(fieldSearchText.toLowerCase()) !== -1
|
||||
const filteredUniqueItemNames = useMemo(() => {
|
||||
return uniqueItemNames.filter(
|
||||
(d) => d.toLowerCase().indexOf(itemSearchText.toLowerCase()) !== -1
|
||||
);
|
||||
}, [fieldSearchText, uniqueFieldNames]);
|
||||
}, [itemSearchText, uniqueItemNames]);
|
||||
|
||||
// If the supplied list of unique field names changes, do a sanity check to only
|
||||
// keep field names in the list of skipped fields that still are in the list of unique fields.
|
||||
useEffect(() => {
|
||||
setSkippedFields((previousSkippedFields) =>
|
||||
previousSkippedFields.filter((d) => uniqueFieldNames.includes(d))
|
||||
setSkippedItems((previousSkippedItems) =>
|
||||
previousSkippedItems.filter((d) => uniqueItemNames.includes(d))
|
||||
);
|
||||
}, [uniqueFieldNames]);
|
||||
}, [uniqueItemNames]);
|
||||
|
||||
const selectedFieldCount = uniqueFieldNames.length - skippedFields.length;
|
||||
const selectedItemCount = uniqueItemNames.length - skippedItems.length;
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
data-test-subj="aiopsFieldFilterPopover"
|
||||
anchorPosition="downLeft"
|
||||
panelPaddingSize="s"
|
||||
panelStyle={{ minWidth: '20%' }}
|
||||
button={
|
||||
<EuiButton
|
||||
data-test-subj="aiopsFieldFilterButton"
|
||||
onClick={onFieldSelectionButtonClick}
|
||||
data-test-subj={dataTestSubj}
|
||||
onClick={onItemSelectionButtonClick}
|
||||
disabled={disabled}
|
||||
size="s"
|
||||
iconType="arrowDown"
|
||||
|
@ -104,21 +119,15 @@ export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
|||
iconSize="s"
|
||||
color="text"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldFilterButtonLabel"
|
||||
defaultMessage="Filter fields"
|
||||
/>
|
||||
{popoverButtonTitle}
|
||||
</EuiButton>
|
||||
}
|
||||
isOpen={isFieldSelectionPopoverOpen}
|
||||
isOpen={isItemSelectionPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
>
|
||||
<EuiPopoverTitle>
|
||||
<EuiText size="xs" color="subdued" style={{ maxWidth: '400px' }}>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldFilterHelpText"
|
||||
defaultMessage="Deselect non-relevant fields to remove them from groups and click the Apply button to rerun the grouping. Use the search bar to filter the list, then select/deselect multiple fields with the actions below."
|
||||
/>
|
||||
{helpText}
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFieldText
|
||||
|
@ -126,26 +135,24 @@ export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
|||
placeholder={i18n.translate('xpack.aiops.analysis.fieldSelectorPlaceholder', {
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
aria-label={i18n.translate('xpack.aiops.analysis.fieldSelectorAriaLabel', {
|
||||
defaultMessage: 'Filter fields',
|
||||
})}
|
||||
value={fieldSearchText}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFieldSearchText(e.currentTarget.value)}
|
||||
aria-label={itemSearchAriaLabel}
|
||||
value={itemSearchText}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setItemSearchText(e.currentTarget.value)}
|
||||
data-test-subj="aiopsFieldSelectorSearch"
|
||||
/>
|
||||
</EuiPopoverTitle>
|
||||
<div css={fieldSelectPopover} data-test-subj="aiopsFieldSelectorFieldNameList">
|
||||
{filteredUniqueFieldNames.map((fieldName) => (
|
||||
<div css={itemSelectPopover} data-test-subj="aiopsFieldSelectorFieldNameList">
|
||||
{filteredUniqueItemNames.map((fieldName) => (
|
||||
<div key={fieldName} css={{ padding: '4px' }}>
|
||||
<EuiSwitch
|
||||
data-test-subj={`aiopsFieldSelectorFieldNameListItem${
|
||||
!skippedFields.includes(fieldName) ? ' checked' : ''
|
||||
!skippedItems.includes(fieldName) ? ' checked' : ''
|
||||
}`}
|
||||
className="euiSwitch--mini"
|
||||
compressed
|
||||
label={fieldName}
|
||||
onChange={(e) => setFieldsFilter([fieldName], e.target.checked)}
|
||||
checked={!skippedFields.includes(fieldName)}
|
||||
onChange={(e) => setItemsFilter([fieldName], e.target.checked)}
|
||||
checked={!skippedItems.includes(fieldName)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
@ -162,19 +169,19 @@ export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
|||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
flush="left"
|
||||
onClick={() => setFieldsFilter(filteredUniqueFieldNames, true)}
|
||||
disabled={fieldSearchText.length > 0 && filteredUniqueFieldNames.length === 0}
|
||||
onClick={() => setItemsFilter(filteredUniqueItemNames, true)}
|
||||
disabled={itemSearchText.length > 0 && filteredUniqueItemNames.length === 0}
|
||||
data-test-subj="aiopsFieldSelectorSelectAllFieldsButton"
|
||||
>
|
||||
{fieldSearchText.length > 0 ? (
|
||||
{itemSearchText.length > 0 ? (
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllSearchedFields"
|
||||
defaultMessage="Select filtered fields"
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllSearchedItems"
|
||||
defaultMessage="Select filtered"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllFields"
|
||||
defaultMessage="Select all fields"
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllItems"
|
||||
defaultMessage="Select all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
|
@ -183,39 +190,35 @@ export const FieldFilterPopover: FC<FieldFilterPopoverProps> = ({
|
|||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
flush="right"
|
||||
onClick={() => setFieldsFilter(filteredUniqueFieldNames, false)}
|
||||
disabled={fieldSearchText.length > 0 && filteredUniqueFieldNames.length === 0}
|
||||
onClick={() => setItemsFilter(filteredUniqueItemNames, false)}
|
||||
disabled={itemSearchText.length > 0 && filteredUniqueItemNames.length === 0}
|
||||
data-test-subj="aiopsFieldSelectorDeselectAllFieldsButton"
|
||||
>
|
||||
{fieldSearchText.length > 0 ? (
|
||||
{itemSearchText.length > 0 ? (
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllSearchedFields"
|
||||
defaultMessage="Deselect filtered fields"
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllSearchedItems"
|
||||
defaultMessage="Deselect filtered"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllFields"
|
||||
defaultMessage="Deselect all fields"
|
||||
id="xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllItems"
|
||||
defaultMessage="Deselect all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
<EuiFlexItem grow={false}>
|
||||
<FieldFilterApplyButton
|
||||
<ItemFilterApplyButton
|
||||
onClick={() => {
|
||||
onChange(skippedFields);
|
||||
setFieldSearchText('');
|
||||
setIsFieldSelectionPopoverOpen(false);
|
||||
onChange(skippedItems);
|
||||
setItemSearchText('');
|
||||
setIsItemSelectionPopoverOpen(false);
|
||||
closePopover();
|
||||
}}
|
||||
disabled={disabledApplyButton || selectedFieldCount < 2 || !isTouched}
|
||||
disabled={disabledApplyButton || selectedItemCount < selectedItemLimit || !isTouched}
|
||||
tooltipContent={
|
||||
selectedFieldCount < 2
|
||||
? i18n.translate('xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected', {
|
||||
defaultMessage: 'Grouping requires at least 2 fields to be selected.',
|
||||
})
|
||||
: undefined
|
||||
selectedItemCount < selectedItemLimit ? disabledApplyTooltipContent : undefined
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
|
@ -41,6 +41,10 @@ import { useLogRateAnalysisStateContext } from '@kbn/aiops-components';
|
|||
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import {
|
||||
commonColumns,
|
||||
significantItemColumns,
|
||||
} from '../log_rate_analysis_results_table/use_columns';
|
||||
|
||||
import {
|
||||
getGroupTableItems,
|
||||
|
@ -48,8 +52,10 @@ import {
|
|||
LogRateAnalysisResultsGroupsTable,
|
||||
} from '../log_rate_analysis_results_table';
|
||||
|
||||
import { FieldFilterPopover } from './field_filter_popover';
|
||||
import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover';
|
||||
import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover';
|
||||
import { LogRateAnalysisTypeCallOut } from './log_rate_analysis_type_callout';
|
||||
import type { ColumnNames } from '../log_rate_analysis_results_table';
|
||||
|
||||
const groupResultsMessage = i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResults',
|
||||
|
@ -77,6 +83,37 @@ const groupResultsOnMessage = i18n.translate(
|
|||
);
|
||||
const resultsGroupedOffId = 'aiopsLogRateAnalysisGroupingOff';
|
||||
const resultsGroupedOnId = 'aiopsLogRateAnalysisGroupingOn';
|
||||
const fieldFilterHelpText = i18n.translate('xpack.aiops.logRateAnalysis.page.fieldFilterHelpText', {
|
||||
defaultMessage:
|
||||
'Deselect non-relevant fields to remove them from groups and click the Apply button to rerun the grouping. Use the search bar to filter the list, then select/deselect multiple fields with the actions below.',
|
||||
});
|
||||
const columnsFilterHelpText = i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.page.columnsFilterHelpText',
|
||||
{
|
||||
defaultMessage: 'Configure visible columns.',
|
||||
}
|
||||
);
|
||||
const disabledFieldFilterApplyButtonTooltipContent = i18n.translate(
|
||||
'xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected',
|
||||
{
|
||||
defaultMessage: 'Grouping requires at least 2 fields to be selected.',
|
||||
}
|
||||
);
|
||||
const disabledColumnFilterApplyButtonTooltipContent = i18n.translate(
|
||||
'xpack.aiops.analysis.columnSelectorNotEnoughColumnsSelected',
|
||||
{
|
||||
defaultMessage: 'At least one column must be selected.',
|
||||
}
|
||||
);
|
||||
const columnSearchAriaLabel = i18n.translate('xpack.aiops.analysis.columnSelectorAriaLabel', {
|
||||
defaultMessage: 'Filter columns',
|
||||
});
|
||||
const columnsButton = i18n.translate('xpack.aiops.logRateAnalysis.page.columnsFilterButtonLabel', {
|
||||
defaultMessage: 'Columns',
|
||||
});
|
||||
const fieldsButton = i18n.translate('xpack.aiops.analysis.fieldFilterButtonLabel', {
|
||||
defaultMessage: 'Filter fields',
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for log rate analysis results data.
|
||||
|
@ -157,6 +194,7 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
|||
);
|
||||
const [shouldStart, setShouldStart] = useState(false);
|
||||
const [toggleIdSelected, setToggleIdSelected] = useState(resultsGroupedOffId);
|
||||
const [skippedColumns, setSkippedColumns] = useState<ColumnNames[]>(['p-value']);
|
||||
|
||||
const onGroupResultsToggle = (optionId: string) => {
|
||||
setToggleIdSelected(optionId);
|
||||
|
@ -179,6 +217,10 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
|||
startHandler(true, false);
|
||||
};
|
||||
|
||||
const onVisibleColumnsChange = (columns: ColumnNames[]) => {
|
||||
setSkippedColumns(columns);
|
||||
};
|
||||
|
||||
const {
|
||||
cancel,
|
||||
start,
|
||||
|
@ -378,12 +420,36 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<FieldFilterPopover
|
||||
dataTestSubj="aiopsFieldFilterButton"
|
||||
disabled={!groupResults || isRunning}
|
||||
disabledApplyButton={isRunning}
|
||||
uniqueFieldNames={uniqueFieldNames}
|
||||
disabledApplyTooltipContent={disabledFieldFilterApplyButtonTooltipContent}
|
||||
helpText={fieldFilterHelpText}
|
||||
itemSearchAriaLabel={fieldsButton}
|
||||
popoverButtonTitle={fieldsButton}
|
||||
uniqueItemNames={uniqueFieldNames}
|
||||
onChange={onFieldsFilterChange}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ColumnFilterPopover
|
||||
dataTestSubj="aiopsColumnFilterButton"
|
||||
disabled={isRunning}
|
||||
disabledApplyButton={isRunning}
|
||||
disabledApplyTooltipContent={disabledColumnFilterApplyButtonTooltipContent}
|
||||
helpText={columnsFilterHelpText}
|
||||
itemSearchAriaLabel={columnSearchAriaLabel}
|
||||
initialSkippedItems={skippedColumns}
|
||||
popoverButtonTitle={columnsButton}
|
||||
selectedItemLimit={1}
|
||||
uniqueItemNames={
|
||||
(groupResults
|
||||
? Object.values(commonColumns)
|
||||
: Object.values(significantItemColumns)) as string[]
|
||||
}
|
||||
onChange={onVisibleColumnsChange as (columns: string[]) => void}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ProgressControls>
|
||||
{showLogRateAnalysisResultsTable && currentAnalysisType !== undefined && (
|
||||
<>
|
||||
|
@ -481,6 +547,7 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
|||
>
|
||||
{showLogRateAnalysisResultsTable && groupResults ? (
|
||||
<LogRateAnalysisResultsGroupsTable
|
||||
skippedColumns={skippedColumns}
|
||||
significantItems={data.significantItems}
|
||||
groupTableItems={groupTableItems}
|
||||
loading={isRunning}
|
||||
|
@ -493,6 +560,7 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
|||
) : null}
|
||||
{showLogRateAnalysisResultsTable && !groupResults ? (
|
||||
<LogRateAnalysisResultsTable
|
||||
skippedColumns={skippedColumns}
|
||||
significantItems={data.significantItems}
|
||||
loading={isRunning}
|
||||
timeRangeMs={timeRangeMs}
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
export { getGroupTableItems } from './get_group_table_items';
|
||||
export { LogRateAnalysisResultsTable } from './log_rate_analysis_results_table';
|
||||
export { LogRateAnalysisResultsGroupsTable } from './log_rate_analysis_results_table_groups';
|
||||
export type { ColumnNames } from './use_columns';
|
||||
|
|
|
@ -10,41 +10,14 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||
import { orderBy, isEqual } from 'lodash';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
|
||||
import type { EuiBasicTableColumn, EuiTableSortingType } from '@elastic/eui';
|
||||
import {
|
||||
useEuiBackgroundColor,
|
||||
EuiBadge,
|
||||
EuiBasicTable,
|
||||
EuiCode,
|
||||
EuiIconTip,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils';
|
||||
import type { EuiTableSortingType } from '@elastic/eui';
|
||||
import { useEuiBackgroundColor, EuiBasicTable } from '@elastic/eui';
|
||||
import { type SignificantItem } from '@kbn/ml-agg-utils';
|
||||
import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker';
|
||||
|
||||
import { getCategoryQuery } from '@kbn/aiops-log-pattern-analysis/get_category_query';
|
||||
|
||||
import { useLogRateAnalysisStateContext } from '@kbn/aiops-components';
|
||||
import { useEuiTheme } from '../../hooks/use_eui_theme';
|
||||
|
||||
import { MiniHistogram } from '../mini_histogram';
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
|
||||
import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label';
|
||||
import { FieldStatsPopover } from '../field_stats_popover';
|
||||
import { useCopyToClipboardAction } from './use_copy_to_clipboard_action';
|
||||
import { useViewInDiscoverAction } from './use_view_in_discover_action';
|
||||
import { useViewInLogPatternAnalysisAction } from './use_view_in_log_pattern_analysis_action';
|
||||
|
||||
const NARROW_COLUMN_WIDTH = '120px';
|
||||
const ACTIONS_COLUMN_WIDTH = '60px';
|
||||
const UNIQUE_COLUMN_WIDTH = '40px';
|
||||
const NOT_AVAILABLE = '--';
|
||||
import { useColumns, SIG_ITEMS_TABLE } from './use_columns';
|
||||
|
||||
const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50];
|
||||
const DEFAULT_SORT_FIELD = 'pValue';
|
||||
|
@ -52,8 +25,6 @@ const DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK = 'doc_count';
|
|||
const DEFAULT_SORT_DIRECTION = 'asc';
|
||||
const DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK = 'desc';
|
||||
|
||||
const TRUNCATE_TEXT_LINES = 3;
|
||||
|
||||
interface LogRateAnalysisResultsTableProps {
|
||||
significantItems: SignificantItem[];
|
||||
loading: boolean;
|
||||
|
@ -64,6 +35,7 @@ interface LogRateAnalysisResultsTableProps {
|
|||
barColorOverride?: string;
|
||||
/** Optional color override for the highlighted bar color for charts */
|
||||
barHighlightColorOverride?: string;
|
||||
skippedColumns: string[];
|
||||
zeroDocsFallback?: boolean;
|
||||
}
|
||||
|
||||
|
@ -75,12 +47,11 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
|
|||
timeRangeMs,
|
||||
barColorOverride,
|
||||
barHighlightColorOverride,
|
||||
skippedColumns,
|
||||
zeroDocsFallback = false,
|
||||
}) => {
|
||||
const euiTheme = useEuiTheme();
|
||||
const primaryBackgroundColor = useEuiBackgroundColor('primary');
|
||||
const { dataView } = useDataSource();
|
||||
const dataViewId = dataView.id;
|
||||
|
||||
const {
|
||||
pinnedGroup,
|
||||
|
@ -100,263 +71,17 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
|
|||
zeroDocsFallback ? DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK : DEFAULT_SORT_DIRECTION
|
||||
);
|
||||
|
||||
const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext();
|
||||
|
||||
const fieldStatsServices: FieldStatsServices = useMemo(() => {
|
||||
return {
|
||||
uiSettings,
|
||||
dataViews: data.dataViews,
|
||||
data,
|
||||
fieldFormats,
|
||||
charts,
|
||||
};
|
||||
}, [uiSettings, data, fieldFormats, charts]);
|
||||
|
||||
const copyToClipBoardAction = useCopyToClipboardAction();
|
||||
const viewInDiscoverAction = useViewInDiscoverAction(dataViewId);
|
||||
const viewInLogPatternAnalysisAction = useViewInLogPatternAnalysisAction(dataViewId);
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<SignificantItem>> = [
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnFieldName',
|
||||
field: 'fieldName',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldNameLabel', {
|
||||
defaultMessage: 'Field name',
|
||||
}),
|
||||
render: (_, { fieldName, fieldValue, key, type, doc_count: count }) => {
|
||||
const dslQuery =
|
||||
type === SIGNIFICANT_ITEM_TYPE.KEYWORD
|
||||
? searchQuery
|
||||
: getCategoryQuery(fieldName, [
|
||||
{
|
||||
key,
|
||||
count,
|
||||
examples: [],
|
||||
regex: '',
|
||||
},
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
{type === SIGNIFICANT_ITEM_TYPE.KEYWORD && (
|
||||
<FieldStatsPopover
|
||||
dataView={dataView}
|
||||
fieldName={fieldName}
|
||||
fieldValue={type === SIGNIFICANT_ITEM_TYPE.KEYWORD ? fieldValue : key}
|
||||
fieldStatsServices={fieldStatsServices}
|
||||
dslQuery={dslQuery}
|
||||
timeRangeMs={timeRangeMs}
|
||||
/>
|
||||
)}
|
||||
{type === SIGNIFICANT_ITEM_TYPE.LOG_PATTERN && (
|
||||
<EuiIconTip
|
||||
type="aggregate"
|
||||
data-test-subj={'aiopsLogPatternIcon'}
|
||||
css={{ marginLeft: euiTheme.euiSizeS, marginRight: euiTheme.euiSizeXS }}
|
||||
size="m"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.fieldContextPopover.descriptionTooltipLogPattern',
|
||||
{
|
||||
defaultMessage:
|
||||
'The field value for this field shows an example of the identified significant text field pattern.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<span title={fieldName} className="eui-textTruncate">
|
||||
{fieldName}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnFieldValue',
|
||||
field: 'fieldValue',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldValueLabel', {
|
||||
defaultMessage: 'Field value',
|
||||
}),
|
||||
render: (_, { fieldValue, type }) => (
|
||||
<span title={String(fieldValue)}>
|
||||
{type === 'keyword' ? (
|
||||
String(fieldValue)
|
||||
) : (
|
||||
<EuiText size="xs">
|
||||
<EuiCode language="log" transparentBackground css={{ paddingInline: '0px' }}>
|
||||
{String(fieldValue)}
|
||||
</EuiCode>
|
||||
</EuiText>
|
||||
)}
|
||||
</span>
|
||||
),
|
||||
sortable: true,
|
||||
textOnly: true,
|
||||
truncateText: { lines: TRUNCATE_TEXT_LINES },
|
||||
valign: 'middle',
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnLogRate',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.logRateLabel"
|
||||
defaultMessage="Log rate"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
position="top"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'A visual representation of the impact of the field on the message rate difference',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { histogram, fieldName, fieldValue }) => (
|
||||
<MiniHistogram
|
||||
chartData={histogram}
|
||||
isLoading={loading && histogram === undefined}
|
||||
label={`${fieldName}:${fieldValue}`}
|
||||
barColorOverride={barColorOverride}
|
||||
barHighlightColorOverride={barHighlightColorOverride}
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
valign: 'middle',
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnDocCount',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'doc_count',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountLabel', {
|
||||
defaultMessage: 'Doc count',
|
||||
}),
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
];
|
||||
|
||||
if (!zeroDocsFallback) {
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnPValue',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.pValueLabel"
|
||||
defaultMessage="p-value"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
position="top"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.pValueColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'The significance of changes in the frequency of values; lower values indicate greater change; sorting this column will automatically do a secondary sort on the doc count column.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE,
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
});
|
||||
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.impactLabel"
|
||||
defaultMessage="Impact"
|
||||
/>
|
||||
|
||||
<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.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { pValue }) => {
|
||||
if (typeof pValue !== 'number') return NOT_AVAILABLE;
|
||||
const label = getFailedTransactionsCorrelationImpactLabel(pValue);
|
||||
return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
|
||||
},
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
});
|
||||
}
|
||||
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnAction',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnName', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
...(viewInDiscoverAction ? [viewInDiscoverAction] : []),
|
||||
...(viewInLogPatternAnalysisAction ? [viewInLogPatternAnalysisAction] : []),
|
||||
copyToClipBoardAction,
|
||||
],
|
||||
width: ACTIONS_COLUMN_WIDTH,
|
||||
valign: 'middle',
|
||||
});
|
||||
|
||||
if (isExpandedRow === true) {
|
||||
columns.unshift({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnUnique',
|
||||
width: UNIQUE_COLUMN_WIDTH,
|
||||
field: 'unique',
|
||||
name: '',
|
||||
render: (_, { unique }) => {
|
||||
if (unique) {
|
||||
return (
|
||||
<EuiIconTip
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.uniqueColumnTooltip',
|
||||
{
|
||||
defaultMessage: 'This field/value pair only appears in this group',
|
||||
}
|
||||
)}
|
||||
position="top"
|
||||
type="asterisk"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
sortable: false,
|
||||
valign: 'middle',
|
||||
});
|
||||
}
|
||||
const columns = useColumns(
|
||||
SIG_ITEMS_TABLE,
|
||||
skippedColumns,
|
||||
searchQuery,
|
||||
timeRangeMs,
|
||||
loading,
|
||||
zeroDocsFallback,
|
||||
barColorOverride,
|
||||
barHighlightColorOverride,
|
||||
isExpandedRow
|
||||
);
|
||||
|
||||
const onChange = useCallback((tableSettings) => {
|
||||
if (tableSettings.page) {
|
||||
|
|
|
@ -32,20 +32,13 @@ import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker';
|
|||
import { stringHash } from '@kbn/ml-string-hash';
|
||||
import { useLogRateAnalysisStateContext, type GroupTableItem } from '@kbn/aiops-components';
|
||||
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
import useMountedState from 'react-use/lib/useMountedState';
|
||||
|
||||
import { MiniHistogram } from '../mini_histogram';
|
||||
|
||||
import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label';
|
||||
import { LogRateAnalysisResultsTable } from './log_rate_analysis_results_table';
|
||||
import { useCopyToClipboardAction } from './use_copy_to_clipboard_action';
|
||||
import { useViewInDiscoverAction } from './use_view_in_discover_action';
|
||||
import { useViewInLogPatternAnalysisAction } from './use_view_in_log_pattern_analysis_action';
|
||||
import { GROUPS_TABLE, useColumns } from './use_columns';
|
||||
|
||||
const NARROW_COLUMN_WIDTH = '120px';
|
||||
const EXPAND_COLUMN_WIDTH = '40px';
|
||||
const ACTIONS_COLUMN_WIDTH = '60px';
|
||||
const NOT_AVAILABLE = '--';
|
||||
const MAX_GROUP_BADGES = 5;
|
||||
|
||||
const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50];
|
||||
|
@ -55,6 +48,7 @@ const DEFAULT_SORT_DIRECTION = 'asc';
|
|||
const DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK = 'desc';
|
||||
|
||||
interface LogRateAnalysisResultsTableProps {
|
||||
skippedColumns: string[];
|
||||
significantItems: SignificantItem[];
|
||||
groupTableItems: GroupTableItem[];
|
||||
loading: boolean;
|
||||
|
@ -68,6 +62,7 @@ interface LogRateAnalysisResultsTableProps {
|
|||
}
|
||||
|
||||
export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTableProps> = ({
|
||||
skippedColumns,
|
||||
significantItems,
|
||||
groupTableItems,
|
||||
loading,
|
||||
|
@ -77,7 +72,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
barHighlightColorOverride,
|
||||
zeroDocsFallback = false,
|
||||
}) => {
|
||||
const { dataView } = useDataSource();
|
||||
const prevSkippedColumns = usePrevious(skippedColumns);
|
||||
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
@ -97,7 +92,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
|
||||
const { pinnedGroup, selectedGroup, setPinnedGroup, setSelectedGroup } =
|
||||
useLogRateAnalysisStateContext();
|
||||
const dataViewId = dataView.id;
|
||||
const isMounted = useMountedState();
|
||||
|
||||
const toggleDetails = (item: GroupTableItem) => {
|
||||
const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
|
||||
|
@ -106,6 +101,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
} else {
|
||||
itemIdToExpandedRowMapValues[item.id] = (
|
||||
<LogRateAnalysisResultsTable
|
||||
skippedColumns={skippedColumns}
|
||||
significantItems={item.groupItemsSortedByUniqueness.reduce<SignificantItem[]>(
|
||||
(p, groupItem) => {
|
||||
const st = significantItems.find(
|
||||
|
@ -135,11 +131,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
|
||||
};
|
||||
|
||||
const copyToClipBoardAction = useCopyToClipboardAction();
|
||||
const viewInDiscoverAction = useViewInDiscoverAction(dataViewId);
|
||||
const viewInLogPatternAnalysisAction = useViewInLogPatternAnalysisAction(dataViewId);
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<GroupTableItem>> = [
|
||||
const groupColumns: Array<EuiBasicTableColumn<GroupTableItem>> = [
|
||||
{
|
||||
align: RIGHT_ALIGNMENT,
|
||||
width: EXPAND_COLUMN_WIDTH,
|
||||
|
@ -199,7 +191,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
</>
|
||||
),
|
||||
render: (_, { uniqueItemsCount, groupItemsSortedByUniqueness }) => {
|
||||
const valuesBadges = [];
|
||||
const valuesBadges: React.ReactNode[] = [];
|
||||
|
||||
for (const groupItem of groupItemsSortedByUniqueness) {
|
||||
const { fieldName, fieldValue, duplicate } = groupItem;
|
||||
|
@ -259,141 +251,20 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
truncateText: true,
|
||||
valign: 'top',
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsGroupsTableColumnLogRate',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTableGroups.logRateLabel"
|
||||
defaultMessage="Log rate"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
position="top"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTableGroups.logRateColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'A visual representation of the impact of the group on the message rate difference.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { histogram, id }) => (
|
||||
<MiniHistogram
|
||||
chartData={histogram}
|
||||
isLoading={loading && histogram === undefined}
|
||||
label={i18n.translate('xpack.aiops.logRateAnalysis.resultsTableGroups.groupLabel', {
|
||||
defaultMessage: 'Group',
|
||||
})}
|
||||
barColorOverride={barColorOverride}
|
||||
barHighlightColorOverride={barHighlightColorOverride}
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
valign: 'top',
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsGroupsTableColumnDocCount',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'docCount',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountLabel', {
|
||||
defaultMessage: 'Doc count',
|
||||
}),
|
||||
sortable: true,
|
||||
valign: 'top',
|
||||
},
|
||||
];
|
||||
|
||||
if (!zeroDocsFallback) {
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsGroupsTableColumnPValue',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTableGroups.pValueLabel"
|
||||
defaultMessage="p-value"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
position="top"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTableGroups.pValueColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'The significance of changes in the frequency of values; lower values indicate greater change; sorting this column will automatically do a secondary sort on the doc count column.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE,
|
||||
sortable: true,
|
||||
valign: 'top',
|
||||
});
|
||||
const columns = useColumns(
|
||||
GROUPS_TABLE,
|
||||
skippedColumns,
|
||||
searchQuery,
|
||||
timeRangeMs,
|
||||
loading,
|
||||
zeroDocsFallback,
|
||||
barColorOverride,
|
||||
barHighlightColorOverride
|
||||
) as Array<EuiBasicTableColumn<GroupTableItem>>;
|
||||
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabel"
|
||||
defaultMessage="Impact"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
position="top"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip',
|
||||
{
|
||||
defaultMessage: 'The level of impact of the group on the message rate difference',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { pValue }) => {
|
||||
if (!pValue) return NOT_AVAILABLE;
|
||||
const label = getFailedTransactionsCorrelationImpactLabel(pValue);
|
||||
return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
|
||||
},
|
||||
sortable: true,
|
||||
valign: 'top',
|
||||
});
|
||||
}
|
||||
|
||||
columns.push({
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnAction',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnName', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
...(viewInDiscoverAction ? [viewInDiscoverAction] : []),
|
||||
...(viewInLogPatternAnalysisAction ? [viewInLogPatternAnalysisAction] : []),
|
||||
copyToClipBoardAction,
|
||||
],
|
||||
width: ACTIONS_COLUMN_WIDTH,
|
||||
valign: 'top',
|
||||
});
|
||||
groupColumns.push(...columns);
|
||||
|
||||
const onChange = useCallback((tableSettings) => {
|
||||
if (tableSettings.page) {
|
||||
|
@ -481,6 +352,32 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
[]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function updateVisbleColumnsInExpandedRows() {
|
||||
// Update the itemIdToExpandedRowMap results table component for each expanded row with the updated skippedColumns prop
|
||||
// Update the itemIdToExpandedRowMap state after we update the components
|
||||
if (
|
||||
isMounted() &&
|
||||
prevSkippedColumns &&
|
||||
prevSkippedColumns !== skippedColumns &&
|
||||
Object.keys(itemIdToExpandedRowMap)?.length > 0
|
||||
) {
|
||||
const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
|
||||
for (const itemId in itemIdToExpandedRowMapValues) {
|
||||
if (itemIdToExpandedRowMapValues.hasOwnProperty(itemId)) {
|
||||
const component = itemIdToExpandedRowMapValues[itemId];
|
||||
itemIdToExpandedRowMapValues[itemId] = (
|
||||
<LogRateAnalysisResultsTable {...component.props} skippedColumns={skippedColumns} />
|
||||
);
|
||||
}
|
||||
}
|
||||
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[prevSkippedColumns, skippedColumns, itemIdToExpandedRowMap]
|
||||
);
|
||||
|
||||
const getRowStyle = (group: GroupTableItem) => {
|
||||
if (pinnedGroup && pinnedGroup.id === group.id) {
|
||||
return {
|
||||
|
@ -503,7 +400,7 @@ export const LogRateAnalysisResultsGroupsTable: FC<LogRateAnalysisResultsTablePr
|
|||
<EuiBasicTable
|
||||
data-test-subj="aiopsLogRateAnalysisResultsGroupsTable"
|
||||
compressed
|
||||
columns={columns}
|
||||
columns={groupColumns}
|
||||
items={pageOfItems}
|
||||
itemId="id"
|
||||
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
|
||||
|
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { type EuiBasicTableColumn, EuiBadge, EuiCode, EuiIconTip, EuiText } from '@elastic/eui';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils';
|
||||
import { getCategoryQuery } from '@kbn/aiops-log-pattern-analysis/get_category_query';
|
||||
import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker';
|
||||
import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats';
|
||||
import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label';
|
||||
import { FieldStatsPopover } from '../field_stats_popover';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import { useEuiTheme } from '../../hooks/use_eui_theme';
|
||||
import { useViewInDiscoverAction } from './use_view_in_discover_action';
|
||||
import { useViewInLogPatternAnalysisAction } from './use_view_in_log_pattern_analysis_action';
|
||||
import { useCopyToClipboardAction } from './use_copy_to_clipboard_action';
|
||||
import { MiniHistogram } from '../mini_histogram';
|
||||
|
||||
const TRUNCATE_TEXT_LINES = 3;
|
||||
const ACTIONS_COLUMN_WIDTH = '60px';
|
||||
const NARROW_COLUMN_WIDTH = '120px';
|
||||
const UNIQUE_COLUMN_WIDTH = '40px';
|
||||
const NOT_AVAILABLE = '--';
|
||||
|
||||
export const commonColumns = {
|
||||
['Log rate']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTitle', {
|
||||
defaultMessage: 'Log rate',
|
||||
}),
|
||||
['Doc count']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountColumnTitle', {
|
||||
defaultMessage: 'Doc count',
|
||||
}),
|
||||
['p-value']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.pValueColumnTitle', {
|
||||
defaultMessage: 'p-value',
|
||||
}),
|
||||
['Impact']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.impactColumnTitle', {
|
||||
defaultMessage: 'Impact',
|
||||
}),
|
||||
['Actions']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnTitle', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
};
|
||||
|
||||
export const significantItemColumns = {
|
||||
['Field name']: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldNameColumnTitle', {
|
||||
defaultMessage: 'Field name',
|
||||
}),
|
||||
['Field value']: i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.fieldValueColumnTitle',
|
||||
{
|
||||
defaultMessage: 'Field value',
|
||||
}
|
||||
),
|
||||
...commonColumns,
|
||||
} as const;
|
||||
|
||||
export const GROUPS_TABLE = 'groups';
|
||||
export const SIG_ITEMS_TABLE = 'significantItems';
|
||||
type TableType = typeof GROUPS_TABLE | typeof SIG_ITEMS_TABLE;
|
||||
export type ColumnNames = keyof typeof significantItemColumns | 'unique';
|
||||
|
||||
const logRateHelpMessage = i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.logRateColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'A visual representation of the impact of the field on the message rate difference',
|
||||
}
|
||||
);
|
||||
const groupLogRateHelpMessage = i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTableGroups.logRateColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'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.',
|
||||
}
|
||||
);
|
||||
|
||||
export const useColumns = (
|
||||
tableType: TableType,
|
||||
skippedColumns: string[],
|
||||
searchQuery: estypes.QueryDslQueryContainer,
|
||||
timeRangeMs: TimeRangeMs,
|
||||
loading: boolean,
|
||||
zeroDocsFallback: boolean,
|
||||
barColorOverride?: string,
|
||||
barHighlightColorOverride?: string,
|
||||
isExpandedRow: boolean = false
|
||||
): Array<EuiBasicTableColumn<SignificantItem>> => {
|
||||
const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext();
|
||||
const { dataView } = useDataSource();
|
||||
const euiTheme = useEuiTheme();
|
||||
const viewInDiscoverAction = useViewInDiscoverAction(dataView.id);
|
||||
const viewInLogPatternAnalysisAction = useViewInLogPatternAnalysisAction(dataView.id);
|
||||
const copyToClipBoardAction = useCopyToClipboardAction();
|
||||
|
||||
const isGroupsTable = tableType === GROUPS_TABLE;
|
||||
|
||||
const fieldStatsServices: FieldStatsServices = useMemo(() => {
|
||||
return {
|
||||
uiSettings,
|
||||
dataViews: data.dataViews,
|
||||
data,
|
||||
fieldFormats,
|
||||
charts,
|
||||
};
|
||||
}, [uiSettings, data, fieldFormats, charts]);
|
||||
|
||||
const columnsMap: Record<ColumnNames, EuiBasicTableColumn<SignificantItem>> = useMemo(
|
||||
() => ({
|
||||
['Field name']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnFieldName',
|
||||
field: 'fieldName',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldNameLabel', {
|
||||
defaultMessage: 'Field name',
|
||||
}),
|
||||
render: (_, { fieldName, fieldValue, key, type, doc_count: count }) => {
|
||||
const dslQuery =
|
||||
type === SIGNIFICANT_ITEM_TYPE.KEYWORD
|
||||
? searchQuery
|
||||
: getCategoryQuery(fieldName, [
|
||||
{
|
||||
key,
|
||||
count,
|
||||
examples: [],
|
||||
regex: '',
|
||||
},
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
{type === SIGNIFICANT_ITEM_TYPE.KEYWORD && (
|
||||
<FieldStatsPopover
|
||||
dataView={dataView}
|
||||
fieldName={fieldName}
|
||||
fieldValue={type === SIGNIFICANT_ITEM_TYPE.KEYWORD ? fieldValue : key}
|
||||
fieldStatsServices={fieldStatsServices}
|
||||
dslQuery={dslQuery}
|
||||
timeRangeMs={timeRangeMs}
|
||||
/>
|
||||
)}
|
||||
{type === SIGNIFICANT_ITEM_TYPE.LOG_PATTERN && (
|
||||
<EuiIconTip
|
||||
type="aggregate"
|
||||
data-test-subj={'aiopsLogPatternIcon'}
|
||||
css={{ marginLeft: euiTheme.euiSizeS, marginRight: euiTheme.euiSizeXS }}
|
||||
size="m"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.fieldContextPopover.descriptionTooltipLogPattern',
|
||||
{
|
||||
defaultMessage:
|
||||
'The field value for this field shows an example of the identified significant text field pattern.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<span title={fieldName} className="eui-textTruncate">
|
||||
{fieldName}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
['Field value']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnFieldValue',
|
||||
field: 'fieldValue',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.fieldValueLabel', {
|
||||
defaultMessage: 'Field value',
|
||||
}),
|
||||
render: (_, { fieldValue, type }) => (
|
||||
<span title={String(fieldValue)}>
|
||||
{type === 'keyword' ? (
|
||||
String(fieldValue)
|
||||
) : (
|
||||
<EuiText size="xs">
|
||||
<EuiCode language="log" transparentBackground css={{ paddingInline: '0px' }}>
|
||||
{String(fieldValue)}
|
||||
</EuiCode>
|
||||
</EuiText>
|
||||
)}
|
||||
</span>
|
||||
),
|
||||
sortable: true,
|
||||
textOnly: true,
|
||||
truncateText: { lines: TRUNCATE_TEXT_LINES },
|
||||
valign: 'middle',
|
||||
},
|
||||
['Log rate']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnLogRate',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.logRateLabel"
|
||||
defaultMessage="Log rate"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
position="top"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
content={isGroupsTable ? groupLogRateHelpMessage : logRateHelpMessage}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { histogram, fieldName, fieldValue }) => (
|
||||
<MiniHistogram
|
||||
chartData={histogram}
|
||||
isLoading={loading && histogram === undefined}
|
||||
label={`${fieldName}:${fieldValue}`}
|
||||
barColorOverride={barColorOverride}
|
||||
barHighlightColorOverride={barHighlightColorOverride}
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
valign: 'middle',
|
||||
},
|
||||
['Impact']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.impactLabel"
|
||||
defaultMessage="Impact"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
position="top"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
content={isGroupsTable ? groupImpactMessage : impactMessage}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (_, { pValue }) => {
|
||||
if (typeof pValue !== 'number') return NOT_AVAILABLE;
|
||||
const label = getFailedTransactionsCorrelationImpactLabel(pValue);
|
||||
return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
|
||||
},
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
['p-value']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnPValue',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.logRateAnalysis.resultsTable.pValueLabel"
|
||||
defaultMessage="p-value"
|
||||
/>
|
||||
|
||||
<EuiIconTip
|
||||
size="s"
|
||||
position="top"
|
||||
color="subdued"
|
||||
type="questionInCircle"
|
||||
className="eui-alignTop"
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.pValueColumnTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'The significance of changes in the frequency of values; lower values indicate greater change; sorting this column will automatically do a secondary sort on the doc count column.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE,
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
['Doc count']: {
|
||||
'data-test-subj': isGroupsTable
|
||||
? 'aiopsLogRateAnalysisResultsGroupsTableColumnDocCount'
|
||||
: 'aiopsLogRateAnalysisResultsTableColumnDocCount',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: isGroupsTable ? 'docCount' : 'doc_count',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.docCountLabel', {
|
||||
defaultMessage: 'Doc count',
|
||||
}),
|
||||
sortable: true,
|
||||
valign: 'middle',
|
||||
},
|
||||
['Actions']: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnAction',
|
||||
name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnName', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
...(viewInDiscoverAction ? [viewInDiscoverAction] : []),
|
||||
...(viewInLogPatternAnalysisAction ? [viewInLogPatternAnalysisAction] : []),
|
||||
copyToClipBoardAction,
|
||||
],
|
||||
width: ACTIONS_COLUMN_WIDTH,
|
||||
valign: 'middle',
|
||||
},
|
||||
unique: {
|
||||
'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnUnique',
|
||||
width: UNIQUE_COLUMN_WIDTH,
|
||||
field: 'unique',
|
||||
name: '',
|
||||
render: (_, { unique }) => {
|
||||
if (unique) {
|
||||
return (
|
||||
<EuiIconTip
|
||||
content={i18n.translate(
|
||||
'xpack.aiops.logRateAnalysis.resultsTable.uniqueColumnTooltip',
|
||||
{
|
||||
defaultMessage: 'This field/value pair only appears in this group',
|
||||
}
|
||||
)}
|
||||
position="top"
|
||||
type="asterisk"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
sortable: false,
|
||||
valign: 'middle',
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
barColorOverride,
|
||||
barHighlightColorOverride,
|
||||
copyToClipBoardAction,
|
||||
dataView?.id,
|
||||
euiTheme,
|
||||
fieldStatsServices,
|
||||
loading,
|
||||
searchQuery,
|
||||
timeRangeMs,
|
||||
viewInDiscoverAction,
|
||||
viewInLogPatternAnalysisAction,
|
||||
]
|
||||
);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
const columnNamesToReturn: Partial<Record<ColumnNames, string>> = isGroupsTable
|
||||
? commonColumns
|
||||
: significantItemColumns;
|
||||
const columnsToReturn = [];
|
||||
|
||||
for (const columnName in columnNamesToReturn) {
|
||||
if (
|
||||
columnNamesToReturn.hasOwnProperty(columnName) === false ||
|
||||
skippedColumns.includes(columnNamesToReturn[columnName as ColumnNames] as string) ||
|
||||
((columnName === 'p-value' || columnName === 'Impact') && zeroDocsFallback)
|
||||
)
|
||||
continue;
|
||||
|
||||
columnsToReturn.push(columnsMap[columnName as ColumnNames]);
|
||||
}
|
||||
|
||||
if (isExpandedRow === true) {
|
||||
columnsToReturn.unshift(columnsMap.unique);
|
||||
}
|
||||
|
||||
return columnsToReturn;
|
||||
}, [isGroupsTable, skippedColumns, zeroDocsFallback, isExpandedRow, columnsMap]);
|
||||
|
||||
return columns;
|
||||
};
|
|
@ -8200,7 +8200,6 @@
|
|||
"xpack.aiops.analysis.analysisTypeSpikeCallOutContentFallback": "La plage temporelle de référence de base ne contient aucun document. Les résultats montrent donc les catégories de message des meilleurs logs et les valeurs des champs pour la plage temporelle de déviation.",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeCallOutTitle": "Type d'analyse : Pic du taux de log",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeFallbackCallOutTitle": "Type d'analyse : Meilleurs éléments pour la plage temporelle de déviation",
|
||||
"xpack.aiops.analysis.fieldSelectorAriaLabel": "Champs de filtre",
|
||||
"xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected": "Le regroupement nécessite la sélection d'au moins 2 champs.",
|
||||
"xpack.aiops.analysis.fieldSelectorPlaceholder": "Recherche",
|
||||
"xpack.aiops.analysisCompleteLabel": "Analyse terminée",
|
||||
|
@ -8336,12 +8335,7 @@
|
|||
"xpack.aiops.logRateAnalysis.page.emptyPromptBody": "La fonction d'analyse des pics de taux de log identifie les combinaisons champ/valeur statistiquement significatives qui contribuent à un pic ou une baisse de taux de log.",
|
||||
"xpack.aiops.logRateAnalysis.page.emptyPromptTitle": "Cliquez sur un pic ou une baisse dans l'histogramme pour lancer l'analyse.",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterApplyButtonLabel": "Appliquer",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterButtonLabel": "Champs de filtre",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterHelpText": "Désélectionnez les champs non pertinents pour les supprimer des groupes et cliquez sur le bouton Appliquer pour réexécuter le regroupement. Utilisez la barre de recherche pour filtrer la liste, puis sélectionnez/désélectionnez plusieurs champs avec les actions ci-dessous.",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllFields": "Désélectionner tous les champs",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllSearchedFields": "Désélectionner les champs filtrés",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllFields": "Sélectionner tous les champs",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllSearchedFields": "Sélectionner les champs filtrés",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptBody": "Essayez d'ajuster la référence de base et les plages temporelles d'écart-type, et réexécutez l'analyse. Si vous n'obtenez toujours aucun résultat, il se peut qu'il n'y ait aucune entité statistiquement significative contribuant à cet écart dans les taux de log.",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptTitle": "L'analyse n'a retourné aucun résultat.",
|
||||
"xpack.aiops.logRateAnalysis.page.tryToContinueAnalysisButtonText": "Essayer de continuer l'analyse",
|
||||
|
@ -8375,12 +8369,8 @@
|
|||
"xpack.aiops.logRateAnalysis.resultsTable.pValueLabel": "valeur-p",
|
||||
"xpack.aiops.logRateAnalysis.resultsTable.uniqueColumnTooltip": "Cette paire champ/valeur apparaît uniquement dans ce groupe",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.groupLabel": "Regrouper",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabel": "Impact",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip": "Niveau d'impact du groupe sur la différence de taux de messages",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateColumnTooltip": "Représentation visuelle de l'impact du groupe sur la différence de taux de messages.",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateLabel": "Taux du log",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueColumnTooltip": "Une différence de fréquence des valeurs, surtout si celles-ci sont plus faibles, indique un changement important. Ordonner un tri de cette colonne entraînera automatiquement un tri secondaire sur la colonne \"nombre de documents\"..",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueLabel": "valeur-p",
|
||||
"xpack.aiops.logRateAnalysisTimeSeriesWarning.description": "L'analyse des taux de log ne fonctionne que sur des index temporels.",
|
||||
"xpack.aiops.miniHistogram.noDataLabel": "S. O.",
|
||||
"xpack.aiops.navMenu.mlAppNameText": "Machine Learning",
|
||||
|
|
|
@ -8188,7 +8188,6 @@
|
|||
"xpack.aiops.analysis.analysisTypeSpikeCallOutContentFallback": "ベースライン時間範囲にはドキュメントが含まれていません。したがって、結果は、偏差時間範囲の上位のログメッセージカテゴリーとフィールド値を示しています。",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeCallOutTitle": "分析タイプ:ログレートスパイク",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeFallbackCallOutTitle": "分析タイプ:偏差時間範囲の上位のアイテム",
|
||||
"xpack.aiops.analysis.fieldSelectorAriaLabel": "フィールドのフィルタリング",
|
||||
"xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected": "グループ化するには、2つ以上のフィールドを選択する必要があります。",
|
||||
"xpack.aiops.analysis.fieldSelectorPlaceholder": "検索",
|
||||
"xpack.aiops.analysisCompleteLabel": "分析完了",
|
||||
|
@ -8324,12 +8323,7 @@
|
|||
"xpack.aiops.logRateAnalysis.page.emptyPromptBody": "ログレート分析機能は、ログレートのスパイクまたはディップに寄与する統計的に有意なフィールド/値の組み合わせを特定します。",
|
||||
"xpack.aiops.logRateAnalysis.page.emptyPromptTitle": "ヒストグラム図のスパイクまたはディップをクリックすると、分析が開始します。",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterApplyButtonLabel": "適用",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterButtonLabel": "フィールドのフィルタリング",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterHelpText": "関連しないフィールドを選択解除すると、グループから削除されます。[適用]ボタンをクリックすると、グループ化が再実行されます。 検索バーを使用してリストをフィルタリングしてから、下のアクションを使用して複数のフィールドを選択/選択解除します。",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllFields": "すべてのフィールドを選択解除",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllSearchedFields": "フィルタリングされたフィールドを選択解除",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllFields": "すべてのフィールドを選択",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllSearchedFields": "フィルタリングされたフィールドを選択",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptBody": "ベースラインと時間範囲のずれを調整し、分析を再実行してください。結果が得られない場合は、このログレートの偏差に寄与する統計的に有意なエンティティが存在しない可能性があります。",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptTitle": "分析の結果が返されませんでした。",
|
||||
"xpack.aiops.logRateAnalysis.page.tryToContinueAnalysisButtonText": "分析を続行してください",
|
||||
|
@ -8363,12 +8357,8 @@
|
|||
"xpack.aiops.logRateAnalysis.resultsTable.pValueLabel": "p値",
|
||||
"xpack.aiops.logRateAnalysis.resultsTable.uniqueColumnTooltip": "このフィールド/値の組み合わせはこのグループでのみ出現します",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.groupLabel": "グループ",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabel": "インパクト",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip": "メッセージレート差異に対するグループの影響のレベル",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateColumnTooltip": "メッセージレート差異に対するグループの影響の視覚的な表示。",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateLabel": "ログレート",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueColumnTooltip": "値の頻度の変化の重要性。値が小さいほど変化が大きいことを示します。この列をソートすると、自動的にdoc count列が二次ソートされます。",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueLabel": "p値",
|
||||
"xpack.aiops.logRateAnalysisTimeSeriesWarning.description": "ログレートは、時間ベースのインデックスに対してのみ実行されます。",
|
||||
"xpack.aiops.miniHistogram.noDataLabel": "N/A",
|
||||
"xpack.aiops.navMenu.mlAppNameText": "機械学習",
|
||||
|
|
|
@ -8204,7 +8204,6 @@
|
|||
"xpack.aiops.analysis.analysisTypeSpikeCallOutContentFallback": "基线时间范围不包含任何文档。因此,结果将显示偏差时间范围的主要日志消息类别和字段值。",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeCallOutTitle": "分析类型:日志速率峰值",
|
||||
"xpack.aiops.analysis.analysisTypeSpikeFallbackCallOutTitle": "分析类型:偏差时间范围的主要项目",
|
||||
"xpack.aiops.analysis.fieldSelectorAriaLabel": "筛选字段",
|
||||
"xpack.aiops.analysis.fieldSelectorNotEnoughFieldsSelected": "分组至少需要 2 个供选择的字段。",
|
||||
"xpack.aiops.analysis.fieldSelectorPlaceholder": "搜索",
|
||||
"xpack.aiops.analysisCompleteLabel": "分析已完成",
|
||||
|
@ -8340,12 +8339,7 @@
|
|||
"xpack.aiops.logRateAnalysis.page.emptyPromptBody": "“日志速率分析”功能会识别有助于达到日志速率峰值或谷值的具有统计意义的字段/值组合。",
|
||||
"xpack.aiops.logRateAnalysis.page.emptyPromptTitle": "单击直方图中的某个峰值或谷值可开始分析。",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterApplyButtonLabel": "应用",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterButtonLabel": "筛选字段",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldFilterHelpText": "取消选择非相关字段以将其从组中移除,然后单击“应用”按钮返回分组。 使用搜索栏筛选列表,然后通过以下操作选择/取消选择多个字段。",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllFields": "取消选择所有字段",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.deselectAllSearchedFields": "取消选择已筛选字段",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllFields": "选择所有字段",
|
||||
"xpack.aiops.logRateAnalysis.page.fieldSelector.selectAllSearchedFields": "选择已筛选字段",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptBody": "尝试调整基线和偏差时间范围,然后重新运行分析。如果仍然没有结果,可能没有具有统计意义的实体导致了该日志速率偏差。",
|
||||
"xpack.aiops.logRateAnalysis.page.noResultsPromptTitle": "分析未返回任何结果。",
|
||||
"xpack.aiops.logRateAnalysis.page.tryToContinueAnalysisButtonText": "尝试继续分析",
|
||||
|
@ -8379,12 +8373,8 @@
|
|||
"xpack.aiops.logRateAnalysis.resultsTable.pValueLabel": "p-value",
|
||||
"xpack.aiops.logRateAnalysis.resultsTable.uniqueColumnTooltip": "此字段/值对只在该分组中出现",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.groupLabel": "组",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabel": "影响",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.impactLabelColumnTooltip": "组对消息速率差异的影响水平",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateColumnTooltip": "组对消息速率差异的影响的视觉表示形式。",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.logRateLabel": "日志速率",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueColumnTooltip": "值的频率更改的意义;值越小表示变化越大;对此列排序会自动在文档计数列上进行二次排序。",
|
||||
"xpack.aiops.logRateAnalysis.resultsTableGroups.pValueLabel": "p-value",
|
||||
"xpack.aiops.logRateAnalysisTimeSeriesWarning.description": "日志速率分析仅在基于时间的索引上运行。",
|
||||
"xpack.aiops.miniHistogram.noDataLabel": "不可用",
|
||||
"xpack.aiops.navMenu.mlAppNameText": "Machine Learning",
|
||||
|
|
|
@ -215,6 +215,27 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await aiops.logRateAnalysisResultsGroupsTable.expandRow();
|
||||
await aiops.logRateAnalysisResultsGroupsTable.scrollAnalysisTableIntoView();
|
||||
|
||||
await ml.testExecution.logTestStep('open the column filter');
|
||||
await aiops.logRateAnalysisPage.assertFilterPopoverButtonExists(
|
||||
'aiopsColumnFilterButton',
|
||||
false
|
||||
);
|
||||
await aiops.logRateAnalysisPage.clickFilterPopoverButton('aiopsColumnFilterButton', true);
|
||||
await aiops.logRateAnalysisPage.assertFieldSelectorFieldNameList(
|
||||
testData.expected.columnSelectorPopover
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('filter columns');
|
||||
await aiops.logRateAnalysisPage.setFieldSelectorSearch(testData.columnSelectorSearch);
|
||||
await aiops.logRateAnalysisPage.assertFieldSelectorFieldNameList([
|
||||
testData.columnSelectorSearch,
|
||||
]);
|
||||
await aiops.logRateAnalysisPage.clickFieldSelectorListItem(
|
||||
'aiopsFieldSelectorFieldNameListItem'
|
||||
);
|
||||
await aiops.logRateAnalysisPage.assertFieldFilterApplyButtonExists(false);
|
||||
await aiops.logRateAnalysisPage.clickFieldFilterApplyButton('aiopsColumnFilterButton');
|
||||
|
||||
const analysisTable = await aiops.logRateAnalysisResultsTable.parseAnalysisTable();
|
||||
|
||||
const actualAnalysisTable = orderBy(analysisTable, ['fieldName', 'fieldValue']);
|
||||
|
@ -231,8 +252,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
await ml.testExecution.logTestStep('open the field filter');
|
||||
await aiops.logRateAnalysisPage.assertFieldFilterPopoverButtonExists(false);
|
||||
await aiops.logRateAnalysisPage.clickFieldFilterPopoverButton(true);
|
||||
await aiops.logRateAnalysisPage.assertFilterPopoverButtonExists(
|
||||
'aiopsFieldFilterButton',
|
||||
false
|
||||
);
|
||||
await aiops.logRateAnalysisPage.clickFilterPopoverButton('aiopsFieldFilterButton', true);
|
||||
await aiops.logRateAnalysisPage.assertFieldSelectorFieldNameList(
|
||||
testData.expected.fieldSelectorPopover
|
||||
);
|
||||
|
@ -249,7 +273,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
if (testData.fieldSelectorApplyAvailable) {
|
||||
await ml.testExecution.logTestStep('regroup results');
|
||||
await aiops.logRateAnalysisPage.clickFieldFilterApplyButton();
|
||||
await aiops.logRateAnalysisPage.clickFieldFilterApplyButton('aiopsFieldFilterButton');
|
||||
|
||||
const filteredAnalysisGroupsTable =
|
||||
await aiops.logRateAnalysisResultsGroupsTable.parseAnalysisTable();
|
||||
|
|
|
@ -143,6 +143,7 @@ export const getArtificialLogDataViewTestData = ({
|
|||
brushDeviationTargetTimestamp: getBrushDeviationTargetTimestamp(),
|
||||
brushIntervalFactor: zeroDocsFallback ? 1 : 10,
|
||||
chartClickCoordinates: [-200, 30],
|
||||
columnSelectorSearch: 'p-value',
|
||||
fieldSelectorSearch: 'user',
|
||||
fieldSelectorApplyAvailable: true,
|
||||
expected: {
|
||||
|
@ -150,6 +151,7 @@ export const getArtificialLogDataViewTestData = ({
|
|||
analysisGroupsTable: getAnalysisGroupsTable(),
|
||||
filteredAnalysisGroupsTable: getFilteredAnalysisGroupsTable(),
|
||||
analysisTable: getAnalysisTable(),
|
||||
columnSelectorPopover: ['Log rate', 'Doc count', 'p-value', 'Impact', 'Actions'],
|
||||
fieldSelectorPopover: getFieldSelectorPopover(),
|
||||
globalState: {
|
||||
refreshInterval: { pause: true, value: 60000 },
|
||||
|
|
|
@ -19,11 +19,13 @@ export const farequoteDataViewTestData: TestData = {
|
|||
brushDeviationTargetTimestamp: 1455033600000,
|
||||
brushIntervalFactor: 1,
|
||||
chartClickCoordinates: [0, 0],
|
||||
columnSelectorSearch: 'p-value',
|
||||
fieldSelectorSearch: 'airline',
|
||||
fieldSelectorApplyAvailable: false,
|
||||
expected: {
|
||||
totalDocCountFormatted: '86,374',
|
||||
sampleProbabilityFormatted: '0.5',
|
||||
columnSelectorPopover: ['Log rate', 'Doc count', 'p-value', 'Impact', 'Actions'],
|
||||
fieldSelectorPopover: ['airline', 'custom_field.keyword'],
|
||||
globalState: {
|
||||
refreshInterval: { pause: true, value: 60000 },
|
||||
|
|
|
@ -19,6 +19,7 @@ export const farequoteDataViewTestDataWithQuery: TestData = {
|
|||
brushDeviationTargetTimestamp: 1455033600000,
|
||||
brushIntervalFactor: 1,
|
||||
chartClickCoordinates: [0, 0],
|
||||
columnSelectorSearch: 'p-value',
|
||||
fieldSelectorSearch: 'airline',
|
||||
fieldSelectorApplyAvailable: false,
|
||||
query: 'NOT airline:("SWR" OR "ACA" OR "AWE" OR "BAW" OR "JAL" OR "JBU" OR "JZA" OR "KLM")',
|
||||
|
@ -43,6 +44,7 @@ export const farequoteDataViewTestDataWithQuery: TestData = {
|
|||
impact: 'High',
|
||||
},
|
||||
],
|
||||
columnSelectorPopover: ['Log rate', 'Doc count', 'p-value', 'Impact', 'Actions'],
|
||||
fieldSelectorPopover: ['airline', 'custom_field.keyword'],
|
||||
globalState: {
|
||||
refreshInterval: { pause: true, value: 60000 },
|
||||
|
|
|
@ -19,6 +19,7 @@ export const kibanaLogsDataViewTestData: TestData = {
|
|||
sourceIndexOrSavedSearch: 'kibana_sample_data_logstsdb',
|
||||
brushIntervalFactor: 1,
|
||||
chartClickCoordinates: [235, 0],
|
||||
columnSelectorSearch: 'p-value',
|
||||
fieldSelectorSearch: 'referer',
|
||||
fieldSelectorApplyAvailable: true,
|
||||
action: {
|
||||
|
@ -69,6 +70,7 @@ export const kibanaLogsDataViewTestData: TestData = {
|
|||
logRate: 'Chart type:bar chart',
|
||||
impact: 'High',
|
||||
})),
|
||||
columnSelectorPopover: ['Log rate', 'Doc count', 'p-value', 'Impact', 'Actions'],
|
||||
fieldSelectorPopover: [
|
||||
'agent.keyword',
|
||||
'clientip',
|
||||
|
|
|
@ -98,7 +98,7 @@ const testData: TestData[] = [
|
|||
fieldValue: 'AAL',
|
||||
impact: 'High',
|
||||
logRate: 'Chart type:bar chart',
|
||||
pValue: '8.96e-49',
|
||||
pValue: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -44,9 +44,10 @@ interface TestDataExpectedWithoutSampleProbability {
|
|||
fieldName: string;
|
||||
fieldValue: string;
|
||||
logRate: string;
|
||||
pValue: string;
|
||||
pValue?: string;
|
||||
impact: string;
|
||||
}>;
|
||||
columnSelectorPopover: string[];
|
||||
fieldSelectorPopover: string[];
|
||||
prompt: 'empty' | 'change-point';
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ export interface TestData {
|
|||
brushDeviationTargetTimestamp?: number;
|
||||
brushIntervalFactor: number;
|
||||
chartClickCoordinates: [number, number];
|
||||
columnSelectorSearch: string;
|
||||
fieldSelectorSearch: string;
|
||||
fieldSelectorApplyAvailable: boolean;
|
||||
query?: string;
|
||||
|
|
|
@ -184,9 +184,9 @@ export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrPr
|
|||
});
|
||||
},
|
||||
|
||||
async assertFieldFilterPopoverButtonExists(isOpen: boolean) {
|
||||
async assertFilterPopoverButtonExists(selector: string, isOpen: boolean) {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail('aiopsFieldFilterButton');
|
||||
await testSubjects.existOrFail(selector);
|
||||
|
||||
if (isOpen) {
|
||||
await testSubjects.existOrFail('aiopsFieldSelectorSearch');
|
||||
|
@ -196,11 +196,11 @@ export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrPr
|
|||
});
|
||||
},
|
||||
|
||||
async clickFieldFilterPopoverButton(expectPopoverToBeOpen: boolean) {
|
||||
await testSubjects.clickWhenNotDisabledWithoutRetry('aiopsFieldFilterButton');
|
||||
async clickFilterPopoverButton(selector: string, expectPopoverToBeOpen: boolean) {
|
||||
await testSubjects.clickWhenNotDisabledWithoutRetry(selector);
|
||||
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
await this.assertFieldFilterPopoverButtonExists(expectPopoverToBeOpen);
|
||||
await this.assertFilterPopoverButtonExists(selector, expectPopoverToBeOpen);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -246,11 +246,17 @@ export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrPr
|
|||
});
|
||||
},
|
||||
|
||||
async clickFieldFilterApplyButton() {
|
||||
async clickFieldFilterApplyButton(selector: string) {
|
||||
await testSubjects.clickWhenNotDisabledWithoutRetry('aiopsFieldFilterApplyButton');
|
||||
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
await this.assertFieldFilterPopoverButtonExists(false);
|
||||
await this.assertFilterPopoverButtonExists(selector, false);
|
||||
});
|
||||
},
|
||||
|
||||
async clickFieldSelectorListItem(selector: string) {
|
||||
await retry.tryForTime(5 * 1000, async () => {
|
||||
await testSubjects.click(selector);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue