mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ErrorHandling: Error boundary for every container (#18845)
* ErrorHandling: Error boundary for every container * Remvoe custom query editor errors
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
import React, { PureComponent, ReactNode } from 'react';
|
import React, { PureComponent, ReactNode } from 'react';
|
||||||
import { Alert } from '@grafana/ui';
|
import { Alert } from '../Alert/Alert';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
|
||||||
interface ErrorInfo {
|
interface ErrorInfo {
|
||||||
componentStack: string;
|
componentStack: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RenderProps {
|
interface RenderProps {
|
||||||
error: Error;
|
error: Error | null;
|
||||||
errorInfo: ErrorInfo;
|
errorInfo: ErrorInfo | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -15,8 +16,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
error: Error;
|
error: Error | null;
|
||||||
errorInfo: ErrorInfo;
|
errorInfo: ErrorInfo | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ErrorBoundary extends PureComponent<Props, State> {
|
export class ErrorBoundary extends PureComponent<Props, State> {
|
||||||
@@ -35,6 +36,7 @@ export class ErrorBoundary extends PureComponent<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
const { error, errorInfo } = this.state;
|
const { error, errorInfo } = this.state;
|
||||||
|
|
||||||
return children({
|
return children({
|
||||||
error,
|
error,
|
||||||
errorInfo,
|
errorInfo,
|
||||||
@@ -42,18 +44,28 @@ export class ErrorBoundary extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAlertPageStyle() {
|
||||||
|
return css`
|
||||||
|
width: 500px;
|
||||||
|
margin: 64px auto;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
interface WithAlertBoxProps {
|
interface WithAlertBoxProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
style?: 'page' | 'alertbox';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
|
export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
|
||||||
static defaultProps: Partial<WithAlertBoxProps> = {
|
static defaultProps: Partial<WithAlertBoxProps> = {
|
||||||
title: 'An unexpected error happened',
|
title: 'An unexpected error happened',
|
||||||
|
style: 'alertbox',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { title, children } = this.props;
|
const { title, children, style } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
{({ error, errorInfo }) => {
|
{({ error, errorInfo }) => {
|
||||||
@@ -61,15 +73,28 @@ export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (style === 'alertbox') {
|
||||||
<Alert title={title}>
|
return (
|
||||||
<details style={{ whiteSpace: 'pre-wrap' }}>
|
<Alert title={title || ''}>
|
||||||
{error && error.toString()}
|
<details style={{ whiteSpace: 'pre-wrap' }}>
|
||||||
<br />
|
{error && error.toString()}
|
||||||
{errorInfo.componentStack}
|
<br />
|
||||||
</details>
|
{errorInfo.componentStack}
|
||||||
</Alert>
|
</details>
|
||||||
);
|
</Alert>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={getAlertPageStyle()}>
|
||||||
|
<h2>{title}</h2>
|
||||||
|
<details style={{ whiteSpace: 'pre-wrap' }}>
|
||||||
|
{error && error.toString()}
|
||||||
|
<br />
|
||||||
|
{errorInfo.componentStack}
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
@@ -78,3 +78,5 @@ export { VariableSuggestion, VariableOrigin } from './DataLinks/DataLinkSuggesti
|
|||||||
export { DataLinksEditor } from './DataLinks/DataLinksEditor';
|
export { DataLinksEditor } from './DataLinks/DataLinksEditor';
|
||||||
export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu';
|
export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu';
|
||||||
export { SeriesIcon } from './Legend/SeriesIcon';
|
export { SeriesIcon } from './Legend/SeriesIcon';
|
||||||
|
|
||||||
|
export { ErrorBoundary, ErrorBoundaryAlert } from './ErrorBoundary/ErrorBoundary';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Unsubscribable } from 'rxjs';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { PanelHeader } from './PanelHeader/PanelHeader';
|
import { PanelHeader } from './PanelHeader/PanelHeader';
|
||||||
import { ErrorBoundary } from 'app/core/components/ErrorBoundary/ErrorBoundary';
|
import { ErrorBoundary } from '@grafana/ui';
|
||||||
|
|
||||||
// Utils & Services
|
// Utils & Services
|
||||||
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
|
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
|||||||
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
||||||
import { Emitter } from 'app/core/utils/emitter';
|
import { Emitter } from 'app/core/utils/emitter';
|
||||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||||
import { ErrorBoundaryAlert } from 'app/core/components/ErrorBoundary/ErrorBoundary';
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelModel } from '../state/PanelModel';
|
import { PanelModel } from '../state/PanelModel';
|
||||||
import { DataQuery, DataSourceApi, PanelData, DataQueryRequest } from '@grafana/ui';
|
import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, ErrorBoundaryAlert } from '@grafana/ui';
|
||||||
import { TimeRange, LoadingState } from '@grafana/data';
|
import { TimeRange, LoadingState } from '@grafana/data';
|
||||||
import { DashboardModel } from '../state/DashboardModel';
|
import { DashboardModel } from '../state/DashboardModel';
|
||||||
|
|
||||||
@@ -259,7 +258,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={bodyClasses}>
|
<div className={bodyClasses}>
|
||||||
<ErrorBoundaryAlert title="Data source query editor failed">{this.renderPluginEditor()}</ErrorBoundaryAlert>
|
<ErrorBoundaryAlert>{this.renderPluginEditor()}</ErrorBoundaryAlert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
export class ErrorBoundary extends Component<{}, any> {
|
|
||||||
constructor(props: {}) {
|
|
||||||
super(props);
|
|
||||||
this.state = { error: null, errorInfo: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidCatch(error: any, errorInfo: any) {
|
|
||||||
// Catch errors in any components below and re-render with error message
|
|
||||||
this.setState({
|
|
||||||
error,
|
|
||||||
errorInfo,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.errorInfo) {
|
|
||||||
// Error path
|
|
||||||
return (
|
|
||||||
<div className="explore-container">
|
|
||||||
<h3>An unexpected error happened.</h3>
|
|
||||||
<details style={{ whiteSpace: 'pre-wrap' }}>
|
|
||||||
{this.state.error && this.state.error.toString()}
|
|
||||||
<br />
|
|
||||||
{this.state.errorInfo.componentStack}
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Normally, just render children
|
|
||||||
return this.props.children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,8 +9,7 @@ import memoizeOne from 'memoize-one';
|
|||||||
// Services & Utils
|
// Services & Utils
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
// Components
|
// Components
|
||||||
import { Alert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
|
import { Alert, ErrorBoundaryAlert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
|
||||||
import LogsContainer from './LogsContainer';
|
import LogsContainer from './LogsContainer';
|
||||||
import QueryRows from './QueryRows';
|
import QueryRows from './QueryRows';
|
||||||
import TableContainer from './TableContainer';
|
import TableContainer from './TableContainer';
|
||||||
@@ -275,7 +274,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="m-t-2" style={{ width }}>
|
<main className="m-t-2" style={{ width }}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundaryAlert>
|
||||||
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
|
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
|
||||||
{!showingStartPage && (
|
{!showingStartPage && (
|
||||||
<>
|
<>
|
||||||
@@ -310,7 +309,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundaryAlert>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import { connect } from 'react-redux';
|
|||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
import { ExploreId } from 'app/types/explore';
|
import { ExploreId } from 'app/types/explore';
|
||||||
|
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
|
||||||
import Explore from './Explore';
|
import Explore from './Explore';
|
||||||
import { CustomScrollbar } from '@grafana/ui';
|
import { CustomScrollbar, ErrorBoundaryAlert } from '@grafana/ui';
|
||||||
import { resetExploreAction } from './state/actionTypes';
|
import { resetExploreAction } from './state/actionTypes';
|
||||||
|
|
||||||
interface WrapperProps {
|
interface WrapperProps {
|
||||||
@@ -27,13 +26,13 @@ export class Wrapper extends Component<WrapperProps> {
|
|||||||
<div className="page-scrollbar-wrapper">
|
<div className="page-scrollbar-wrapper">
|
||||||
<CustomScrollbar autoHeightMin={'100%'} autoHeightMax={''} className="custom-scrollbar--page">
|
<CustomScrollbar autoHeightMin={'100%'} autoHeightMax={''} className="custom-scrollbar--page">
|
||||||
<div className="explore-wrapper">
|
<div className="explore-wrapper">
|
||||||
<ErrorBoundary>
|
<ErrorBoundaryAlert style="page">
|
||||||
<Explore exploreId={ExploreId.left} />
|
<Explore exploreId={ExploreId.left} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundaryAlert>
|
||||||
{split && (
|
{split && (
|
||||||
<ErrorBoundary>
|
<ErrorBoundaryAlert style="page">
|
||||||
<Explore exploreId={ExploreId.right} />
|
<Explore exploreId={ExploreId.right} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundaryAlert>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ import coreModule from 'app/core/core_module';
|
|||||||
import { store } from 'app/store/store';
|
import { store } from 'app/store/store';
|
||||||
import { ContextSrv } from 'app/core/services/context_srv';
|
import { ContextSrv } from 'app/core/services/context_srv';
|
||||||
import { provideTheme } from 'app/core/utils/ConfigProvider';
|
import { provideTheme } from 'app/core/utils/ConfigProvider';
|
||||||
|
import { ErrorBoundaryAlert } from '@grafana/ui';
|
||||||
|
|
||||||
function WrapInProvider(store: any, Component: any, props: any) {
|
function WrapInProvider(store: any, Component: any, props: any) {
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Component {...props} />
|
<ErrorBoundaryAlert style="page">
|
||||||
|
<Component {...props} />
|
||||||
|
</ErrorBoundaryAlert>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user