[RAC][Uptime] Status check reason messages (#123189)

* testing out

* update getStatusMessage

* format interval

* temp: keep only statusMessage for now

* update translation message

* availability check message (previous below threshold)

* add availability interval

* availability & status check message

* i18n check

* fix failing tests

* fix failing tests

* more failing tests

* finalize reason format and continue with fixing failing tests

* fix more failing tests

* fix monitor_status tests

* fix uptime functional tests

* fix more failing tests

* clean up unused stuff

* fix failing tests

* fix failing tests

* more failing tests (pfff)

* fix failing tests

* move all status check translations in the translations file

* a bit of refactoring

* temp

* rename monitor params to monitor status message params

* remove unused PingType

* refactoring: move getInterval to another file and create getMonitorDownStatusMessageParams

* separate alerts table reason message from alert message in the rule flyout

* bring back old alert message format that is used in rule flyout

* fix failing tests

* remove unused file

* refactor status message so that an extra dot does not appear on alert connector message

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
mgiota 2022-01-25 20:53:28 +01:00 committed by GitHub
parent c3866c4032
commit 09aac9e42d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 104 deletions

View file

@ -27162,11 +27162,8 @@
"xpack.uptime.alerts.durationAnomaly.description": "アップタイム監視期間が異常なときにアラートを発行します。",
"xpack.uptime.alerts.monitorExpression.label": "フィルター{title}を削除",
"xpack.uptime.alerts.monitorStatus": "稼働状況の監視ステータス",
"xpack.uptime.alerts.monitorStatus.actionVariables.availabilityMessage": "{availabilityRatio}%のしきい値を下回ります。想定される可用性は{expectedAvailability}%です",
"xpack.uptime.alerts.monitorStatus.actionVariables.context.downMonitorsWithGeo.description": "アラートによって「ダウン」と検知された一部またはすべてのモニターを示す、生成された概要。",
"xpack.uptime.alerts.monitorStatus.actionVariables.context.message.description": "現在ダウンしているモニターを要約する生成されたメッセージ。",
"xpack.uptime.alerts.monitorStatus.actionVariables.down": "ダウン",
"xpack.uptime.alerts.monitorStatus.actionVariables.downAndAvailabilityMessage": "{statusMessage}と{availabilityMessage}",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted": "アラートがトリガーされた場合、現在のトリガー状態が開始するときを示すタイムスタンプ",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt": "このアラートが最初に確認されるときを示すタイムスタンプ",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt": "このアラートが最初にトリガーされたときを示すタイムスタンプ",
@ -27199,7 +27196,6 @@
"xpack.uptime.alerts.monitorStatus.availability.unit.headline": "時間範囲単位を選択します",
"xpack.uptime.alerts.monitorStatus.availability.unit.selectable": "この選択を使用して、このアラートの可用性範囲単位を設定",
"xpack.uptime.alerts.monitorStatus.clientName": "稼働状況の監視ステータス",
"xpack.uptime.alerts.monitorStatus.defaultActionMessage": "URL {monitorUrl}のモニター{monitorName}は{observerLocation}から{statusMessage}です。最新のエラーメッセージは{latestErrorMessage}です",
"xpack.uptime.alerts.monitorStatus.description": "監視が停止しているか、可用性しきい値に違反したときにアラートを発行します。",
"xpack.uptime.alerts.monitorStatus.filterBar.ariaLabel": "監視状態アラートのフィルター基準を許可するインプット",
"xpack.uptime.alerts.monitorStatus.filters.anyLocation": "任意の場所",

View file

@ -27632,11 +27632,8 @@
"xpack.uptime.alerts.durationAnomaly.description": "运行时间监测持续时间异常时告警。",
"xpack.uptime.alerts.monitorExpression.label": "移除筛选 {title}",
"xpack.uptime.alerts.monitorStatus": "运行时间监测状态",
"xpack.uptime.alerts.monitorStatus.actionVariables.availabilityMessage": "低于阈值,{availabilityRatio}% 可用性应为 {expectedAvailability}%",
"xpack.uptime.alerts.monitorStatus.actionVariables.context.downMonitorsWithGeo.description": "生成的摘要,显示告警已检测为“关闭”的部分或所有监测",
"xpack.uptime.alerts.monitorStatus.actionVariables.context.message.description": "生成的消息,汇总当前关闭的监测",
"xpack.uptime.alerts.monitorStatus.actionVariables.down": "关闭",
"xpack.uptime.alerts.monitorStatus.actionVariables.downAndAvailabilityMessage": "{statusMessage} 以及 {availabilityMessage}",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted": "表示告警触发时当前触发状况开始的时间戳",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt": "表示此告警首次检查的时间戳",
"xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt": "表示告警首次触发的时间戳",
@ -27669,7 +27666,6 @@
"xpack.uptime.alerts.monitorStatus.availability.unit.headline": "选择时间范围单位",
"xpack.uptime.alerts.monitorStatus.availability.unit.selectable": "使用此选择来设置此告警的可用性范围单位",
"xpack.uptime.alerts.monitorStatus.clientName": "运行时间监测状态",
"xpack.uptime.alerts.monitorStatus.defaultActionMessage": "在 {observerLocation}URL 为 {monitorUrl} 的监测 {monitorName} 是 {statusMessage}。最新错误消息是 {latestErrorMessage}",
"xpack.uptime.alerts.monitorStatus.description": "监测关闭或超出可用性阈值时告警。",
"xpack.uptime.alerts.monitorStatus.filterBar.ariaLabel": "允许对监测状态告警使用筛选条件的输入",
"xpack.uptime.alerts.monitorStatus.filters.anyLocation": "任意位置",

View file

@ -6,7 +6,6 @@
*/
import * as t from 'io-ts';
export const StatusCheckFiltersType = t.type({
'monitor.type': t.array(t.string),
'observer.geo.name': t.array(t.string),

View file

@ -21,11 +21,11 @@ export const VALUE_MUST_BE_AN_INTEGER = i18n.translate('xpack.uptime.settings.in
export const MonitorStatusTranslations = {
defaultActionMessage: i18n.translate('xpack.uptime.alerts.monitorStatus.defaultActionMessage', {
defaultMessage:
'Monitor {monitorName} with url {monitorUrl} is {statusMessage} from {observerLocation}. The latest error message is {latestErrorMessage}',
'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage}. The latest error message is {latestErrorMessage}',
values: {
monitorName: '{{state.monitorName}}',
monitorUrl: '{{{state.monitorUrl}}}',
statusMessage: '{{state.statusMessage}}',
statusMessage: '{{{state.statusMessage}}}',
latestErrorMessage: '{{{state.latestErrorMessage}}}',
observerLocation: '{{state.observerLocation}}',
},

View file

@ -202,7 +202,7 @@ describe('monitor status alert type', () => {
})
).toMatchInlineSnapshot(`
Object {
"defaultActionMessage": "Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}",
"defaultActionMessage": "Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}",
"description": "Alert when a monitor is down or an availability threshold is breached.",
"documentationUrl": [Function],
"format": [Function],

View file

@ -50,7 +50,7 @@ describe('Alert Actions factory', () => {
eventAction: 'trigger',
severity: 'error',
summary:
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}',
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}',
},
id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9',
},
@ -75,7 +75,7 @@ describe('Alert Actions factory', () => {
eventAction: 'trigger',
severity: 'error',
summary:
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}',
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}',
},
},
]);
@ -93,7 +93,7 @@ describe('Alert Actions factory', () => {
eventAction: 'trigger',
severity: 'error',
summary:
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}',
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}',
},
id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9',
},
@ -118,7 +118,7 @@ describe('Alert Actions factory', () => {
eventAction: 'trigger',
severity: 'error',
summary:
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}',
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}',
},
},
]);

View file

@ -130,7 +130,7 @@ function getIndexActionParams(selectedMonitor: Ping, recovery = false): IndexAct
{
monitorName: '{{state.monitorName}}',
monitorUrl: '{{{state.monitorUrl}}}',
statusMessage: '{{state.statusMessage}}',
statusMessage: '{{{state.statusMessage}}}',
latestErrorMessage: '{{{state.latestErrorMessage}}}',
observerLocation: '{{state.observerLocation}}',
},

View file

@ -28,6 +28,7 @@ const mockMonitors = [
monitorInfo: {
...makePing({
id: 'first',
name: 'First',
location: 'harrisburg',
url: 'localhost:8080',
}),
@ -44,6 +45,7 @@ const mockMonitors = [
monitorInfo: {
...makePing({
id: 'first',
name: 'First',
location: 'fairbanks',
url: 'localhost:5601',
}),
@ -66,15 +68,16 @@ const mockCommonAlertDocumentFields = (monitorInfo: GetMonitorStatusResult['moni
const mockStatusAlertDocument = (
monitor: GetMonitorStatusResult,
isAutoGenerated: boolean = false
isAutoGenerated: boolean = false,
count: number,
interval: string,
numTimes: number
) => {
const { monitorInfo } = monitor;
return {
fields: {
...mockCommonAlertDocumentFields(monitor.monitorInfo),
[ALERT_REASON]: `Monitor first with url ${monitorInfo?.url?.full} is down from ${
monitorInfo.observer?.geo?.name
}. The latest error message is ${monitorInfo.error?.message || ''}`,
[ALERT_REASON]: `First from ${monitor.monitorInfo.observer?.geo?.name} failed ${count} times in the last ${interval}. Alert when > ${numTimes}.`,
},
id: getInstanceId(
monitorInfo,
@ -88,13 +91,11 @@ const mockAvailabilityAlertDocument = (monitor: GetMonitorAvailabilityResult) =>
return {
fields: {
...mockCommonAlertDocumentFields(monitor.monitorInfo),
[ALERT_REASON]: `Monitor ${monitorInfo.monitor.name || monitorInfo.monitor.id} with url ${
monitorInfo?.url?.full
} is below threshold with ${(monitor.availabilityRatio! * 100).toFixed(
2
)}% availability expected is 99.34% from ${
[ALERT_REASON]: `${monitorInfo.monitor.name || monitorInfo.monitor.id} from ${
monitorInfo.observer?.geo?.name
}. The latest error message is ${monitorInfo.error?.message || ''}`,
} 35 days availability is ${(monitor.availabilityRatio! * 100).toFixed(
2
)}%. Alert when < 99.34%.`,
},
id: getInstanceId(monitorInfo, `${monitorInfo?.monitor.id}-${monitorInfo.observer?.geo?.name}`),
};
@ -183,7 +184,12 @@ describe('status check alert', () => {
mockGetter.mockReturnValue(mockMonitors);
const { server, libs, plugins } = bootstrapDependencies({ getMonitorStatus: mockGetter });
const alert = statusCheckAlertFactory(server, libs, plugins);
const options = mockOptions();
const options = mockOptions({
numTimes: 5,
count: 234,
timerangeUnit: 'm',
timerangeCount: 15,
});
const {
services: { alertWithLifecycle },
} = options;
@ -192,7 +198,9 @@ describe('status check alert', () => {
expect(mockGetter).toHaveBeenCalledTimes(1);
expect(alertWithLifecycle).toHaveBeenCalledTimes(2);
mockMonitors.forEach((monitor) => {
expect(alertWithLifecycle).toBeCalledWith(mockStatusAlertDocument(monitor));
expect(alertWithLifecycle).toBeCalledWith(
mockStatusAlertDocument(monitor, false, 234, '15 mins', 5)
);
});
expect(mockGetter.mock.calls[0][0]).toEqual(
expect.objectContaining({
@ -219,13 +227,13 @@ describe('status check alert', () => {
"lastTriggeredAt": "foo date string",
"latestErrorMessage": "error message 1",
"monitorId": "first",
"monitorName": "first",
"monitorName": "First",
"monitorType": "myType",
"monitorUrl": "localhost:8080",
"observerHostname": undefined,
"observerLocation": "harrisburg",
"reason": "Monitor first with url localhost:8080 is down from harrisburg. The latest error message is error message 1",
"statusMessage": "down",
"reason": "First from harrisburg failed 234 times in the last 15 mins. Alert when > 5.",
"statusMessage": "failed 234 times in the last 15 mins. Alert when > 5.",
},
]
`);
@ -257,7 +265,9 @@ describe('status check alert', () => {
expect(mockGetter).toHaveBeenCalledTimes(1);
expect(alertWithLifecycle).toHaveBeenCalledTimes(2);
mockMonitors.forEach((monitor) => {
expect(alertWithLifecycle).toBeCalledWith(mockStatusAlertDocument(monitor, true));
expect(alertWithLifecycle).toBeCalledWith(
mockStatusAlertDocument(monitor, true, 234, '15m', 5)
);
});
expect(mockGetter.mock.calls[0][0]).toEqual(
expect.objectContaining({
@ -284,13 +294,13 @@ describe('status check alert', () => {
"lastTriggeredAt": "foo date string",
"latestErrorMessage": "error message 1",
"monitorId": "first",
"monitorName": "first",
"monitorName": "First",
"monitorType": "myType",
"monitorUrl": "localhost:8080",
"observerHostname": undefined,
"observerLocation": "harrisburg",
"reason": "Monitor first with url localhost:8080 is down from harrisburg. The latest error message is error message 1",
"statusMessage": "down",
"reason": "First from harrisburg failed 234 times in the last 15m. Alert when > 5.",
"statusMessage": "failed 234 times in the last 15m. Alert when > 5.",
},
]
`);
@ -314,7 +324,7 @@ describe('status check alert', () => {
const alert = statusCheckAlertFactory(server, libs, plugins);
const options = mockOptions({
numTimes: 4,
timespanRange: { from: 'now-14h', to: 'now' },
timerange: { from: 'now-14h', to: 'now' },
locations: ['fairbanks'],
filters: '',
});
@ -325,7 +335,9 @@ describe('status check alert', () => {
const [{ value: alertInstanceMock }] = alertWithLifecycle.mock.results;
mockMonitors.forEach((monitor) => {
expect(alertWithLifecycle).toBeCalledWith(mockStatusAlertDocument(monitor));
expect(alertWithLifecycle).toBeCalledWith(
mockStatusAlertDocument(monitor, false, 234, '14h', 4)
);
});
expect(alertInstanceMock.replaceState).toHaveBeenCalledTimes(2);
expect(alertInstanceMock.replaceState.mock.calls[0]).toMatchInlineSnapshot(`
@ -340,13 +352,13 @@ describe('status check alert', () => {
"lastTriggeredAt": "7.7 date",
"latestErrorMessage": "error message 1",
"monitorId": "first",
"monitorName": "first",
"monitorName": "First",
"monitorType": "myType",
"monitorUrl": "localhost:8080",
"observerHostname": undefined,
"observerLocation": "harrisburg",
"reason": "Monitor first with url localhost:8080 is down from harrisburg. The latest error message is error message 1",
"statusMessage": "down",
"reason": "First from harrisburg failed 234 times in the last 14h. Alert when > 4.",
"statusMessage": "failed 234 times in the last 14h. Alert when > 4.",
},
]
`);
@ -392,7 +404,9 @@ describe('status check alert', () => {
} = options;
const [{ value: alertInstanceMock }] = alertWithLifecycle.mock.results;
mockMonitors.forEach((monitor) => {
expect(alertWithLifecycle).toBeCalledWith(mockStatusAlertDocument(monitor));
expect(alertWithLifecycle).toBeCalledWith(
mockStatusAlertDocument(monitor, false, 234, '15 mins', 3)
);
});
expect(mockGetter).toHaveBeenCalledTimes(1);
expect(mockGetter.mock.calls[0][0]).toEqual(
@ -554,13 +568,13 @@ describe('status check alert', () => {
"lastTriggeredAt": "foo date string",
"latestErrorMessage": "error message 1",
"monitorId": "first",
"monitorName": "first",
"monitorName": "First",
"monitorType": "myType",
"monitorUrl": "localhost:8080",
"observerHostname": undefined,
"observerLocation": "harrisburg",
"reason": "Monitor first with url localhost:8080 is down from harrisburg. The latest error message is error message 1",
"statusMessage": "down",
"reason": "First from harrisburg failed 234 times in the last 15 mins. Alert when > 3.",
"statusMessage": "failed 234 times in the last 15 mins. Alert when > 3.",
},
]
`);
@ -752,8 +766,8 @@ describe('status check alert', () => {
"monitorUrl": "https://foo.com",
"observerHostname": undefined,
"observerLocation": "harrisburg",
"reason": "Monitor Foo with url https://foo.com is below threshold with 99.28% availability expected is 99.34% from harrisburg. The latest error message is ",
"statusMessage": "below threshold with 99.28% availability expected is 99.34%",
"reason": "Foo from harrisburg 35 days availability is 99.28%. Alert when < 99.34%.",
"statusMessage": "35 days availability is 99.28%. Alert when < 99.34%.",
},
]
`);
@ -1281,15 +1295,18 @@ describe('status check alert', () => {
describe('statusMessage', () => {
it('creates message for down item', () => {
expect(
getStatusMessage(
makePing({
getStatusMessage({
info: makePing({
id: 'test-node-service',
location: 'fairbanks',
name: 'Test Node Service',
url: 'http://localhost:12349',
})
)
).toMatchInlineSnapshot(`"down"`);
}),
count: 235,
numTimes: 10,
interval: '30 days',
})
).toMatchInlineSnapshot(`"failed 235 times in the last 30 days. Alert when > 10."`);
});
it('creates message for availability item', () => {
@ -1315,18 +1332,23 @@ describe('status check alert', () => {
rangeUnit: 'm',
}
)
).toMatchInlineSnapshot(`"below threshold with 58.04% availability expected is 90%"`);
).toMatchInlineSnapshot(`"5 mins availability is 58.04%. Alert when < 90%."`);
});
it('creates message for down and availability item', () => {
expect(
getStatusMessage(
makePing({
id: 'test-node-service',
location: 'fairbanks',
name: 'Test Node Service',
url: 'http://localhost:12349',
}),
{
info: makePing({
id: 'test-node-service',
location: 'fairbanks',
name: 'Test Node Service',
url: 'http://localhost:12349',
}),
count: 235,
numTimes: 10,
interval: '30 days',
},
{
monitorId: 'test-node-service',
location: 'harrisburg',
@ -1347,7 +1369,7 @@ describe('status check alert', () => {
}
)
).toMatchInlineSnapshot(
`"down and also below threshold with 58.04% availability expected is 90%"`
`"failed 235 times in the last 30 days. Alert when > 10. The 5 mins availability is 58.04%. Alert when < 90%."`
);
});
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { min } from 'lodash';
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
import datemath from '@elastic/datemath';
import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
@ -18,19 +20,27 @@ import {
GetMonitorAvailabilityParams,
} from '../../../common/runtime_types';
import { MONITOR_STATUS } from '../../../common/constants/alerts';
import { updateState, generateAlertMessage } from './common';
import { commonMonitorStateI18, commonStateTranslations, DOWN_LABEL } from './translations';
import { updateState } from './common';
import {
commonMonitorStateI18,
commonStateTranslations,
statusCheckTranslations,
} from './translations';
import { stringifyKueries, combineFiltersAndUserSearch } from '../../../common/lib';
import { GetMonitorAvailabilityResult } from '../requests/get_monitor_availability';
import { GetMonitorStatusResult } from '../requests/get_monitor_status';
import {
GetMonitorStatusResult,
GetMonitorDownStatusMessageParams,
getMonitorDownStatusMessageParams,
getInterval,
} from '../requests/get_monitor_status';
import { UNNAMED_LOCATION } from '../../../common/constants';
import { MonitorStatusTranslations } from '../../../common/translations';
import { getUptimeIndexPattern, IndexPatternTitleAndFields } from '../requests/get_index_pattern';
import { UMServerLibs, UptimeESClient, createUptimeESClient } from '../lib';
import { ActionGroupIdsOf } from '../../../../alerting/common';
momentDurationFormatSetup(moment);
export type ActionGroupIds = ActionGroupIdsOf<typeof MONITOR_STATUS>;
/**
* Returns the appropriate range for filtering the documents by `@timestamp`.
*
@ -131,6 +141,8 @@ export const formatFilterString = async (
);
export const getMonitorSummary = (monitorInfo: Ping, statusMessage: string) => {
const monitorName = monitorInfo.monitor?.name ?? monitorInfo.monitor?.id;
const observerLocation = monitorInfo.observer?.geo?.name ?? UNNAMED_LOCATION;
const summary = {
monitorUrl: monitorInfo.url?.full,
monitorId: monitorInfo.monitor?.id,
@ -140,13 +152,10 @@ export const getMonitorSummary = (monitorInfo: Ping, statusMessage: string) => {
observerLocation: monitorInfo.observer?.geo?.name ?? UNNAMED_LOCATION,
observerHostname: monitorInfo.agent?.name,
};
const reason = generateAlertMessage(MonitorStatusTranslations.defaultActionMessage, {
...summary,
statusMessage,
});
return {
...summary,
reason,
reason: `${monitorName} from ${observerLocation} ${statusMessage}`,
};
};
@ -162,40 +171,32 @@ export const getMonitorAlertDocument = (monitorSummary: Record<string, string |
});
export const getStatusMessage = (
downMonInfo?: Ping,
downMonParams?: GetMonitorDownStatusMessageParams,
availMonInfo?: GetMonitorAvailabilityResult,
availability?: GetMonitorAvailabilityParams
) => {
let statusMessage = '';
if (downMonInfo) {
statusMessage = DOWN_LABEL;
if (downMonParams?.info) {
statusMessage = `${statusCheckTranslations.downMonitorsLabel(
downMonParams.count!,
downMonParams.interval!,
downMonParams.numTimes
)}.`;
}
let availabilityMessage = '';
if (availMonInfo) {
availabilityMessage = i18n.translate(
'xpack.uptime.alerts.monitorStatus.actionVariables.availabilityMessage',
{
defaultMessage:
'below threshold with {availabilityRatio}% availability expected is {expectedAvailability}%',
values: {
availabilityRatio: (availMonInfo.availabilityRatio! * 100).toFixed(2),
expectedAvailability: availability?.threshold,
},
}
);
availabilityMessage = `${statusCheckTranslations.availabilityBreachLabel(
(availMonInfo.availabilityRatio! * 100).toFixed(2),
availability?.threshold!,
getInterval(availability?.range!, availability?.rangeUnit!)
)}.`;
}
if (availMonInfo && downMonInfo) {
return i18n.translate(
'xpack.uptime.alerts.monitorStatus.actionVariables.downAndAvailabilityMessage',
{
defaultMessage: '{statusMessage} and also {availabilityMessage}',
values: {
statusMessage,
availabilityMessage,
},
}
);
if (availMonInfo && downMonParams?.info) {
return `${statusCheckTranslations.downMonitorsAndAvailabilityBreachLabel(
statusMessage,
availabilityMessage
)}`;
}
return statusMessage + availabilityMessage;
};
@ -314,6 +315,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
isAutoGenerated,
timerange: oldVersionTimeRange,
} = rawParams;
const uptimeEsClient = createUptimeESClient({
esClient: scopedClusterClient.asCurrentUser,
savedObjectsClient,
@ -322,7 +324,6 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
const filterString = await formatFilterString(uptimeEsClient, filters, search, libs);
const timespanInterval = `${String(timerangeCount)}${timerangeUnit}`;
// Range filter for `monitor.timespan`, the range of time the ping is valid
const timespanRange = oldVersionTimeRange || {
from: `now-${timespanInterval}`,
@ -354,9 +355,17 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
for (const monitorLoc of downMonitorsByLocation) {
const monitorInfo = monitorLoc.monitorInfo;
const statusMessage = getStatusMessage(monitorInfo);
const monitorSummary = getMonitorSummary(monitorInfo, statusMessage);
const monitorStatusMessageParams = getMonitorDownStatusMessageParams(
monitorInfo,
monitorLoc.count,
numTimes,
timerangeCount,
timerangeUnit,
oldVersionTimeRange
);
const statusMessage = getStatusMessage(monitorStatusMessageParams);
const monitorSummary = getMonitorSummary(monitorInfo, statusMessage);
const alert = alertWithLifecycle({
id: getInstanceId(monitorInfo, monitorLoc.location),
fields: getMonitorAlertDocument(monitorSummary),
@ -394,11 +403,27 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
({ monitorId, location }) => getMonIdByLoc(monitorId, location) === monIdByLoc
)?.monitorInfo;
const downMonCount = downMonitorsByLocation.find(
({ monitorId, location }) => getMonIdByLoc(monitorId, location) === monIdByLoc
)?.count;
const monitorInfo = downMonInfo || availMonInfo?.monitorInfo!;
const statusMessage = getStatusMessage(downMonInfo!, availMonInfo!, availability);
const monitorSummary = getMonitorSummary(monitorInfo, statusMessage);
const monitorStatusMessageParams = getMonitorDownStatusMessageParams(
downMonInfo!,
downMonCount!,
numTimes,
timerangeCount,
timerangeUnit,
oldVersionTimeRange
);
const statusMessage = getStatusMessage(
monitorStatusMessageParams,
availMonInfo!,
availability
);
const monitorSummary = getMonitorSummary(monitorInfo, statusMessage);
const alert = alertWithLifecycle({
id: getInstanceId(monitorInfo, monIdByLoc),
fields: getMonitorAlertDocument(monitorSummary),

View file

@ -328,6 +328,39 @@ export const durationAnomalyTranslations = {
],
};
export const DOWN_LABEL = i18n.translate('xpack.uptime.alerts.monitorStatus.actionVariables.down', {
defaultMessage: 'down',
});
export const statusCheckTranslations = {
downMonitorsLabel: (count: number, interval: string, numTimes: number) =>
i18n.translate('xpack.uptime.alerts.monitorStatus.actionVariables.down', {
defaultMessage: `failed {count} times in the last {interval}. Alert when > {numTimes}`,
values: {
count,
interval,
numTimes,
},
}),
availabilityBreachLabel: (
availabilityRatio: string,
expectedAvailability: string,
interval: string
) =>
i18n.translate('xpack.uptime.alerts.monitorStatus.actionVariables.availabilityMessage', {
defaultMessage:
'{interval} availability is {availabilityRatio}%. Alert when < {expectedAvailability}%',
values: {
availabilityRatio,
expectedAvailability,
interval,
},
}),
downMonitorsAndAvailabilityBreachLabel: (
downMonitorsMessage: string,
availabilityBreachMessage: string
) =>
i18n.translate('xpack.uptime.alerts.monitorStatus.actionVariables.downAndAvailabilityMessage', {
defaultMessage: '{downMonitorsMessage} The {availabilityBreachMessage}',
values: {
downMonitorsMessage,
availabilityBreachMessage,
},
}),
};

View file

@ -8,12 +8,15 @@
import { JsonObject } from '@kbn/utility-types';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { PromiseType } from 'utility-types';
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
import { asMutableArray } from '../../../common/utils/as_mutable_array';
import { UMElasticsearchQueryFn } from '../adapters';
import { Ping } from '../../../common/runtime_types/ping';
import { createEsQuery } from '../../../common/utils/es_search';
import { UptimeESClient } from '../lib';
import { UNNAMED_LOCATION } from '../../../common/constants';
momentDurationFormatSetup(moment);
export interface GetMonitorStatusParams {
filters?: JsonObject;
@ -31,6 +34,46 @@ export interface GetMonitorStatusResult {
monitorInfo: Ping;
}
export const getInterval = (timerangeCount: number, timerangeUnit: string): string => {
switch (timerangeUnit) {
case 's':
return moment.duration(timerangeCount, 'seconds').format('s [sec]');
case 'm':
return moment.duration(timerangeCount, 'minutes').format('m [min]');
case 'h':
return moment.duration(timerangeCount, 'hours').format('h [hr]');
case 'd':
return moment.duration(timerangeCount, 'days').format('d [day]');
default:
return `${timerangeCount} ${timerangeUnit}`;
}
};
export interface GetMonitorDownStatusMessageParams {
info: Ping;
count: number;
interval?: string;
numTimes: number;
}
export const getMonitorDownStatusMessageParams = (
info: Ping,
count: number,
numTimes: number,
timerangeCount: number,
timerangeUnit: string,
oldVersionTimeRange: { from: string; to: string }
) => {
return {
info,
count,
interval: oldVersionTimeRange
? oldVersionTimeRange.from.slice(-3)
: getInterval(timerangeCount, timerangeUnit),
numTimes,
};
};
const getLocationClause = (locations: string[]) => ({
bool: {
should: [

View file

@ -107,7 +107,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
group: 'xpack.uptime.alerts.actionGroups.monitorStatus',
params: {
message:
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} is {{state.statusMessage}} from {{state.observerLocation}}. The latest error message is {{{state.latestErrorMessage}}}',
'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}}. The latest error message is {{{state.latestErrorMessage}}}',
},
id: 'my-slack1',
},