Skip to content

Commit

Permalink
[ML] AIOps: Fix to not run log rate analysis twice when no spike/dip …
Browse files Browse the repository at this point in the history
…detected. (#180980)

## Summary

Part of #172981.

This fixes to not run log rate analysis twice when no spike/dip detected
and a user needs to adapt the initial selection. When a user clicks in
an area of the histogram chart that's not a highlighted change point,
the click will just trigger the baseline/deviation time range selection,
but it will not automatically run the analysis. Instead, an updated
prompt is shown below the chart that explains that the
baseline/deviation can be adjusted via dragging and the analysis can be
run via the button below that description.

Initial view after loading the page:

<img width="1040" alt="image"
src="https://github.com/elastic/kibana/assets/230104/90e8c390-af2a-45e2-8d11-cfd42285200b">

User clicked in an area that's not covered by the highlighted change
point:

<img width="1026" alt="image"
src="https://github.com/elastic/kibana/assets/230104/050a07e0-c5e6-4639-a854-83fae10b125b">


### Checklist

Delete any items that are not applicable to this PR.

- [x] 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)
- [x] [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
- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
walterra committed Apr 18, 2024
1 parent 87c0451 commit 6fdcd8d
Show file tree
Hide file tree
Showing 34 changed files with 312 additions and 81 deletions.
8 changes: 8 additions & 0 deletions x-pack/packages/ml/aiops_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ export { DualBrush, DualBrushAnnotation } from './src/dual_brush';
export { ProgressControls } from './src/progress_controls';
export {
DocumentCountChart,
DocumentCountChartWithAutoAnalysisStart,
type BrushSettings,
type BrushSelectionUpdateHandler,
} from './src/document_count_chart';
export type { DocumentCountChartProps } from './src/document_count_chart';
export {
useLogRateAnalysisStateContext,
LogRateAnalysisStateProvider,
type GroupTableItem,
type GroupTableItemGroup,
type TableItemAction,
} from './src/log_rate_analysis_state_provider';
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';

import { DualBrush, DualBrushAnnotation } from '../..';

import { useLogRateAnalysisStateContext } from '../log_rate_analysis_state_provider';

import { BrushBadge } from './brush_badge';

declare global {
Expand Down Expand Up @@ -87,6 +89,11 @@ export type BrushSelectionUpdateHandler = (
logRateAnalysisType: LogRateAnalysisType
) => void;

/**
* Callback to set the autoRunAnalysis flag
*/
type SetAutoRunAnalysisFn = (isAutoRun: boolean) => void;

/**
* Props for document count chart
*/
Expand Down Expand Up @@ -118,9 +125,11 @@ export interface DocumentCountChartProps {
chartPointsSplitLabel: string;
/** Whether or not brush has been reset */
isBrushCleared: boolean;
/** Callback to set the autoRunAnalysis flag */
setAutoRunAnalysis?: SetAutoRunAnalysisFn;
/** Timestamp for start of initial analysis */
autoAnalysisStart?: number | WindowParameters;
/** Optional style to override bar chart */
/** Optional style to override bar chart */
barStyleAccessor?: BarStyleAccessor;
/** Optional color override for the default bar color for charts */
barColorOverride?: string;
Expand Down Expand Up @@ -181,6 +190,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
interval,
chartPointsSplitLabel,
isBrushCleared,
setAutoRunAnalysis,
autoAnalysisStart,
barColorOverride,
barStyleAccessor,
Expand Down Expand Up @@ -305,6 +315,17 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
windowParameters === undefined &&
adjustedChartPoints !== undefined
) {
if (setAutoRunAnalysis) {
const autoRun =
typeof startRange !== 'number' ||
(typeof startRange === 'number' &&
changePoint !== undefined &&
startRange >= changePoint.startTs &&
startRange <= changePoint.endTs);

setAutoRunAnalysis(autoRun);
}

const wp = getWindowParametersForTrigger(
startRange,
interval,
Expand Down Expand Up @@ -333,6 +354,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
timeRangeLatest,
snapTimestamps,
originalWindowParameters,
setAutoRunAnalysis,
setWindowParameters,
brushSelectionUpdateHandler,
adjustedChartPoints,
Expand Down Expand Up @@ -535,3 +557,24 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
</>
);
};

/**
* Functional component that renders a `DocumentCountChart` with additional properties
* managed by the log rate analysis state. It leverages the `useLogRateAnalysisStateContext`
* to acquire state variables like `initialAnalysisStart` and functions such as
* `setAutoRunAnalysis`. These values are then passed as props to the `DocumentCountChart`.
*
* @param props - The properties passed to the DocumentCountChart component.
* @returns The DocumentCountChart component enhanced with automatic analysis start capabilities.
*/
export const DocumentCountChartWithAutoAnalysisStart: FC<DocumentCountChartProps> = (props) => {
const { initialAnalysisStart, setAutoRunAnalysis } = useLogRateAnalysisStateContext();

return (
<DocumentCountChart
{...props}
autoAnalysisStart={initialAnalysisStart}
setAutoRunAnalysis={setAutoRunAnalysis}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
* 2.0.
*/

export { DocumentCountChart } from './document_count_chart';
export {
DocumentCountChart,
DocumentCountChartWithAutoAnalysisStart,
} from './document_count_chart';
export type {
BrushSelectionUpdateHandler,
BrushSettings,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

export {
useLogRateAnalysisStateContext,
LogRateAnalysisStateProvider,
} from './log_rate_analysis_state_provider';
export type { GroupTableItem, GroupTableItemGroup, TableItemAction } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ import React, {
} from 'react';

import type { SignificantItem } from '@kbn/ml-agg-utils';
import type { WindowParameters } from '@kbn/aiops-log-rate-analysis';

import type { GroupTableItem } from './types';

type InitialAnalysisStart = number | WindowParameters | undefined;
type SignificantItemOrNull = SignificantItem | null;
type GroupOrNull = GroupTableItem | null;

interface LogRateAnalysisResultsTableRow {
interface LogRateAnalysisState {
autoRunAnalysis: boolean;
setAutoRunAnalysis: Dispatch<SetStateAction<boolean>>;
initialAnalysisStart: InitialAnalysisStart;
setInitialAnalysisStart: Dispatch<SetStateAction<InitialAnalysisStart>>;
pinnedSignificantItem: SignificantItemOrNull;
setPinnedSignificantItem: Dispatch<SetStateAction<SignificantItemOrNull>>;
pinnedGroup: GroupOrNull;
Expand All @@ -36,12 +42,38 @@ interface LogRateAnalysisResultsTableRow {
clearAllRowState: () => void;
}

export const logRateAnalysisResultsTableRowContext = createContext<
LogRateAnalysisResultsTableRow | undefined
>(undefined);
const LogRateAnalysisStateContext = createContext<LogRateAnalysisState | undefined>(undefined);

export const LogRateAnalysisResultsTableRowStateProvider: FC = ({ children }) => {
// State that will be shared with all components
/**
* Props for LogRateAnalysisStateProvider.
*/
interface LogRateAnalysisStateProviderProps {
/** The parameters to be used to trigger an analysis. */
initialAnalysisStart?: InitialAnalysisStart;
}

/**
* Context provider component that manages and provides global state for Log Rate Analysis.
* This provider handles several pieces of state important for controlling and displaying
* log rate analysis data, such as the control of automatic analysis runs, and the management
* of both pinned and selected significant items and groups.
*
* The state includes mechanisms for setting initial analysis parameters, toggling analysis,
* and managing the current selection and pinned state of significant items and groups.
*
* @param props - Props object containing initial settings for the analysis,
* including children components to be wrapped by the Provider.
* @returns A context provider wrapping children with access to log rate analysis state.
*/
export const LogRateAnalysisStateProvider: FC<LogRateAnalysisStateProviderProps> = (props) => {
const { children, initialAnalysisStart: incomingInitialAnalysisStart } = props;

const [autoRunAnalysis, setAutoRunAnalysis] = useState(true);
const [initialAnalysisStart, setInitialAnalysisStart] = useState<
number | WindowParameters | undefined
>(incomingInitialAnalysisStart);

// Row state that will be shared with all components
const [pinnedSignificantItem, setPinnedSignificantItem] = useState<SignificantItemOrNull>(null);
const [pinnedGroup, setPinnedGroup] = useState<GroupOrNull>(null);
const [selectedSignificantItem, setSelectedSignificantItem] =
Expand All @@ -66,8 +98,12 @@ export const LogRateAnalysisResultsTableRowStateProvider: FC = ({ children }) =>
}
}, [selectedGroup, pinnedGroup]);

const contextValue: LogRateAnalysisResultsTableRow = useMemo(
const contextValue: LogRateAnalysisState = useMemo(
() => ({
autoRunAnalysis,
setAutoRunAnalysis,
initialAnalysisStart,
setInitialAnalysisStart,
pinnedSignificantItem,
setPinnedSignificantItem,
pinnedGroup,
Expand All @@ -86,6 +122,10 @@ export const LogRateAnalysisResultsTableRowStateProvider: FC = ({ children }) =>
},
}),
[
autoRunAnalysis,
setAutoRunAnalysis,
initialAnalysisStart,
setInitialAnalysisStart,
pinnedSignificantItem,
setPinnedSignificantItem,
pinnedGroup,
Expand All @@ -101,19 +141,26 @@ export const LogRateAnalysisResultsTableRowStateProvider: FC = ({ children }) =>

return (
// Provider managing the state
<logRateAnalysisResultsTableRowContext.Provider value={contextValue}>
<LogRateAnalysisStateContext.Provider value={contextValue}>
{children}
</logRateAnalysisResultsTableRowContext.Provider>
</LogRateAnalysisStateContext.Provider>
);
};

export const useLogRateAnalysisResultsTableRowContext = () => {
const logRateAnalysisResultsTableRow = useContext(logRateAnalysisResultsTableRowContext);
/**
* Custom hook for accessing the state of log rate analysis from the LogRateAnalysisStateContext.
* This hook must be used within a component that is a descendant of the LogRateAnalysisStateContext provider.
*
* @returns The current state of the log rate analysis.
* @throws Throws an error if the hook is used outside of its Provider context.
*/
export const useLogRateAnalysisStateContext = () => {
const logRateAnalysisState = useContext(LogRateAnalysisStateContext);

// If `undefined`, throw an error.
if (logRateAnalysisResultsTableRow === undefined) {
throw new Error('useLogRateAnalysisResultsTableRowContext was used outside of its Provider');
if (logRateAnalysisState === undefined) {
throw new Error('useLogRateAnalysisStateContext was used outside of its Provider');
}

return logRateAnalysisResultsTableRow;
return logRateAnalysisState;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,36 @@ import type { EuiTableActionsColumnType } from '@elastic/eui';

import type { SignificantItem, SignificantItemGroupItem } from '@kbn/ml-agg-utils';

/**
* Type for defining attributes picked from
* SignificantItemGroupItem used in the grouped table.
*/
export type GroupTableItemGroup = Pick<
SignificantItemGroupItem,
'key' | 'type' | 'fieldName' | 'fieldValue' | 'docCount' | 'pValue' | 'duplicate'
>;

/**
* Represents a single item in the group table.
*/
export interface GroupTableItem {
/** Unique identifier for the group table item. */
id: string;
/** Document count associated with the item. */
docCount: number;
/** Statistical p-value indicating the significance of the item, nullable. */
pValue: number | null;
/** Count of unique items within the group. */
uniqueItemsCount: number;
/** Array of items within the group, sorted by uniqueness. */
groupItemsSortedByUniqueness: GroupTableItemGroup[];
/** Histogram data for the significant item. */
histogram: SignificantItem['histogram'];
}

/**
* Type for action columns in a table that involves SignificantItem or GroupTableItem.
*/
export type TableItemAction = EuiTableActionsColumnType<
SignificantItem | GroupTableItem
>['actions'][number];
1 change: 1 addition & 0 deletions x-pack/packages/ml/aiops_components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@kbn/field-formats-plugin",
"@kbn/visualization-utils",
"@kbn/aiops-log-rate-analysis",
"@kbn/ml-agg-utils",
],
"exclude": [
"target/**/*",
Expand Down
8 changes: 7 additions & 1 deletion x-pack/performance/journeys_e2e/aiops_log_rate_analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ export const journey = new Journey({
await page.waitForSelector(subj('aiopsNoWindowParametersEmptyPrompt'));
})
.step('Run AIOps Log Rate Analysis', async ({ page }) => {
// Select the chart and click in the area where the spike is located to trigger log rate analysis.
// Select the chart and click in the area where the spike is located.
const chart = await page.locator(subj('aiopsDocumentCountChart'));
await chart.click({ position: { x: 710, y: 50 } });

// Click the "Run analysis" button.
await page.waitForSelector(subj('aiopsLogRateAnalysisNoAutoRunContentRunAnalysisButton'));
await page.click(subj('aiopsLogRateAnalysisNoAutoRunContentRunAnalysisButton'));

// Wait for the analysis to complete.
await page.waitForSelector(subj('aiopsAnalysisComplete'), { timeout: 120000 });
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
*/

import type { SignificantItem } from '@kbn/ml-agg-utils';

import type { GroupTableItem } from '../../components/log_rate_analysis_results_table/types';
import type { GroupTableItem } from '@kbn/aiops-components';

import { buildExtendedBaseFilterCriteria } from './build_extended_base_filter_criteria';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import type { Query } from '@kbn/es-query';
import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils';
import { buildBaseFilterCriteria } from '@kbn/ml-query-utils';
import { getCategoryQuery } from '@kbn/aiops-log-pattern-analysis/get_category_query';

import type { GroupTableItem } from '../../components/log_rate_analysis_results_table/types';
import type { GroupTableItem } from '@kbn/aiops-components';

/*
* Contains utility functions for building and processing queries.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import type {
} from '@elastic/charts/dist/chart_types/xy_chart/utils/specs';

import type { LogRateHistogramItem, WindowParameters } from '@kbn/aiops-log-rate-analysis';
import { DocumentCountChart, type BrushSelectionUpdateHandler } from '@kbn/aiops-components';
import {
DocumentCountChartWithAutoAnalysisStart,
type BrushSelectionUpdateHandler,
} from '@kbn/aiops-components';

import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context';
import type { DocumentCountStats } from '../../../get_document_stats';
Expand All @@ -29,13 +32,11 @@ export interface DocumentCountContentProps {
isBrushCleared: boolean;
totalCount: number;
sampleProbability: number;
initialAnalysisStart?: number | WindowParameters;
/** Optional color override for the default bar color for charts */
barColorOverride?: string;
/** Optional color override for the highlighted bar color for charts */
barHighlightColorOverride?: string;
windowParameters?: WindowParameters;
incomingInitialAnalysisStart?: number | WindowParameters;
baselineLabel?: string;
deviationLabel?: string;
barStyleAccessor?: BarStyleAccessor;
Expand All @@ -51,11 +52,9 @@ export const DocumentCountContent: FC<DocumentCountContentProps> = ({
isBrushCleared,
totalCount,
sampleProbability,
initialAnalysisStart,
barColorOverride,
barHighlightColorOverride,
windowParameters,
incomingInitialAnalysisStart,
...docCountChartProps
}) => {
const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext();
Expand Down Expand Up @@ -100,7 +99,7 @@ export const DocumentCountContent: FC<DocumentCountContentProps> = ({
</EuiFlexItem>
{documentCountStats.interval !== undefined && (
<EuiFlexItem>
<DocumentCountChart
<DocumentCountChartWithAutoAnalysisStart
dependencies={{ data, uiSettings, fieldFormats, charts }}
brushSelectionUpdateHandler={brushSelectionUpdateHandler}
chartPoints={chartPoints}
Expand All @@ -110,7 +109,6 @@ export const DocumentCountContent: FC<DocumentCountContentProps> = ({
interval={documentCountStats.interval}
chartPointsSplitLabel={documentCountStatsSplitLabel}
isBrushCleared={isBrushCleared}
autoAnalysisStart={initialAnalysisStart}
barColorOverride={barColorOverride}
barHighlightColorOverride={barHighlightColorOverride}
changePoint={documentCountStats.changePoint}
Expand Down
Loading

0 comments on commit 6fdcd8d

Please sign in to comment.