Alerts and cases page components for observability plugin (#93365)
Create components for the alerts and cases pages. This only contains basic empty components and stories for them.
This commit is contained in:
parent
1e6a024c4d
commit
5e31f91614
|
@ -408,4 +408,8 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
|
|||
type: 'boolean',
|
||||
_meta: { description: 'Non-default value of setting.' },
|
||||
},
|
||||
'observability:enableAlertingExperience': {
|
||||
type: 'boolean',
|
||||
_meta: { description: 'Non-default value of setting.' },
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ export interface UsageStats {
|
|||
'securitySolution:rulesTableRefresh': string;
|
||||
'apm:enableSignificantTerms': boolean;
|
||||
'apm:enableServiceOverview': boolean;
|
||||
'observability:enableAlertingExperience': boolean;
|
||||
'visualize:enableLabs': boolean;
|
||||
'visualization:heatmap:maxBuckets': number;
|
||||
'visualization:colorMapping': string;
|
||||
|
|
|
@ -8026,6 +8026,12 @@
|
|||
"_meta": {
|
||||
"description": "Non-default value of setting."
|
||||
}
|
||||
},
|
||||
"observability:enableAlertingExperience": {
|
||||
"type": "boolean",
|
||||
"_meta": {
|
||||
"description": "Non-default value of setting."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
8
x-pack/plugins/observability/common/ui_settings_keys.ts
Normal file
8
x-pack/plugins/observability/common/ui_settings_keys.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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 const enableAlertingExperience = 'observability:enableAlertingExperience';
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { MouseEvent, useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Router, Switch } from 'react-router-dom';
|
||||
import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
|
||||
|
@ -21,12 +21,7 @@ import { useRouteParams } from '../hooks/use_route_params';
|
|||
import { ObservabilityPluginSetupDeps } from '../plugin';
|
||||
import { HasDataContextProvider } from '../context/has_data_context';
|
||||
import { Breadcrumbs, routes } from '../routes';
|
||||
|
||||
const observabilityLabelBreadcrumb = {
|
||||
text: i18n.translate('xpack.observability.observability.breadcrumb.', {
|
||||
defaultMessage: 'Observability',
|
||||
}),
|
||||
};
|
||||
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
|
||||
function getTitleFromBreadCrumbs(breadcrumbs: Breadcrumbs) {
|
||||
return breadcrumbs.map(({ text }) => text).reverse();
|
||||
|
@ -42,12 +37,24 @@ function App() {
|
|||
const Wrapper = () => {
|
||||
const { core } = usePluginContext();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const breadcrumb = [observabilityLabelBreadcrumb, ...route.breadcrumb];
|
||||
useEffect(() => {
|
||||
core.chrome.setBreadcrumbs(breadcrumb);
|
||||
core.chrome.docTitle.change(getTitleFromBreadCrumbs(breadcrumb));
|
||||
}, [core, breadcrumb]);
|
||||
const href = core.http.basePath.prepend('/app/observability');
|
||||
const breadcrumbs = [
|
||||
{
|
||||
href,
|
||||
text: i18n.translate('xpack.observability.observability.breadcrumb.', {
|
||||
defaultMessage: 'Observability',
|
||||
}),
|
||||
onClick: (event: MouseEvent<HTMLAnchorElement>) => {
|
||||
event.preventDefault();
|
||||
core.application.navigateToUrl(href);
|
||||
},
|
||||
},
|
||||
...route.breadcrumb,
|
||||
];
|
||||
core.chrome.setBreadcrumbs(breadcrumbs);
|
||||
core.chrome.docTitle.change(getTitleFromBreadCrumbs(breadcrumbs));
|
||||
}, [core]);
|
||||
|
||||
const params = useRouteParams(path);
|
||||
return route.handler(params);
|
||||
|
@ -76,7 +83,7 @@ export const renderApp = (
|
|||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins, storage: new Storage(localStorage) }}>
|
||||
<PluginContext.Provider value={{ appMountParameters, core, plugins }}>
|
||||
<Router history={history}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
|
|
|
@ -21,6 +21,7 @@ import React, { useState } from 'react';
|
|||
import { EuiSelect } from '@elastic/eui';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { Alert } from '../../../../../../alerting/common';
|
||||
import { enableAlertingExperience } from '../../../../../common/ui_settings_keys';
|
||||
import { usePluginContext } from '../../../../hooks/use_plugin_context';
|
||||
import { SectionContainer } from '..';
|
||||
|
||||
|
@ -40,6 +41,10 @@ export function AlertsSection({ alerts }: Props) {
|
|||
const { core } = usePluginContext();
|
||||
const [filter, setFilter] = useState(ALL_TYPES);
|
||||
|
||||
const href = core.uiSettings.get(enableAlertingExperience)
|
||||
? '/app/observability/alerts'
|
||||
: '/app/management/insightsAndAlerting/triggersActions/alerts';
|
||||
|
||||
const filterOptions = uniqBy(alerts, (alert) => alert.consumer).map(({ consumer }) => ({
|
||||
value: consumer,
|
||||
text: consumer,
|
||||
|
@ -51,7 +56,7 @@ export function AlertsSection({ alerts }: Props) {
|
|||
defaultMessage: 'Alerts',
|
||||
})}
|
||||
appLink={{
|
||||
href: '/app/management/insightsAndAlerting/triggersActions/alerts',
|
||||
href,
|
||||
label: i18n.translate('xpack.observability.overview.alert.appLink', {
|
||||
defaultMessage: 'Manage alerts',
|
||||
}),
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { EuiBetaBadge } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
export function ExperimentalBadge() {
|
||||
return (
|
||||
<EuiBetaBadge
|
||||
label={i18n.translate('xpack.observability.experimentalBadgeLabel', {
|
||||
defaultMessage: 'Experimental',
|
||||
})}
|
||||
tooltipContent={i18n.translate('xpack.observability.experimentalBadgeDescription', {
|
||||
defaultMessage:
|
||||
'This functionality is experimental and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features.',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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, { ComponentType } from 'react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { AlertsPage } from '.';
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { PluginContext, PluginContextValue } from '../../context/plugin_context';
|
||||
import { AlertsFlyout } from './alerts_flyout';
|
||||
import { AlertItem } from './alerts_table';
|
||||
import { eventLogPocData, wireframeData } from './example_data';
|
||||
|
||||
export default {
|
||||
title: 'app/Alerts',
|
||||
component: AlertsPage,
|
||||
decorators: [
|
||||
(Story: ComponentType) => {
|
||||
return (
|
||||
<IntlProvider locale="en">
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
data: { query: {} },
|
||||
docLinks: { links: { query: {} } },
|
||||
storage: { get: () => {} },
|
||||
uiSettings: {
|
||||
get: (setting: string) => {
|
||||
if (setting === 'dateFormat') {
|
||||
return '';
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<PluginContext.Provider
|
||||
value={
|
||||
({
|
||||
core: {
|
||||
http: { basePath: { prepend: (_: string) => '' } },
|
||||
},
|
||||
} as unknown) as PluginContextValue
|
||||
}
|
||||
>
|
||||
<Story />
|
||||
</PluginContext.Provider>
|
||||
</KibanaContextProvider>
|
||||
</IntlProvider>
|
||||
);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function Example() {
|
||||
return <AlertsPage items={wireframeData} routeParams={{ query: {} }} />;
|
||||
}
|
||||
|
||||
export function EventLog() {
|
||||
return <AlertsPage items={eventLogPocData as AlertItem[]} routeParams={{ query: {} }} />;
|
||||
}
|
||||
|
||||
export function EmptyState() {
|
||||
return <AlertsPage items={[]} routeParams={{ query: {} }} />;
|
||||
}
|
||||
|
||||
export function Flyout() {
|
||||
return <AlertsFlyout {...wireframeData[0]} onClose={() => {}} />;
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiBadge,
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutProps,
|
||||
EuiInMemoryTable,
|
||||
EuiSpacer,
|
||||
EuiTabbedContent,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { AlertItem } from './alerts_table';
|
||||
|
||||
type AlertsFlyoutProps = AlertItem & EuiFlyoutProps;
|
||||
|
||||
export function AlertsFlyout(props: AlertsFlyoutProps) {
|
||||
const {
|
||||
actualValue,
|
||||
affectedEntity,
|
||||
expectedValue,
|
||||
onClose,
|
||||
reason,
|
||||
severity,
|
||||
severityLog,
|
||||
status,
|
||||
duration,
|
||||
type,
|
||||
} = props;
|
||||
const timestamp = props['@timestamp'];
|
||||
|
||||
const overviewListItems = [
|
||||
{
|
||||
title: 'Status',
|
||||
description: status || '-',
|
||||
},
|
||||
{
|
||||
title: 'Severity',
|
||||
description: severity || '-', // TODO: badge and "(changed 2 min ago)"
|
||||
},
|
||||
{
|
||||
title: 'Affected entity',
|
||||
description: affectedEntity || '-', // TODO: link to entity
|
||||
},
|
||||
{
|
||||
title: 'Triggered',
|
||||
description: timestamp, // TODO: format date
|
||||
},
|
||||
{
|
||||
title: 'Duration',
|
||||
description: duration || '-', // TODO: format duration
|
||||
},
|
||||
{
|
||||
title: 'Expected value',
|
||||
description: expectedValue || '-',
|
||||
},
|
||||
{
|
||||
title: 'Actual value',
|
||||
description: actualValue || '-',
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
description: type || '-',
|
||||
},
|
||||
];
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: 'overview',
|
||||
name: i18n.translate('xpack.observability.alerts.flyoutOverviewTabTitle', {
|
||||
defaultMessage: 'Overview',
|
||||
}),
|
||||
content: (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<EuiInMemoryTable
|
||||
columns={[
|
||||
{ field: 'title', name: '' },
|
||||
{ field: 'description', name: '' },
|
||||
]}
|
||||
items={overviewListItems}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="xs">
|
||||
<h4>Severity log</h4>
|
||||
</EuiTitle>
|
||||
<EuiInMemoryTable
|
||||
columns={[
|
||||
{ field: '@timestamp', name: 'Timestamp', dataType: 'date' },
|
||||
{
|
||||
field: 'severity',
|
||||
name: 'Severity',
|
||||
render: (_, item) => (
|
||||
<>
|
||||
<EuiBadge>{item.severity}</EuiBadge> {item.message}
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
items={severityLog ?? []}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'metadata',
|
||||
name: i18n.translate('xpack.observability.alerts.flyoutMetadataTabTitle', {
|
||||
defaultMessage: 'Metadata',
|
||||
}),
|
||||
disabled: true,
|
||||
content: <></>,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiFlyout onClose={onClose} size="s">
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle size="xs">
|
||||
<h2>{reason}</h2>
|
||||
</EuiTitle>
|
||||
<EuiTabbedContent size="s" tabs={tabs} />
|
||||
</EuiFlyoutHeader>
|
||||
</EuiFlyout>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public';
|
||||
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
|
||||
|
||||
export function AlertsSearchBar() {
|
||||
return (
|
||||
<SearchBar
|
||||
indexPatterns={[]}
|
||||
placeholder={i18n.translate('xpack.observability.alerts.searchBarPlaceholder', {
|
||||
defaultMessage: '"domain": "ecommerce" AND ("service.name": "ProductCatalogService" …)',
|
||||
})}
|
||||
query={{ query: '', language: 'kuery' }}
|
||||
timeHistory={new TimeHistory(new Storage(localStorage))}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiBasicTable,
|
||||
EuiBasicTableColumn,
|
||||
EuiBasicTableProps,
|
||||
DefaultItemAction,
|
||||
EuiTableSelectionType,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { AlertsFlyout } from './alerts_flyout';
|
||||
|
||||
/**
|
||||
* The type of an item in the alert list.
|
||||
*
|
||||
* The fields here are the minimum to make this work at this time, but
|
||||
* eventually this type should be derived from the schema of what is returned in
|
||||
* the API response.
|
||||
*/
|
||||
export interface AlertItem {
|
||||
'@timestamp': number;
|
||||
reason: string;
|
||||
severity: string;
|
||||
// These are just made up so we can make example links
|
||||
service?: { name?: string };
|
||||
pod?: string;
|
||||
log?: boolean;
|
||||
// Other fields used in the flyout
|
||||
actualValue?: string;
|
||||
affectedEntity?: string;
|
||||
expectedValue?: string;
|
||||
severityLog?: Array<{ '@timestamp': number; severity: string; message: string }>;
|
||||
status?: string;
|
||||
duration?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
type AlertsTableProps = Omit<
|
||||
EuiBasicTableProps<AlertItem>,
|
||||
'columns' | 'isSelectable' | 'pagination' | 'selection'
|
||||
>;
|
||||
|
||||
export function AlertsTable(props: AlertsTableProps) {
|
||||
const [flyoutAlert, setFlyoutAlert] = useState<AlertItem | undefined>(undefined);
|
||||
const handleFlyoutClose = () => setFlyoutAlert(undefined);
|
||||
const { prepend } = usePluginContext().core.http.basePath;
|
||||
|
||||
// This is a contrived implementation of the reason field that shows how
|
||||
// you could link to certain types of resources based on what's contained
|
||||
// in their alert data.
|
||||
function reasonRenderer(text: string, item: AlertItem) {
|
||||
const serviceName = item.service?.name;
|
||||
const pod = item.pod;
|
||||
const log = item.log;
|
||||
|
||||
if (serviceName) {
|
||||
return <EuiLink href={prepend(`/app/apm/services/${serviceName}`)}>{text}</EuiLink>;
|
||||
} else if (pod) {
|
||||
return <EuiLink href={prepend(`/app/metrics/link-to/host-detail/${pod}`)}>{text}</EuiLink>;
|
||||
} else if (log) {
|
||||
return <EuiLink href={prepend(`/app/logs/stream`)}>{text}</EuiLink>;
|
||||
} else {
|
||||
return <>{text}</>;
|
||||
}
|
||||
}
|
||||
|
||||
const actions: Array<DefaultItemAction<AlertItem>> = [
|
||||
{
|
||||
name: 'Alert details',
|
||||
description: 'Alert details',
|
||||
onClick: (item) => {
|
||||
setFlyoutAlert(item);
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
];
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<AlertItem>> = [
|
||||
{
|
||||
field: '@timestamp',
|
||||
name: 'Triggered',
|
||||
dataType: 'date',
|
||||
},
|
||||
{
|
||||
field: 'duration',
|
||||
name: 'Duration',
|
||||
},
|
||||
{
|
||||
field: 'severity',
|
||||
name: 'Severity',
|
||||
},
|
||||
{
|
||||
field: 'reason',
|
||||
name: 'Reason',
|
||||
dataType: 'string',
|
||||
render: reasonRenderer,
|
||||
},
|
||||
{
|
||||
actions,
|
||||
name: 'Actions',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{flyoutAlert && <AlertsFlyout {...flyoutAlert} onClose={handleFlyoutClose} />}
|
||||
<EuiBasicTable<AlertItem>
|
||||
{...props}
|
||||
isSelectable={true}
|
||||
selection={{} as EuiTableSelectionType<AlertItem>}
|
||||
columns={columns}
|
||||
pagination={{ pageIndex: 0, pageSize: 0, totalItemCount: 0 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
509
x-pack/plugins/observability/public/pages/alerts/example_data.ts
Normal file
509
x-pack/plugins/observability/public/pages/alerts/example_data.ts
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example data from Whimsical wireframes: https://whimsical.com/observability-alerting-user-journeys-8TFDcHRPMQDJgtLpJ7XuBj
|
||||
*/
|
||||
export const wireframeData = [
|
||||
{
|
||||
'@timestamp': 1615392661000,
|
||||
duration: '10 min 2 s',
|
||||
severity: '-',
|
||||
reason: 'Error count is greater than 100 (current value is 135) on shippingService',
|
||||
service: { name: 'opbeans-go' },
|
||||
affectedEntity: 'opbeans-go service',
|
||||
status: 'Active',
|
||||
expectedValue: '< 100',
|
||||
actualValue: '135',
|
||||
severityLog: [
|
||||
{ '@timestamp': 1615392661000, severity: 'critical', message: 'Load is 3.5' },
|
||||
{ '@timestamp': 1615392600000, severity: 'warning', message: 'Load is 2.5' },
|
||||
{ '@timestamp': 1615392552000, severity: 'critical', message: 'Load is 3.5' },
|
||||
],
|
||||
type: 'APM Error count',
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615392600000,
|
||||
duration: '11 min 1 s',
|
||||
severity: '-',
|
||||
reason: 'Latency is greater than 1500ms (current value is 1700ms) on frontend',
|
||||
service: { name: 'opbeans-go' },
|
||||
severityLog: [],
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615392552000,
|
||||
duration: '10 min 2 s',
|
||||
severity: 'critical',
|
||||
reason: 'Latency anomaly score is 84 on checkoutService',
|
||||
service: { name: 'opbeans-go' },
|
||||
severityLog: [],
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615392391000,
|
||||
duration: '10 min 2 s',
|
||||
severity: '-',
|
||||
reason:
|
||||
'CPU is greater than a threshold of 75% (current value is 83%) on gke-eden-3-prod-pool-2-395ef018-06xg',
|
||||
pod: 'gke-dev-oblt-dev-oblt-pool-30f1ba48-skw',
|
||||
severityLog: [],
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615392363000,
|
||||
duration: '10 min 2 s',
|
||||
severity: '-',
|
||||
reason:
|
||||
"Log count with 'Log.level.error' and 'service.name; frontend' is greater than 75 (current value 122)",
|
||||
log: true,
|
||||
severityLog: [],
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615392361000,
|
||||
duration: '10 min 2 s',
|
||||
severity: 'critical',
|
||||
reason: 'Load is greater than 2 (current value is 3.5) on gke-eden-3-prod-pool-2-395ef018-06xg',
|
||||
pod: 'gke-dev-oblt-dev-oblt-pool-30f1ba48-skw',
|
||||
severityLog: [],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Example data from this proof of concept: https://github.com/dgieselaar/kibana/tree/alerting-event-log-poc
|
||||
*/
|
||||
export const eventLogPocData = [
|
||||
{
|
||||
'@timestamp': 1615395754597,
|
||||
first_seen: 1615362488702,
|
||||
severity: 'warning',
|
||||
severity_value: 1241.4546,
|
||||
reason:
|
||||
'Transaction duration for opbeans-java/request in production was above the threshold of 1.0 ms (1.2 ms)',
|
||||
rule_id: 'cb1fc3e0-7fef-11eb-827d-d94e80a23d8d',
|
||||
rule_name: 'Latency threshold | opbeans-java',
|
||||
rule_type_id: 'apm.transaction_duration',
|
||||
rule_type_name: 'Latency threshold',
|
||||
alert_instance_title: ['opbeans-java/request:production'],
|
||||
alert_instance_name: 'apm.transaction_duration_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: '1b354805-4bf3-4626-b6be-5801d7d1e256',
|
||||
influencers: [
|
||||
'service.name:opbeans-java',
|
||||
'service.environment:production',
|
||||
'transaction.type:request',
|
||||
],
|
||||
fields: {
|
||||
'processor.event': 'transaction',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
'transaction.type': 'request',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615359600000,
|
||||
y: 48805,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615370400000,
|
||||
y: 3992.5,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615381200000,
|
||||
y: 4296.7998046875,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615392000000,
|
||||
y: 1633.8182373046875,
|
||||
threshold: 1000,
|
||||
},
|
||||
],
|
||||
recovered: false,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615326143423,
|
||||
first_seen: 1615323802378,
|
||||
severity: 'warning',
|
||||
severity_value: 27,
|
||||
reason: 'Error count for opbeans-node in production was above the threshold of 2 (27)',
|
||||
rule_id: '335b38d0-80be-11eb-9fd1-d3725789930d',
|
||||
rule_name: 'Error count threshold',
|
||||
rule_type_id: 'apm.error_rate',
|
||||
rule_type_name: 'Error count threshold',
|
||||
alert_instance_title: ['opbeans-node:production'],
|
||||
alert_instance_name: 'opbeans-node_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: '19165a4f-296a-4045-9448-40c793d97d02',
|
||||
influencers: ['service.name:opbeans-node', 'service.environment:production'],
|
||||
fields: {
|
||||
'processor.event': 'error',
|
||||
'service.name': 'opbeans-node',
|
||||
'service.environment': 'production',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615323780000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324080000,
|
||||
y: 34,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324380000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324680000,
|
||||
y: 34,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324980000,
|
||||
y: 35,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325280000,
|
||||
y: 31,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325580000,
|
||||
y: 36,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325880000,
|
||||
y: 35,
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615326143423,
|
||||
first_seen: 1615325783256,
|
||||
severity: 'warning',
|
||||
severity_value: 27,
|
||||
reason: 'Error count for opbeans-java in production was above the threshold of 2 (27)',
|
||||
rule_id: '335b38d0-80be-11eb-9fd1-d3725789930d',
|
||||
rule_name: 'Error count threshold',
|
||||
rule_type_id: 'apm.error_rate',
|
||||
rule_type_name: 'Error count threshold',
|
||||
alert_instance_title: ['opbeans-java:production'],
|
||||
alert_instance_name: 'opbeans-java_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: '73075d90-e27a-4e20-9ba0-3512a16c2829',
|
||||
influencers: ['service.name:opbeans-java', 'service.environment:production'],
|
||||
fields: {
|
||||
'processor.event': 'error',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615325760000,
|
||||
y: 36,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325820000,
|
||||
y: 26,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325880000,
|
||||
y: 28,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325940000,
|
||||
y: 35,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615326000000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615326060000,
|
||||
y: 23,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615326120000,
|
||||
y: 27,
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615326143423,
|
||||
first_seen: 1615323802378,
|
||||
severity: 'warning',
|
||||
severity_value: 4759.9116,
|
||||
reason:
|
||||
'Transaction duration for opbeans-java/request in production was above the threshold of 1.0 ms (4.8 ms)',
|
||||
rule_id: 'cb1fc3e0-7fef-11eb-827d-d94e80a23d8d',
|
||||
rule_name: 'Latency threshold | opbeans-java',
|
||||
rule_type_id: 'apm.transaction_duration',
|
||||
rule_type_name: 'Latency threshold',
|
||||
alert_instance_title: ['opbeans-java/request:production'],
|
||||
alert_instance_name: 'apm.transaction_duration_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: 'ffa0437d-6656-4553-a1cd-c170fc6e2f81',
|
||||
influencers: [
|
||||
'service.name:opbeans-java',
|
||||
'service.environment:production',
|
||||
'transaction.type:request',
|
||||
],
|
||||
fields: {
|
||||
'processor.event': 'transaction',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
'transaction.type': 'request',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615323780000,
|
||||
y: 13145.51171875,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615324080000,
|
||||
y: 15995.15625,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615324380000,
|
||||
y: 18974.59375,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615324680000,
|
||||
y: 11604.87890625,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615324980000,
|
||||
y: 17945.9609375,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615325280000,
|
||||
y: 9933.22265625,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615325580000,
|
||||
y: 10011.58984375,
|
||||
threshold: 1000,
|
||||
},
|
||||
{
|
||||
x: 1615325880000,
|
||||
y: 10953.1845703125,
|
||||
threshold: 1000,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615325663207,
|
||||
first_seen: 1615324762861,
|
||||
severity: 'warning',
|
||||
severity_value: 27,
|
||||
reason: 'Error count for opbeans-java in production was above the threshold of 2 (27)',
|
||||
rule_id: '335b38d0-80be-11eb-9fd1-d3725789930d',
|
||||
rule_name: 'Error count threshold',
|
||||
rule_type_id: 'apm.error_rate',
|
||||
rule_type_name: 'Error count threshold',
|
||||
alert_instance_title: ['opbeans-java:production'],
|
||||
alert_instance_name: 'opbeans-java_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: 'bf5f9574-57c8-44ed-9a3c-512b446695cf',
|
||||
influencers: ['service.name:opbeans-java', 'service.environment:production'],
|
||||
fields: {
|
||||
'processor.event': 'error',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615324740000,
|
||||
y: 34,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325040000,
|
||||
y: 35,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325340000,
|
||||
y: 31,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615325640000,
|
||||
y: 27,
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615324642764,
|
||||
first_seen: 1615324402620,
|
||||
severity: 'warning',
|
||||
severity_value: 32,
|
||||
reason: 'Error count for opbeans-java in production was above the threshold of 2 (32)',
|
||||
rule_id: '335b38d0-80be-11eb-9fd1-d3725789930d',
|
||||
rule_name: 'Error count threshold',
|
||||
rule_type_id: 'apm.error_rate',
|
||||
rule_type_name: 'Error count threshold',
|
||||
alert_instance_title: ['opbeans-java:production'],
|
||||
alert_instance_name: 'opbeans-java_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: '87768bef-67a3-4ddd-b95d-7ab8830b30ef',
|
||||
influencers: ['service.name:opbeans-java', 'service.environment:production'],
|
||||
fields: {
|
||||
'processor.event': 'error',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615324402000,
|
||||
y: 30,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324432000,
|
||||
y: null,
|
||||
threshold: null,
|
||||
},
|
||||
{
|
||||
x: 1615324462000,
|
||||
y: 28,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324492000,
|
||||
y: null,
|
||||
threshold: null,
|
||||
},
|
||||
{
|
||||
x: 1615324522000,
|
||||
y: 30,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324552000,
|
||||
y: null,
|
||||
threshold: null,
|
||||
},
|
||||
{
|
||||
x: 1615324582000,
|
||||
y: 18,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324612000,
|
||||
y: null,
|
||||
threshold: null,
|
||||
},
|
||||
{
|
||||
x: 1615324642000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
{
|
||||
'@timestamp': 1615324282583,
|
||||
first_seen: 1615323802378,
|
||||
severity: 'warning',
|
||||
severity_value: 30,
|
||||
reason: 'Error count for opbeans-java in production was above the threshold of 2 (30)',
|
||||
rule_id: '335b38d0-80be-11eb-9fd1-d3725789930d',
|
||||
rule_name: 'Error count threshold',
|
||||
rule_type_id: 'apm.error_rate',
|
||||
rule_type_name: 'Error count threshold',
|
||||
alert_instance_title: ['opbeans-java:production'],
|
||||
alert_instance_name: 'opbeans-java_production',
|
||||
unique: 1,
|
||||
group_by_field: 'alert_instance.uuid',
|
||||
group_by_value: '31d087bd-51ae-419d-81c0-d0671eb97392',
|
||||
influencers: ['service.name:opbeans-java', 'service.environment:production'],
|
||||
fields: {
|
||||
'processor.event': 'error',
|
||||
'service.name': 'opbeans-java',
|
||||
'service.environment': 'production',
|
||||
},
|
||||
timeseries: [
|
||||
{
|
||||
x: 1615323780000,
|
||||
y: 31,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615323840000,
|
||||
y: 30,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615323900000,
|
||||
y: 24,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615323960000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324020000,
|
||||
y: 32,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324080000,
|
||||
y: 30,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324140000,
|
||||
y: 25,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324200000,
|
||||
y: 34,
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
x: 1615324260000,
|
||||
y: 30,
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
recovered: true,
|
||||
},
|
||||
];
|
99
x-pack/plugins/observability/public/pages/alerts/index.tsx
Normal file
99
x-pack/plugins/observability/public/pages/alerts/index.tsx
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiButton,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiPage,
|
||||
EuiPageHeader,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { ExperimentalBadge } from '../../components/shared/experimental_badge';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { RouteParams } from '../../routes';
|
||||
import { AlertsSearchBar } from './alerts_search_bar';
|
||||
import { AlertItem, AlertsTable } from './alerts_table';
|
||||
import { wireframeData } from './example_data';
|
||||
|
||||
interface AlertsPageProps {
|
||||
items?: AlertItem[];
|
||||
routeParams: RouteParams<'/alerts'>;
|
||||
}
|
||||
|
||||
export function AlertsPage({ items }: AlertsPageProps) {
|
||||
// For now, if we're not passed any items load the example wireframe data.
|
||||
if (!items) {
|
||||
items = wireframeData;
|
||||
}
|
||||
|
||||
const { core } = usePluginContext();
|
||||
const { prepend } = core.http.basePath;
|
||||
|
||||
// In a future milestone we'll have a page dedicated to rule management in
|
||||
// observability. For now link to the settings page.
|
||||
const manageDetectionRulesHref = prepend(
|
||||
'/app/management/insightsAndAlerting/triggersActions/alerts'
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageHeader
|
||||
pageTitle={
|
||||
<>
|
||||
{i18n.translate('xpack.observability.alertsTitle', { defaultMessage: 'Alerts' })}{' '}
|
||||
<ExperimentalBadge />
|
||||
</>
|
||||
}
|
||||
rightSideItems={[
|
||||
<EuiButton fill href={manageDetectionRulesHref} iconType="gear">
|
||||
{i18n.translate('xpack.observability.alerts.manageDetectionRulesButtonLabel', {
|
||||
defaultMessage: 'Manage detection rules',
|
||||
})}
|
||||
</EuiButton>,
|
||||
]}
|
||||
>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.observability.alertsDisclaimerTitle', {
|
||||
defaultMessage: 'Experimental',
|
||||
})}
|
||||
color="warning"
|
||||
iconType="beaker"
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.observability.alertsDisclaimerText', {
|
||||
defaultMessage:
|
||||
'This page shows an experimental alerting view. The data shown here will probably not be an accurate representation of alerts. A non-experimental list of alerts is available in the Alerts and Actions settings in Stack Management.',
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
<EuiLink
|
||||
href={prepend('/app/management/insightsAndAlerting/triggersActions/alerts')}
|
||||
>
|
||||
{i18n.translate('xpack.observability.alertsDisclaimerLinkText', {
|
||||
defaultMessage: 'Alerts and Actions',
|
||||
})}
|
||||
</EuiLink>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AlertsSearchBar />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AlertsTable items={items} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeader>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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, { ComponentType } from 'react';
|
||||
import { CasesPage } from '.';
|
||||
import { RouteParams } from '../../routes';
|
||||
|
||||
export default {
|
||||
title: 'app/Cases',
|
||||
component: CasesPage,
|
||||
decorators: [
|
||||
(Story: ComponentType) => {
|
||||
return <Story />;
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function EmptyState() {
|
||||
return <CasesPage routeParams={{} as RouteParams<'/cases'>} />;
|
||||
}
|
49
x-pack/plugins/observability/public/pages/cases/index.tsx
Normal file
49
x-pack/plugins/observability/public/pages/cases/index.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageHeader } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { ExperimentalBadge } from '../../components/shared/experimental_badge';
|
||||
import { RouteParams } from '../../routes';
|
||||
|
||||
interface CasesProps {
|
||||
routeParams: RouteParams<'/cases'>;
|
||||
}
|
||||
|
||||
export function CasesPage(props: CasesProps) {
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageHeader
|
||||
pageTitle={
|
||||
<>
|
||||
{i18n.translate('xpack.observability.casesTitle', { defaultMessage: 'Cases' })}{' '}
|
||||
<ExperimentalBadge />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.observability.casesDisclaimerTitle', {
|
||||
defaultMessage: 'Coming soon',
|
||||
})}
|
||||
color="warning"
|
||||
iconType="beaker"
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.observability.casesDisclaimerText', {
|
||||
defaultMessage: 'This is the future home of cases.',
|
||||
})}
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeader>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
|
@ -38,25 +38,53 @@ export class Plugin implements PluginClass<ObservabilityPluginSetup, Observabili
|
|||
constructor(context: PluginInitializerContext) {}
|
||||
|
||||
public setup(core: CoreSetup, plugins: ObservabilityPluginSetupDeps) {
|
||||
const category = DEFAULT_APP_CATEGORIES.observability;
|
||||
const euiIconType = 'logo-observability';
|
||||
const mount = async (params: AppMountParameters<unknown>) => {
|
||||
// Load application bundle
|
||||
const { renderApp } = await import('./application');
|
||||
// Get start services
|
||||
const [coreStart] = await core.getStartServices();
|
||||
|
||||
return renderApp(coreStart, plugins, params);
|
||||
};
|
||||
const updater$ = this.appUpdater$;
|
||||
|
||||
core.application.register({
|
||||
id: 'observability-overview',
|
||||
title: 'Overview',
|
||||
order: 8000,
|
||||
euiIconType: 'logoObservability',
|
||||
appRoute: '/app/observability',
|
||||
updater$: this.appUpdater$,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
|
||||
mount: async (params: AppMountParameters<unknown>) => {
|
||||
// Load application bundle
|
||||
const { renderApp } = await import('./application');
|
||||
// Get start services
|
||||
const [coreStart] = await core.getStartServices();
|
||||
|
||||
return renderApp(coreStart, plugins, params);
|
||||
},
|
||||
order: 8000,
|
||||
category,
|
||||
euiIconType,
|
||||
mount,
|
||||
updater$,
|
||||
});
|
||||
|
||||
if (core.uiSettings.get('observability:enableAlertingExperience')) {
|
||||
core.application.register({
|
||||
id: 'observability-alerts',
|
||||
title: 'Alerts',
|
||||
appRoute: '/app/observability/alerts',
|
||||
order: 8025,
|
||||
category,
|
||||
euiIconType,
|
||||
mount,
|
||||
updater$,
|
||||
});
|
||||
|
||||
core.application.register({
|
||||
id: 'observability-cases',
|
||||
title: 'Cases',
|
||||
appRoute: '/app/observability/cases',
|
||||
order: 8050,
|
||||
category,
|
||||
euiIconType,
|
||||
mount,
|
||||
updater$,
|
||||
});
|
||||
}
|
||||
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.registerSolution({
|
||||
id: 'observability',
|
||||
|
|
|
@ -12,6 +12,8 @@ import { HomePage } from '../pages/home';
|
|||
import { LandingPage } from '../pages/landing';
|
||||
import { OverviewPage } from '../pages/overview';
|
||||
import { jsonRt } from './json_rt';
|
||||
import { AlertsPage } from '../pages/alerts';
|
||||
import { CasesPage } from '../pages/cases';
|
||||
|
||||
export type RouteParams<T extends keyof typeof routes> = DecodeParams<typeof routes[T]['params']>;
|
||||
|
||||
|
@ -25,6 +27,7 @@ export interface Params {
|
|||
query?: t.HasProps;
|
||||
path?: t.HasProps;
|
||||
}
|
||||
|
||||
export const routes = {
|
||||
'/': {
|
||||
handler: () => {
|
||||
|
@ -72,4 +75,44 @@ export const routes = {
|
|||
},
|
||||
],
|
||||
},
|
||||
'/cases': {
|
||||
handler: (routeParams: any) => {
|
||||
return <CasesPage routeParams={routeParams} />;
|
||||
},
|
||||
params: {
|
||||
query: t.partial({
|
||||
rangeFrom: t.string,
|
||||
rangeTo: t.string,
|
||||
refreshPaused: jsonRt.pipe(t.boolean),
|
||||
refreshInterval: jsonRt.pipe(t.number),
|
||||
}),
|
||||
},
|
||||
breadcrumb: [
|
||||
{
|
||||
text: i18n.translate('xpack.observability.cases.breadcrumb', {
|
||||
defaultMessage: 'Cases',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
'/alerts': {
|
||||
handler: (routeParams: any) => {
|
||||
return <AlertsPage routeParams={routeParams} />;
|
||||
},
|
||||
params: {
|
||||
query: t.partial({
|
||||
rangeFrom: t.string,
|
||||
rangeTo: t.string,
|
||||
refreshPaused: jsonRt.pipe(t.boolean),
|
||||
refreshInterval: jsonRt.pipe(t.number),
|
||||
}),
|
||||
},
|
||||
breadcrumb: [
|
||||
{
|
||||
text: i18n.translate('xpack.observability.alerts.breadcrumb', {
|
||||
defaultMessage: 'Alerts',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
ScopedAnnotationsClientFactory,
|
||||
AnnotationsAPI,
|
||||
} from './lib/annotations/bootstrap_annotations';
|
||||
import { uiSettings } from './ui_settings';
|
||||
|
||||
type LazyScopedAnnotationsClientFactory = (
|
||||
...args: Parameters<ScopedAnnotationsClientFactory>
|
||||
|
@ -32,6 +33,8 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
|
||||
let annotationsApiPromise: Promise<AnnotationsAPI> | undefined;
|
||||
|
||||
core.uiSettings.register(uiSettings);
|
||||
|
||||
if (config.annotations.enabled) {
|
||||
annotationsApiPromise = bootstrapAnnotations({
|
||||
core,
|
||||
|
|
32
x-pack/plugins/observability/server/ui_settings.ts
Normal file
32
x-pack/plugins/observability/server/ui_settings.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { UiSettingsParams } from '../../../../src/core/types';
|
||||
import { enableAlertingExperience } from '../common/ui_settings_keys';
|
||||
|
||||
/**
|
||||
* uiSettings definitions for Observability.
|
||||
*/
|
||||
export const uiSettings: Record<string, UiSettingsParams<boolean>> = {
|
||||
[enableAlertingExperience]: {
|
||||
category: ['observability'],
|
||||
name: i18n.translate('xpack.observability.enableAlertingExperienceExperimentName', {
|
||||
defaultMessage: 'Observability alerting experience',
|
||||
}),
|
||||
value: false,
|
||||
description: i18n.translate(
|
||||
'xpack.observability.enableAlertingExperienceExperimentDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Enable the experimental alerting experience for Observability. Adds the Alerts and Cases pages.',
|
||||
}
|
||||
),
|
||||
schema: schema.boolean(),
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue