Skip to content

Commit

Permalink
[ML] Explain log rate spikes: Plugin setup (#131317)
Browse files Browse the repository at this point in the history
Sets up the boilerplate code for the aiops plugin and adds a demo page within the ML app to demonstrate single API request data streaming from Kibana server to UI client.
  • Loading branch information
walterra committed May 12, 2022
1 parent d6805f9 commit 6df1b28
Show file tree
Hide file tree
Showing 50 changed files with 1,286 additions and 5 deletions.
5 changes: 3 additions & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,11 @@
/x-pack/test/screenshot_creation/apps/ml_docs @elastic/ml-ui
/x-pack/test/screenshot_creation/services/ml_screenshots.ts @elastic/ml-ui

# ML team owns and maintains the transform plugin despite it living in the Data management section.
/x-pack/plugins/transform/ @elastic/ml-ui
# Additional plugins maintained by the ML team.
/x-pack/plugins/aiops/ @elastic/ml-ui
/x-pack/plugins/data_visualizer/ @elastic/ml-ui
/x-pack/plugins/file_upload/ @elastic/ml-ui
/x-pack/plugins/transform/ @elastic/ml-ui
/x-pack/test/accessibility/apps/transform.ts @elastic/ml-ui
/x-pack/test/api_integration/apis/transform/ @elastic/ml-ui
/x-pack/test/api_integration_basic/apis/transform/ @elastic/ml-ui
Expand Down
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ The plugin exposes the static DefaultEditorController class to consume.
|The Kibana actions plugin provides a framework to create executable actions. You can:
|{kib-repo}blob/{branch}/x-pack/plugins/aiops/README.md[aiops]
|The plugin provides APIs and components for AIOps features, including the “Explain log rate spikes” UI, maintained by the ML team.
|{kib-repo}blob/{branch}/x-pack/plugins/alerting/README.md[alerting]
|The Kibana Alerting plugin provides a common place to set up rules. You can:
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pageLoadAssetSize:
advancedSettings: 27596
actions: 20000
aiops: 10000
alerting: 106936
apm: 64385
canvas: 1066647
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@
"@kbn/ui-actions-enhanced-examples-plugin/*": ["x-pack/examples/ui_actions_enhanced_examples/*"],
"@kbn/actions-plugin": ["x-pack/plugins/actions"],
"@kbn/actions-plugin/*": ["x-pack/plugins/actions/*"],
"@kbn/aiops-plugin": ["x-pack/plugins/aiops"],
"@kbn/aiops-plugin/*": ["x-pack/plugins/aiops/*"],
"@kbn/alerting-plugin": ["x-pack/plugins/alerting"],
"@kbn/alerting-plugin/*": ["x-pack/plugins/alerting/*"],
"@kbn/apm-plugin": ["x-pack/plugins/apm"],
Expand Down
1 change: 1 addition & 0 deletions x-pack/.i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"xpack.logstash": ["plugins/logstash"],
"xpack.main": "legacy/plugins/xpack_main",
"xpack.maps": ["plugins/maps"],
"xpack.aiops": ["plugins/aiops"],
"xpack.ml": ["plugins/ml"],
"xpack.monitoring": ["plugins/monitoring"],
"xpack.osquery": ["plugins/osquery"],
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/aiops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# aiops

The plugin provides APIs and components for AIOps features, including the “Explain log rate spikes” UI, maintained by the ML team.

---

## Development

See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment.
68 changes: 68 additions & 0 deletions x-pack/plugins/aiops/common/api/example_stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 { schema, TypeOf } from '@kbn/config-schema';

export const aiopsExampleStreamSchema = schema.object({
/** Boolean flag to enable/disabling simulation of response errors. */
simulateErrors: schema.maybe(schema.boolean()),
/** Maximum timeout between streaming messages. */
timeout: schema.maybe(schema.number()),
});

export type AiopsExampleStreamSchema = TypeOf<typeof aiopsExampleStreamSchema>;

export const API_ACTION_NAME = {
UPDATE_PROGRESS: 'update_progress',
ADD_TO_ENTITY: 'add_to_entity',
DELETE_ENTITY: 'delete_entity',
} as const;
export type ApiActionName = typeof API_ACTION_NAME[keyof typeof API_ACTION_NAME];

interface ApiActionUpdateProgress {
type: typeof API_ACTION_NAME.UPDATE_PROGRESS;
payload: number;
}

export function updateProgressAction(payload: number): ApiActionUpdateProgress {
return {
type: API_ACTION_NAME.UPDATE_PROGRESS,
payload,
};
}

interface ApiActionAddToEntity {
type: typeof API_ACTION_NAME.ADD_TO_ENTITY;
payload: {
entity: string;
value: number;
};
}

export function addToEntityAction(entity: string, value: number): ApiActionAddToEntity {
return {
type: API_ACTION_NAME.ADD_TO_ENTITY,
payload: {
entity,
value,
},
};
}

interface ApiActionDeleteEntity {
type: typeof API_ACTION_NAME.DELETE_ENTITY;
payload: string;
}

export function deleteEntityAction(payload: string): ApiActionDeleteEntity {
return {
type: API_ACTION_NAME.DELETE_ENTITY,
payload,
};
}

export type ApiAction = ApiActionUpdateProgress | ApiActionAddToEntity | ApiActionDeleteEntity;
19 changes: 19 additions & 0 deletions x-pack/plugins/aiops/common/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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 type { AiopsExampleStreamSchema } from './example_stream';

export const API_ENDPOINT = {
EXAMPLE_STREAM: '/internal/aiops/example_stream',
ANOTHER: '/internal/aiops/another',
} as const;
export type ApiEndpoint = typeof API_ENDPOINT[keyof typeof API_ENDPOINT];

export interface ApiEndpointOptions {
[API_ENDPOINT.EXAMPLE_STREAM]: AiopsExampleStreamSchema;
[API_ENDPOINT.ANOTHER]: { anotherOption: string };
}
22 changes: 22 additions & 0 deletions x-pack/plugins/aiops/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

/**
* PLUGIN_ID is used as a unique identifier for the aiops plugin
*/
export const PLUGIN_ID = 'aiops';

/**
* PLUGIN_NAME is used as the display name for the aiops plugin
*/
export const PLUGIN_NAME = 'AIOps';

/**
* This is an internal hard coded feature flag so we can easily turn on/off the
* "Explain log rate spikes UI" during development until the first release.
*/
export const AIOPS_ENABLED = true;
15 changes: 15 additions & 0 deletions x-pack/plugins/aiops/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/x-pack/plugins/aiops'],
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/aiops',
coverageReporters: ['text', 'html'],
collectCoverageFrom: ['<rootDir>/x-pack/plugins/aiops/{common,public,server}/**/*.{js,ts,tsx}'],
};
16 changes: 16 additions & 0 deletions x-pack/plugins/aiops/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "aiops",
"version": "1.0.0",
"kibanaVersion": "kibana",
"owner": {
"name": "Machine Learning UI",
"githubTeam": "ml-ui"
},
"description": "AIOps plugin maintained by ML team.",
"server": true,
"ui": true,
"requiredPlugins": [],
"optionalPlugins": [],
"requiredBundles": ["kibanaReact"],
"extraPublicDirs": ["common"]
}
15 changes: 15 additions & 0 deletions x-pack/plugins/aiops/public/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { lazyLoadModules } from '../lazy_load_bundle';

import type { ExplainLogRateSpikesSpec } from '../components/explain_log_rate_spikes';

export async function getExplainLogRateSpikesComponent(): Promise<() => ExplainLogRateSpikesSpec> {
const modules = await lazyLoadModules();
return () => modules.ExplainLogRateSpikes;
}
167 changes: 167 additions & 0 deletions x-pack/plugins/aiops/public/components/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* 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, { useEffect, useState } from 'react';

import { Chart, Settings, Axis, BarSeries, Position, ScaleType } from '@elastic/charts';

import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useKibana } from '@kbn/kibana-react-plugin/public';

import {
EuiBadge,
EuiButton,
EuiCheckbox,
EuiFlexGroup,
EuiFlexItem,
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiPageContentHeader,
EuiProgress,
EuiSpacer,
EuiTitle,
EuiText,
} from '@elastic/eui';

import { getStatusMessage } from './get_status_message';
import { initialState, resetStream, streamReducer } from './stream_reducer';
import { useStreamFetchReducer } from './use_stream_fetch_reducer';

export const AiopsApp = () => {
const { notifications } = useKibana();

const [simulateErrors, setSimulateErrors] = useState(false);

const { dispatch, start, cancel, data, isCancelled, isRunning } = useStreamFetchReducer(
'/internal/aiops/example_stream',
streamReducer,
initialState,
{ simulateErrors }
);

const { errors, progress, entities } = data;

const onClickHandler = async () => {
if (isRunning) {
cancel();
} else {
dispatch(resetStream());
start();
}
};

useEffect(() => {
if (errors.length > 0) {
notifications.toasts.danger({ body: errors[errors.length - 1] });
}
}, [errors, notifications.toasts]);

const buttonLabel = isRunning
? i18n.translate('xpack.aiops.stopbuttonText', {
defaultMessage: 'Stop development',
})
: i18n.translate('xpack.aiops.startbuttonText', {
defaultMessage: 'Start development',
});

return (
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="xpack.aiops.congratulationsTitle"
defaultMessage="Single endpoint streaming demo"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton
type="primary"
size="s"
onClick={onClickHandler}
aria-label={buttonLabel}
>
{buttonLabel}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText>
<EuiBadge>{progress}%</EuiBadge>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiProgress value={progress} max={100} size="xs" />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<div style={{ height: '300px' }}>
<Chart>
<Settings rotation={90} />
<Axis
id="entities"
position={Position.Bottom}
title={i18n.translate('xpack.aiops.barChart.commitsTitle', {
defaultMessage: 'Commits',
})}
showOverlappingTicks
/>
<Axis
id="left2"
title={i18n.translate('xpack.aiops.barChart.developersTitle', {
defaultMessage: 'Developers',
})}
position={Position.Left}
/>

<BarSeries
id="commits"
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
data={Object.entries(entities)
.map(([x, y]) => {
return {
x,
y,
};
})
.sort((a, b) => b.y - a.y)}
/>
</Chart>
</div>
<p>{getStatusMessage(isRunning, isCancelled, data.progress)}</p>
<EuiCheckbox
id="aiopSimulateErrorsCheckbox"
label={i18n.translate(
'xpack.aiops.explainLogRateSpikes.simulateErrorsCheckboxLabel',
{
defaultMessage:
'Simulate errors (gets applied to new streams only, not currently running ones).',
}
)}
checked={simulateErrors}
onChange={(e) => setSimulateErrors(!simulateErrors)}
compressed
/>
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
);
};
Loading

0 comments on commit 6df1b28

Please sign in to comment.