[Logs + Metrics UI] Prevent component errors from breaking the whole UI (#65456)

This commit is contained in:
Felix Stürmer 2020-05-07 11:33:50 +02:00 committed by GitHub
parent f53b147097
commit 8a8647ab95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 221 additions and 199 deletions

View file

@ -4,18 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { ColumnarPage } from '../../../components/page';
import { LogEntryCategoriesPageContent } from './page_content';
import { LogEntryCategoriesPageProviders } from './page_providers';
export const LogEntryCategoriesPage = () => {
return (
<LogEntryCategoriesPageProviders>
<ColumnarPage data-test-subj="logsLogEntryCategoriesPage">
<LogEntryCategoriesPageContent />
</ColumnarPage>
</LogEntryCategoriesPageProviders>
<EuiErrorBoundary>
<LogEntryCategoriesPageProviders>
<ColumnarPage data-test-subj="logsLogEntryCategoriesPage">
<LogEntryCategoriesPageContent />
</ColumnarPage>
</LogEntryCategoriesPageProviders>
</EuiErrorBoundary>
);
};

View file

@ -4,18 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { ColumnarPage } from '../../../components/page';
import { LogEntryRatePageContent } from './page_content';
import { LogEntryRatePageProviders } from './page_providers';
export const LogEntryRatePage = () => {
return (
<LogEntryRatePageProviders>
<ColumnarPage data-test-subj="logsLogEntryRatePage">
<LogEntryRatePageContent />
</ColumnarPage>
</LogEntryRatePageProviders>
<EuiErrorBoundary>
<LogEntryRatePageProviders>
<ColumnarPage data-test-subj="logsLogEntryRatePage">
<LogEntryRatePageContent />
</ColumnarPage>
</LogEntryRatePageProviders>
</EuiErrorBoundary>
);
};

View file

@ -4,16 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { LogsPageContent } from './page_content';
import { LogsPageProviders } from './page_providers';
export const LogsPage: React.FunctionComponent<RouteComponentProps> = ({ match }) => {
export const LogsPage: React.FunctionComponent<RouteComponentProps> = () => {
return (
<LogsPageProviders>
<LogsPageContent />
</LogsPageProviders>
<EuiErrorBoundary>
<LogsPageProviders>
<LogsPageContent />
</LogsPageProviders>
</EuiErrorBoundary>
);
};

View file

@ -7,6 +7,7 @@
import {
EuiButton,
EuiCallOut,
EuiErrorBoundary,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
@ -74,7 +75,7 @@ export const LogsSettingsPage = () => {
}
return (
<>
<EuiErrorBoundary>
<EuiPage>
<EuiPageBody
className="eui-displayBlock"
@ -181,7 +182,7 @@ export const LogsSettingsPage = () => {
</EuiFlexGroup>
</EuiPageBody>
</EuiPage>
</>
</EuiErrorBoundary>
);
};

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { useTrackPageview } from '../../../../../observability/public';
import { ColumnarPage } from '../../../components/page';
@ -15,11 +16,13 @@ export const StreamPage = () => {
useTrackPageview({ app: 'infra_logs', path: 'stream' });
useTrackPageview({ app: 'infra_logs', path: 'stream', delay: 15000 });
return (
<LogsPageProviders>
<ColumnarPage data-test-subj="infraLogsPage">
<StreamPageHeader />
<StreamPageContent />
</ColumnarPage>
</LogsPageProviders>
<EuiErrorBoundary>
<LogsPageProviders>
<ColumnarPage data-test-subj="infraLogsPage">
<StreamPageHeader />
<StreamPageContent />
</ColumnarPage>
</LogsPageProviders>
</EuiErrorBoundary>
);
};

View file

@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { EuiErrorBoundary, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { DocumentTitle } from '../../components/document_title';
import { HelpCenterContent } from '../../components/help_center_content';
import { RoutedTabs } from '../../components/navigation/routed_tabs';
@ -36,103 +36,105 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
const uiCapabilities = useKibana().services.application?.capabilities;
return (
<Source.Provider sourceId="default">
<WaffleOptionsProvider>
<WaffleTimeProvider>
<WaffleFiltersProvider>
<ColumnarPage>
<DocumentTitle
title={i18n.translate('xpack.infra.homePage.documentTitle', {
defaultMessage: 'Metrics',
})}
/>
<HelpCenterContent
feedbackLink="https://discuss.elastic.co/c/metrics"
appName={i18n.translate('xpack.infra.header.infrastructureHelpAppName', {
defaultMessage: 'Metrics',
})}
/>
<Header
breadcrumbs={[
{
text: i18n.translate('xpack.infra.header.infrastructureTitle', {
defaultMessage: 'Metrics',
}),
},
]}
readOnlyBadge={!uiCapabilities?.infrastructure?.save}
/>
<AppNavigation
aria-label={i18n.translate('xpack.infra.header.infrastructureNavigationTitle', {
defaultMessage: 'Metrics',
})}
>
<EuiFlexGroup gutterSize={'none'} alignItems={'center'}>
<EuiFlexItem>
<RoutedTabs
tabs={[
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.inventoryTabTitle', {
defaultMessage: 'Inventory',
}),
pathname: '/inventory',
},
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
pathname: '/explorer',
},
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
defaultMessage: 'Settings',
}),
pathname: '/settings',
},
]}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<Route path={'/explorer'} component={MetricsAlertDropdown} />
<Route path={'/inventory'} component={InventoryAlertDropdown} />
</EuiFlexItem>
</EuiFlexGroup>
</AppNavigation>
<Switch>
<Route path={'/inventory'} component={SnapshotPage} />
<Route
path={'/explorer'}
render={props => (
<WithSource>
{({ configuration, createDerivedIndexPattern }) => (
<MetricsExplorerOptionsContainer.Provider>
<WithMetricsExplorerOptionsUrlState />
{configuration ? (
<MetricsExplorerPage
derivedIndexPattern={createDerivedIndexPattern('metrics')}
source={configuration}
{...props}
/>
) : (
<SourceLoadingPage />
)}
</MetricsExplorerOptionsContainer.Provider>
)}
</WithSource>
)}
<EuiErrorBoundary>
<Source.Provider sourceId="default">
<WaffleOptionsProvider>
<WaffleTimeProvider>
<WaffleFiltersProvider>
<ColumnarPage>
<DocumentTitle
title={i18n.translate('xpack.infra.homePage.documentTitle', {
defaultMessage: 'Metrics',
})}
/>
<Route path={'/settings'} component={MetricsSettingsPage} />
</Switch>
</ColumnarPage>
</WaffleFiltersProvider>
</WaffleTimeProvider>
</WaffleOptionsProvider>
</Source.Provider>
<HelpCenterContent
feedbackLink="https://discuss.elastic.co/c/metrics"
appName={i18n.translate('xpack.infra.header.infrastructureHelpAppName', {
defaultMessage: 'Metrics',
})}
/>
<Header
breadcrumbs={[
{
text: i18n.translate('xpack.infra.header.infrastructureTitle', {
defaultMessage: 'Metrics',
}),
},
]}
readOnlyBadge={!uiCapabilities?.infrastructure?.save}
/>
<AppNavigation
aria-label={i18n.translate('xpack.infra.header.infrastructureNavigationTitle', {
defaultMessage: 'Metrics',
})}
>
<EuiFlexGroup gutterSize={'none'} alignItems={'center'}>
<EuiFlexItem>
<RoutedTabs
tabs={[
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.inventoryTabTitle', {
defaultMessage: 'Inventory',
}),
pathname: '/inventory',
},
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
pathname: '/explorer',
},
{
app: 'metrics',
title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
defaultMessage: 'Settings',
}),
pathname: '/settings',
},
]}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<Route path={'/explorer'} component={MetricsAlertDropdown} />
<Route path={'/inventory'} component={InventoryAlertDropdown} />
</EuiFlexItem>
</EuiFlexGroup>
</AppNavigation>
<Switch>
<Route path={'/inventory'} component={SnapshotPage} />
<Route
path={'/explorer'}
render={props => (
<WithSource>
{({ configuration, createDerivedIndexPattern }) => (
<MetricsExplorerOptionsContainer.Provider>
<WithMetricsExplorerOptionsUrlState />
{configuration ? (
<MetricsExplorerPage
derivedIndexPattern={createDerivedIndexPattern('metrics')}
source={configuration}
{...props}
/>
) : (
<SourceLoadingPage />
)}
</MetricsExplorerOptionsContainer.Provider>
)}
</WithSource>
)}
/>
<Route path={'/settings'} component={MetricsSettingsPage} />
</Switch>
</ColumnarPage>
</WaffleFiltersProvider>
</WaffleTimeProvider>
</WaffleOptionsProvider>
</Source.Provider>
</EuiErrorBoundary>
);
};

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiButton, EuiErrorBoundary, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
@ -41,65 +41,70 @@ export const SnapshotPage = () => {
});
return (
<ColumnarPage>
<DocumentTitle
title={(previousTitle: string) =>
i18n.translate('xpack.infra.infrastructureSnapshotPage.documentTitle', {
defaultMessage: '{previousTitle} | Inventory',
values: {
previousTitle,
},
})
}
/>
{isLoading ? (
<SourceLoadingPage />
) : metricIndicesExist ? (
<>
<FilterBar />
<Layout />
</>
) : hasFailedLoadingSource ? (
<SourceErrorPage errorMessage={loadSourceFailureMessage || ''} retry={loadSource} />
) : (
<NoIndices
title={i18n.translate('xpack.infra.homePage.noMetricsIndicesTitle', {
defaultMessage: "Looks like you don't have any metrics indices.",
})}
message={i18n.translate('xpack.infra.homePage.noMetricsIndicesDescription', {
defaultMessage: "Let's add some!",
})}
actions={
<EuiFlexGroup>
<EuiFlexItem>
<EuiButton
{...tutorialLinkProps}
color="primary"
fill
data-test-subj="infrastructureViewSetupInstructionsButton"
>
{i18n.translate('xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel', {
defaultMessage: 'View setup instructions',
})}
</EuiButton>
</EuiFlexItem>
{uiCapabilities?.infrastructure?.configureSource ? (
<EuiFlexItem>
<ViewSourceConfigurationButton
app="metrics"
data-test-subj="configureSourceButton"
>
{i18n.translate('xpack.infra.configureSourceActionLabel', {
defaultMessage: 'Change source configuration',
})}
</ViewSourceConfigurationButton>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
<EuiErrorBoundary>
<ColumnarPage>
<DocumentTitle
title={(previousTitle: string) =>
i18n.translate('xpack.infra.infrastructureSnapshotPage.documentTitle', {
defaultMessage: '{previousTitle} | Inventory',
values: {
previousTitle,
},
})
}
data-test-subj="noMetricsIndicesPrompt"
/>
)}
</ColumnarPage>
{isLoading ? (
<SourceLoadingPage />
) : metricIndicesExist ? (
<>
<FilterBar />
<Layout />
</>
) : hasFailedLoadingSource ? (
<SourceErrorPage errorMessage={loadSourceFailureMessage || ''} retry={loadSource} />
) : (
<NoIndices
title={i18n.translate('xpack.infra.homePage.noMetricsIndicesTitle', {
defaultMessage: "Looks like you don't have any metrics indices.",
})}
message={i18n.translate('xpack.infra.homePage.noMetricsIndicesDescription', {
defaultMessage: "Let's add some!",
})}
actions={
<EuiFlexGroup>
<EuiFlexItem>
<EuiButton
{...tutorialLinkProps}
color="primary"
fill
data-test-subj="infrastructureViewSetupInstructionsButton"
>
{i18n.translate(
'xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel',
{
defaultMessage: 'View setup instructions',
}
)}
</EuiButton>
</EuiFlexItem>
{uiCapabilities?.infrastructure?.configureSource ? (
<EuiFlexItem>
<ViewSourceConfigurationButton
app="metrics"
data-test-subj="configureSourceButton"
>
{i18n.translate('xpack.infra.configureSourceActionLabel', {
defaultMessage: 'Change source configuration',
})}
</ViewSourceConfigurationButton>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
}
data-test-subj="noMetricsIndicesPrompt"
/>
)}
</ColumnarPage>
</EuiErrorBoundary>
);
};

View file

@ -4,17 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { Source } from '../../../containers/source';
import { MetricsTimeProvider } from './hooks/use_metrics_time';
export const withMetricPageProviders = <T extends object>(Component: React.ComponentType<T>) => (
props: T
) => (
<Source.Provider sourceId="default">
<MetricsTimeProvider>
<Component {...props} />
</MetricsTimeProvider>
</Source.Provider>
<EuiErrorBoundary>
<Source.Provider sourceId="default">
<MetricsTimeProvider>
<Component {...props} />
</MetricsTimeProvider>
</Source.Provider>
</EuiErrorBoundary>
);

View file

@ -4,17 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { IIndexPattern } from 'src/plugins/data/public';
import { useTrackPageview } from '../../../../../observability/public';
import { SourceQuery } from '../../../../common/graphql/types';
import { DocumentTitle } from '../../../components/document_title';
import { NoData } from '../../../components/empty_states';
import { MetricsExplorerCharts } from './components/charts';
import { MetricsExplorerToolbar } from './components/toolbar';
import { SourceQuery } from '../../../../common/graphql/types';
import { NoData } from '../../../components/empty_states';
import { useMetricsExplorerState } from './hooks/use_metric_explorer_state';
import { useTrackPageview } from '../../../../../observability/public';
interface MetricsExplorerPageProps {
source: SourceQuery.Query['source']['configuration'];
@ -45,7 +45,7 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl
useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 });
return (
<React.Fragment>
<EuiErrorBoundary>
<DocumentTitle
title={(previousTitle: string) =>
i18n.translate('xpack.infra.infrastructureMetricsExplorerPage.documentTitle', {
@ -95,6 +95,6 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl
onTimeChange={handleTimeChange}
/>
)}
</React.Fragment>
</EuiErrorBoundary>
);
};

View file

@ -4,16 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { SourceConfigurationSettings } from '../../components/source_configuration/source_configuration_settings';
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { SourceConfigurationSettings } from '../../components/source_configuration/source_configuration_settings';
export const MetricsSettingsPage = () => {
const uiCapabilities = useKibana().services.application?.capabilities;
return (
<SourceConfigurationSettings
shouldAllowEdit={uiCapabilities?.infrastructure?.configureSource as boolean}
displaySettings="metrics"
/>
<EuiErrorBoundary>
<SourceConfigurationSettings
shouldAllowEdit={uiCapabilities?.infrastructure?.configureSource as boolean}
displaySettings="metrics"
/>
</EuiErrorBoundary>
);
};