Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] AIOps: Fix to not run log rate analysis twice when no spike/dip detected. #180980

Merged
merged 15 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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