Skip to content

Commit

Permalink
[Monitoring] Fix issues displaying alerts (#72891)
Browse files Browse the repository at this point in the history
* Fix issues displaying alerts

* Fix type issues

* More support for multiple alerts

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
chrisronline and elasticmachine committed Jul 22, 2020
1 parent bdc08d0 commit 854af2e
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 81 deletions.
36 changes: 24 additions & 12 deletions x-pack/plugins/monitoring/public/alerts/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,25 @@ import { AlertPanel } from './panel';
import { Legacy } from '../legacy_shims';
import { isInSetupMode } from '../lib/setup_mode';

function getDateFromState(states: CommonAlertState[]) {
const timestamp = states[0].state.ui.triggeredMS;
function getDateFromState(state: CommonAlertState) {
const timestamp = state.state.ui.triggeredMS;
const tz = Legacy.shims.uiSettings.get('dateFormat:tz');
return formatDateTimeLocal(timestamp, false, tz === 'Browser' ? null : tz);
}

export const numberOfAlertsLabel = (count: number) => `${count} alert${count > 1 ? 's' : ''}`;

interface AlertInPanel {
alert: CommonAlertStatus;
alertState: CommonAlertState;
}

interface Props {
alerts: { [alertTypeId: string]: CommonAlertStatus };
stateFilter: (state: AlertState) => boolean;
}
export const AlertsBadge: React.FC<Props> = (props: Props) => {
const { stateFilter = () => true } = props;
const [showPopover, setShowPopover] = React.useState<AlertSeverity | boolean | null>(null);
const inSetupMode = isInSetupMode();
const alerts = Object.values(props.alerts).filter(Boolean);
Expand Down Expand Up @@ -93,15 +100,20 @@ export const AlertsBadge: React.FC<Props> = (props: Props) => {
);
} else {
const byType = {
[AlertSeverity.Danger]: [] as CommonAlertStatus[],
[AlertSeverity.Warning]: [] as CommonAlertStatus[],
[AlertSeverity.Success]: [] as CommonAlertStatus[],
[AlertSeverity.Danger]: [] as AlertInPanel[],
[AlertSeverity.Warning]: [] as AlertInPanel[],
[AlertSeverity.Success]: [] as AlertInPanel[],
};

for (const alert of alerts) {
for (const alertState of alert.states) {
const state = alertState.state as AlertState;
byType[state.ui.severity].push(alert);
if (alertState.firing && stateFilter(alertState.state)) {
const state = alertState.state as AlertState;
byType[state.ui.severity].push({
alertState,
alert,
});
}
}
}

Expand All @@ -127,14 +139,14 @@ export const AlertsBadge: React.FC<Props> = (props: Props) => {
{
id: 0,
title: `Alerts`,
items: list.map(({ alert, states }, index) => {
items: list.map(({ alert, alertState }, index) => {
return {
name: (
<Fragment>
<EuiText size="s">
<h4>{getDateFromState(states)}</h4>
<h4>{getDateFromState(alertState)}</h4>
</EuiText>
<EuiText>{alert.label}</EuiText>
<EuiText>{alert.alert.label}</EuiText>
</Fragment>
),
panel: index + 1,
Expand All @@ -144,9 +156,9 @@ export const AlertsBadge: React.FC<Props> = (props: Props) => {
...list.map((alertStatus, index) => {
return {
id: index + 1,
title: getDateFromState(alertStatus.states),
title: getDateFromState(alertStatus.alertState),
width: 400,
content: <AlertPanel alert={alertStatus} />,
content: <AlertPanel alert={alertStatus.alert} alertState={alertStatus.alertState} />,
};
}),
];
Expand Down
9 changes: 5 additions & 4 deletions x-pack/plugins/monitoring/public/alerts/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { CommonAlertStatus } from '../../common/types';
import { AlertSeverity } from '../../common/enums';
import { replaceTokens } from './lib/replace_tokens';
import { AlertMessage } from '../../server/alerts/types';
import { AlertMessage, AlertState } from '../../server/alerts/types';

const TYPES = [
{
Expand All @@ -31,16 +31,17 @@ const TYPES = [

interface Props {
alerts: { [alertTypeId: string]: CommonAlertStatus };
stateFilter: (state: AlertState) => boolean;
}
export const AlertsCallout: React.FC<Props> = (props: Props) => {
const { alerts } = props;
const { alerts, stateFilter = () => true } = props;

const callouts = TYPES.map((type) => {
const list = [];
for (const alertTypeId of Object.keys(alerts)) {
const alertInstance = alerts[alertTypeId];
for (const { state } of alertInstance.states) {
if (state.ui.severity === type.severity) {
for (const { firing, state } of alertInstance.states) {
if (firing && stateFilter(state) && state.ui.severity === type.severity) {
list.push(state);
}
}
Expand Down
20 changes: 8 additions & 12 deletions x-pack/plugins/monitoring/public/alerts/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
EuiListGroupItem,
} from '@elastic/eui';

import { CommonAlertStatus } from '../../common/types';
import { CommonAlertStatus, CommonAlertState } from '../../common/types';
import { AlertMessage } from '../../server/alerts/types';
import { Legacy } from '../legacy_shims';
import { replaceTokens } from './lib/replace_tokens';
Expand All @@ -30,10 +30,12 @@ import { BASE_ALERT_API_PATH } from '../../../alerts/common';

interface Props {
alert: CommonAlertStatus;
alertState?: CommonAlertState;
}
export const AlertPanel: React.FC<Props> = (props: Props) => {
const {
alert: { states, alert },
alert: { alert },
alertState,
} = props;
const [showFlyout, setShowFlyout] = React.useState(false);
const [isEnabled, setIsEnabled] = React.useState(alert.rawAlert.enabled);
Expand Down Expand Up @@ -190,20 +192,14 @@ export const AlertPanel: React.FC<Props> = (props: Props) => {
</Fragment>
);

if (inSetupMode) {
if (inSetupMode || !alertState) {
return <div style={{ padding: '1rem' }}>{configurationUi}</div>;
}

const firingStates = states.filter((state) => state.firing);
if (!firingStates.length) {
return <div style={{ padding: '1rem' }}>{configurationUi}</div>;
}

const firingState = firingStates[0];
const nextStepsUi =
firingState.state.ui.message.nextSteps && firingState.state.ui.message.nextSteps.length ? (
alertState.state.ui.message.nextSteps && alertState.state.ui.message.nextSteps.length ? (
<EuiListGroup>
{firingState.state.ui.message.nextSteps.map((step: AlertMessage, index: number) => (
{alertState.state.ui.message.nextSteps.map((step: AlertMessage, index: number) => (
<EuiListGroupItem size="s" key={index} label={replaceTokens(step)} />
))}
</EuiListGroup>
Expand All @@ -213,7 +209,7 @@ export const AlertPanel: React.FC<Props> = (props: Props) => {
<Fragment>
<div style={{ padding: '1rem' }}>
<EuiTitle size="xs">
<h5>{replaceTokens(firingState.state.ui.message)}</h5>
<h5>{replaceTokens(alertState.state.ui.message)}</h5>
</EuiTitle>
{nextStepsUi ? <EuiSpacer size="s" /> : null}
{nextStepsUi}
Expand Down
22 changes: 15 additions & 7 deletions x-pack/plugins/monitoring/public/alerts/status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,44 @@ import { CommonAlertStatus } from '../../common/types';
import { AlertSeverity } from '../../common/enums';
import { AlertState } from '../../server/alerts/types';
import { AlertsBadge } from './badge';
import { isInSetupMode } from '../lib/setup_mode';

interface Props {
alerts: { [alertTypeId: string]: CommonAlertStatus };
showBadge: boolean;
showOnlyCount: boolean;
stateFilter: (state: AlertState) => boolean;
}
export const AlertsStatus: React.FC<Props> = (props: Props) => {
const { alerts, showBadge = false, showOnlyCount = false } = props;
const { alerts, showBadge = false, showOnlyCount = false, stateFilter = () => true } = props;
const inSetupMode = isInSetupMode();

if (!alerts) {
return null;
}

let atLeastOneDanger = false;
const count = Object.values(alerts).reduce((cnt, alertStatus) => {
if (alertStatus.states.length) {
const firingStates = alertStatus.states.filter((state) => state.firing);
const firingAndFilterStates = firingStates.filter((state) => stateFilter(state.state));
cnt += firingAndFilterStates.length;
if (firingStates.length) {
if (!atLeastOneDanger) {
for (const state of alertStatus.states) {
if ((state.state as AlertState).ui.severity === AlertSeverity.Danger) {
if (
stateFilter(state.state) &&
(state.state as AlertState).ui.severity === AlertSeverity.Danger
) {
atLeastOneDanger = true;
break;
}
}
}
cnt++;
}
return cnt;
}, 0);

if (count === 0) {
if (count === 0 && (!inSetupMode || showOnlyCount)) {
return (
<EuiToolTip
content={i18n.translate('xpack.monitoring.alerts.status.clearToolip', {
Expand All @@ -62,8 +70,8 @@ export const AlertsStatus: React.FC<Props> = (props: Props) => {
);
}

if (showBadge) {
return <AlertsBadge alerts={alerts} />;
if (showBadge || inSetupMode) {
return <AlertsBadge alerts={alerts} stateFilter={stateFilter} />;
}

const severity = atLeastOneDanger ? AlertSeverity.Danger : AlertSeverity.Warning;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,14 @@ export const Node = ({
<EuiPage>
<EuiPageBody>
<EuiPanel>
<NodeDetailStatus stats={nodeSummary} alerts={alerts} />
<NodeDetailStatus
stats={nodeSummary}
alerts={alerts}
alertsStateFilter={(state) => state.nodeId === nodeId}
/>
</EuiPanel>
<EuiSpacer size="m" />
<AlertsCallout alerts={alerts} />
<AlertsCallout alerts={alerts} stateFilter={(state) => state.nodeId === nodeId} />
<EuiPageContent>
<EuiFlexGrid columns={2} gutterSize="s">
{metricsToShow.map((metric, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { formatMetric } from '../../../lib/format_number';
import { i18n } from '@kbn/i18n';
import { AlertsStatus } from '../../../alerts/status';

export function NodeDetailStatus({ stats, alerts = {} }) {
export function NodeDetailStatus({ stats, alerts = {}, alertsStateFilter = () => true }) {
const {
transport_address: transportAddress,
usedHeap,
Expand All @@ -33,7 +33,7 @@ export function NodeDetailStatus({ stats, alerts = {} }) {
label: i18n.translate('xpack.monitoring.elasticsearch.nodeDetailStatus.alerts', {
defaultMessage: 'Alerts',
}),
value: <AlertsStatus alerts={alerts} showOnlyCount={true} />,
value: <AlertsStatus alerts={alerts} showOnlyCount={true} stateFilter={alertsStateFilter} />,
},
{
label: i18n.translate('xpack.monitoring.elasticsearch.nodeDetailStatus.transportAddress', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,14 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler
field: 'alerts',
width: '175px',
sortable: true,
render: () => {
return <AlertsStatus showBadge={true} alerts={alerts} />;
render: (_field, node) => {
return (
<AlertsStatus
showBadge={true}
alerts={alerts}
stateFilter={(state) => state.nodeId === node.resolver}
/>
);
},
});

Expand Down
Loading

0 comments on commit 854af2e

Please sign in to comment.