[Metrics UI] Use Notify Every in Alert Preview (#74401)
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
060f0895cc
commit
9cf546f3d0
|
@ -52,6 +52,8 @@ const baseAlertRequestParamsRT = rt.intersection([
|
|||
]),
|
||||
criteria: rt.array(rt.any),
|
||||
alertInterval: rt.string,
|
||||
alertThrottle: rt.string,
|
||||
alertOnNoData: rt.boolean,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
@ -91,6 +93,7 @@ export const alertPreviewSuccessResponsePayloadRT = rt.type({
|
|||
fired: rt.number,
|
||||
noData: rt.number,
|
||||
error: rt.number,
|
||||
notifications: rt.number,
|
||||
}),
|
||||
});
|
||||
export type AlertPreviewSuccessResponsePayload = rt.TypeOf<
|
||||
|
|
|
@ -33,6 +33,7 @@ import { getAlertPreview, PreviewableAlertTypes } from './get_alert_preview';
|
|||
|
||||
interface Props {
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
alertType: PreviewableAlertTypes;
|
||||
fetch: HttpSetup['fetch'];
|
||||
alertParams: { criteria: any[]; sourceId: string } & Record<string, any>;
|
||||
|
@ -45,6 +46,7 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
const {
|
||||
alertParams,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
fetch,
|
||||
alertType,
|
||||
validate,
|
||||
|
@ -73,16 +75,27 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
...alertParams,
|
||||
lookback: previewLookbackInterval as 'h' | 'd' | 'w' | 'M',
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData: showNoDataResults ?? false,
|
||||
} as AlertPreviewRequestParams,
|
||||
alertType,
|
||||
});
|
||||
setPreviewResult({ ...result, groupByDisplayName, previewLookbackInterval });
|
||||
setPreviewResult({ ...result, groupByDisplayName, previewLookbackInterval, alertThrottle });
|
||||
} catch (e) {
|
||||
setPreviewError(e);
|
||||
} finally {
|
||||
setIsPreviewLoading(false);
|
||||
}
|
||||
}, [alertParams, alertInterval, fetch, alertType, groupByDisplayName, previewLookbackInterval]);
|
||||
}, [
|
||||
alertParams,
|
||||
alertInterval,
|
||||
fetch,
|
||||
alertType,
|
||||
groupByDisplayName,
|
||||
previewLookbackInterval,
|
||||
alertThrottle,
|
||||
showNoDataResults,
|
||||
]);
|
||||
|
||||
const previewIntervalError = useMemo(() => {
|
||||
const intervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||
|
@ -101,6 +114,13 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
return hasValidationErrors || previewIntervalError;
|
||||
}, [alertParams.criteria, previewIntervalError, validate]);
|
||||
|
||||
const showNumberOfNotifications = useMemo(() => {
|
||||
if (!previewResult) return false;
|
||||
const { notifications, fired, noData, error } = previewResult.resultTotals;
|
||||
const unthrottledNotifications = fired + (showNoDataResults ? noData + error : 0);
|
||||
return unthrottledNotifications > notifications;
|
||||
}, [previewResult, showNoDataResults]);
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.infra.metrics.alertFlyout.previewLabel', {
|
||||
|
@ -136,19 +156,22 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
<>
|
||||
<EuiSpacer size={'s'} />
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
size="s"
|
||||
title={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.alertPreviewResult"
|
||||
defaultMessage="This alert would have occurred {firedTimes}"
|
||||
defaultMessage="There were {firedTimes}"
|
||||
values={{
|
||||
firedTimes: (
|
||||
<strong>
|
||||
{previewResult.resultTotals.fired}{' '}
|
||||
{previewResult.resultTotals.fired === 1
|
||||
? firedTimeLabel
|
||||
: firedTimesLabel}
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.firedTimes"
|
||||
defaultMessage="{fired, plural, one {# instance} other {# instances}}"
|
||||
values={{
|
||||
fired: previewResult.resultTotals.fired,
|
||||
}}
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
|
@ -173,7 +196,7 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
) : null}
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.alertPreviewResultLookback"
|
||||
defaultMessage="in the last {lookback}."
|
||||
defaultMessage="that satisfied the conditions of this alert in the last {lookback}."
|
||||
values={{
|
||||
lookback: previewOptions.find(
|
||||
(e) => e.value === previewResult.previewLookbackInterval
|
||||
|
@ -211,6 +234,32 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
defaultMessage="An error occurred when trying to evaluate some of the data."
|
||||
/>
|
||||
) : null}
|
||||
{showNumberOfNotifications ? (
|
||||
<>
|
||||
<EuiSpacer size={'s'} />
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.alertPreviewTotalNotifications"
|
||||
defaultMessage='As a result, this alert would have sent {notifications} based on the selected "notify every" setting of "{alertThrottle}."'
|
||||
values={{
|
||||
alertThrottle: previewResult.alertThrottle,
|
||||
notifications: (
|
||||
<strong>
|
||||
{i18n.translate(
|
||||
'xpack.infra.metrics.alertFlyout.alertPreviewTotalNotificationsNumber',
|
||||
{
|
||||
defaultMessage:
|
||||
'{notifs, plural, one {# notification} other {# notifications}}',
|
||||
values: {
|
||||
notifs: previewResult.resultTotals.notifications,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}{' '}
|
||||
</EuiCallOut>
|
||||
</>
|
||||
)}
|
||||
|
@ -218,6 +267,7 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
<>
|
||||
<EuiSpacer size={'s'} />
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.previewIntervalTooShortTitle"
|
||||
|
@ -242,6 +292,7 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
<EuiSpacer size={'s'} />
|
||||
{previewError.body?.statusCode === 508 ? (
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.tooManyBucketsErrorTitle"
|
||||
|
@ -264,6 +315,7 @@ export const AlertPreview: React.FC<Props> = (props) => {
|
|||
</EuiCallOut>
|
||||
) : (
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.alertFlyout.alertPreviewError"
|
||||
|
@ -349,10 +401,3 @@ const previewOptions = [
|
|||
const previewDOMOptions: Array<{ text: string; value: string }> = previewOptions.map((o) =>
|
||||
omit(o, 'shortText')
|
||||
);
|
||||
|
||||
const firedTimeLabel = i18n.translate('xpack.infra.metrics.alertFlyout.firedTime', {
|
||||
defaultMessage: 'time',
|
||||
});
|
||||
const firedTimesLabel = i18n.translate('xpack.infra.metrics.alertFlyout.firedTimes', {
|
||||
defaultMessage: 'times',
|
||||
});
|
||||
|
|
|
@ -45,10 +45,3 @@ export const previewOptions = [
|
|||
}),
|
||||
},
|
||||
];
|
||||
|
||||
export const firedTimeLabel = i18n.translate('xpack.infra.metrics.alertFlyout.firedTime', {
|
||||
defaultMessage: 'time',
|
||||
});
|
||||
export const firedTimesLabel = i18n.translate('xpack.infra.metrics.alertFlyout.firedTimes', {
|
||||
defaultMessage: 'times',
|
||||
});
|
||||
|
|
|
@ -69,6 +69,7 @@ describe('Expression', () => {
|
|||
<Expressions
|
||||
alertsContext={context}
|
||||
alertInterval="1m"
|
||||
alertThrottle="1m"
|
||||
alertParams={alertParams as any}
|
||||
errors={[]}
|
||||
setAlertParams={(key, value) => Reflect.set(alertParams, key, value)}
|
||||
|
|
|
@ -89,6 +89,7 @@ interface Props {
|
|||
alertOnNoData?: boolean;
|
||||
};
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
alertsContext: AlertsContextValue<AlertContextMeta>;
|
||||
setAlertParams(key: string, value: any): void;
|
||||
setAlertProperty(key: string, value: any): void;
|
||||
|
@ -104,7 +105,14 @@ const defaultExpression = {
|
|||
} as InventoryMetricConditions;
|
||||
|
||||
export const Expressions: React.FC<Props> = (props) => {
|
||||
const { setAlertParams, alertParams, errors, alertsContext, alertInterval } = props;
|
||||
const {
|
||||
setAlertParams,
|
||||
alertParams,
|
||||
errors,
|
||||
alertsContext,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
} = props;
|
||||
const { source, createDerivedIndexPattern } = useSourceViaHttp({
|
||||
sourceId: 'default',
|
||||
type: 'metrics',
|
||||
|
@ -378,6 +386,7 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
<EuiSpacer size={'m'} />
|
||||
<AlertPreview
|
||||
alertInterval={alertInterval}
|
||||
alertThrottle={alertThrottle}
|
||||
alertType={METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID}
|
||||
alertParams={pick(alertParams, 'criteria', 'nodeType', 'sourceId', 'filterQuery')}
|
||||
validate={validateMetricThreshold}
|
||||
|
|
|
@ -68,6 +68,7 @@ describe('Expression', () => {
|
|||
<Expressions
|
||||
alertsContext={context}
|
||||
alertInterval="1m"
|
||||
alertThrottle="1m"
|
||||
alertParams={alertParams}
|
||||
errors={[]}
|
||||
setAlertParams={(key, value) => Reflect.set(alertParams, key, value)}
|
||||
|
|
|
@ -51,6 +51,7 @@ interface Props {
|
|||
alertParams: AlertParams;
|
||||
alertsContext: AlertsContextValue<AlertContextMeta>;
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
setAlertParams(key: string, value: any): void;
|
||||
setAlertProperty(key: string, value: any): void;
|
||||
}
|
||||
|
@ -65,7 +66,14 @@ const defaultExpression = {
|
|||
export { defaultExpression };
|
||||
|
||||
export const Expressions: React.FC<Props> = (props) => {
|
||||
const { setAlertParams, alertParams, errors, alertsContext, alertInterval } = props;
|
||||
const {
|
||||
setAlertParams,
|
||||
alertParams,
|
||||
errors,
|
||||
alertsContext,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
} = props;
|
||||
const { source, createDerivedIndexPattern } = useSourceViaHttp({
|
||||
sourceId: 'default',
|
||||
type: 'metrics',
|
||||
|
@ -399,6 +407,7 @@ export const Expressions: React.FC<Props> = (props) => {
|
|||
<EuiSpacer size={'m'} />
|
||||
<AlertPreview
|
||||
alertInterval={alertInterval}
|
||||
alertThrottle={alertThrottle}
|
||||
alertType={METRIC_THRESHOLD_ALERT_TYPE_ID}
|
||||
alertParams={pick(alertParams, 'criteria', 'groupBy', 'filterQuery', 'sourceId')}
|
||||
showNoDataResults={alertParams.alertOnNoData}
|
||||
|
|
|
@ -29,6 +29,8 @@ interface PreviewInventoryMetricThresholdAlertParams {
|
|||
source: InfraSource;
|
||||
lookback: Unit;
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
alertOnNoData: boolean;
|
||||
}
|
||||
|
||||
export const previewInventoryMetricThresholdAlert = async ({
|
||||
|
@ -37,6 +39,8 @@ export const previewInventoryMetricThresholdAlert = async ({
|
|||
source,
|
||||
lookback,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
}: PreviewInventoryMetricThresholdAlertParams) => {
|
||||
const { criteria, filterQuery, nodeType } = params as InventoryMetricThresholdParams;
|
||||
|
||||
|
@ -52,6 +56,10 @@ export const previewInventoryMetricThresholdAlert = async ({
|
|||
|
||||
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||
const alertResultsPerExecution = alertIntervalInSeconds / bucketIntervalInSeconds;
|
||||
const throttleIntervalInSeconds = getIntervalInSeconds(alertThrottle);
|
||||
const executionsPerThrottle = Math.floor(
|
||||
(throttleIntervalInSeconds / alertIntervalInSeconds) * alertResultsPerExecution
|
||||
);
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
criteria.map((c) =>
|
||||
|
@ -66,6 +74,12 @@ export const previewInventoryMetricThresholdAlert = async ({
|
|||
let numberOfTimesFired = 0;
|
||||
let numberOfNoDataResults = 0;
|
||||
let numberOfErrors = 0;
|
||||
let numberOfNotifications = 0;
|
||||
let throttleTracker = 0;
|
||||
const notifyWithThrottle = () => {
|
||||
if (throttleTracker === 0) numberOfNotifications++;
|
||||
throttleTracker++;
|
||||
};
|
||||
for (let i = 0; i < numberOfExecutionBuckets; i++) {
|
||||
const mappedBucketIndex = Math.floor(i * alertResultsPerExecution);
|
||||
const allConditionsFiredInMappedBucket = results.every((result) => {
|
||||
|
@ -79,11 +93,27 @@ export const previewInventoryMetricThresholdAlert = async ({
|
|||
const someConditionsErrorInMappedBucket = results.some((result) => {
|
||||
return result[item].isError;
|
||||
});
|
||||
if (allConditionsFiredInMappedBucket) numberOfTimesFired++;
|
||||
if (someConditionsNoDataInMappedBucket) numberOfNoDataResults++;
|
||||
if (someConditionsErrorInMappedBucket) numberOfErrors++;
|
||||
if (someConditionsErrorInMappedBucket) {
|
||||
numberOfErrors++;
|
||||
if (alertOnNoData) {
|
||||
notifyWithThrottle();
|
||||
}
|
||||
return [numberOfTimesFired, numberOfNoDataResults, numberOfErrors];
|
||||
} else if (someConditionsNoDataInMappedBucket) {
|
||||
numberOfNoDataResults++;
|
||||
if (alertOnNoData) {
|
||||
notifyWithThrottle();
|
||||
}
|
||||
} else if (allConditionsFiredInMappedBucket) {
|
||||
numberOfTimesFired++;
|
||||
notifyWithThrottle();
|
||||
} else if (throttleTracker > 0) {
|
||||
throttleTracker++;
|
||||
}
|
||||
if (throttleTracker === executionsPerThrottle) {
|
||||
throttleTracker = 0;
|
||||
}
|
||||
}
|
||||
return [numberOfTimesFired, numberOfNoDataResults, numberOfErrors, numberOfNotifications];
|
||||
});
|
||||
|
||||
return previewResults;
|
||||
|
|
|
@ -16,11 +16,14 @@ describe('Previewing the metric threshold alert type', () => {
|
|||
...baseParams,
|
||||
lookback: 'h',
|
||||
alertInterval: '1m',
|
||||
alertThrottle: '1m',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResults, noDataResults, errorResults] = ungroupedResult;
|
||||
const [firedResults, noDataResults, errorResults, notifications] = ungroupedResult;
|
||||
expect(firedResults).toBe(30);
|
||||
expect(noDataResults).toBe(0);
|
||||
expect(errorResults).toBe(0);
|
||||
expect(notifications).toBe(30);
|
||||
});
|
||||
|
||||
test('returns the expected results using a bucket interval shorter than the alert interval', async () => {
|
||||
|
@ -28,22 +31,42 @@ describe('Previewing the metric threshold alert type', () => {
|
|||
...baseParams,
|
||||
lookback: 'h',
|
||||
alertInterval: '3m',
|
||||
alertThrottle: '3m',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResults, noDataResults, errorResults] = ungroupedResult;
|
||||
const [firedResults, noDataResults, errorResults, notifications] = ungroupedResult;
|
||||
expect(firedResults).toBe(10);
|
||||
expect(noDataResults).toBe(0);
|
||||
expect(errorResults).toBe(0);
|
||||
expect(notifications).toBe(10);
|
||||
});
|
||||
test('returns the expected results using a bucket interval longer than the alert interval', async () => {
|
||||
const [ungroupedResult] = await previewMetricThresholdAlert({
|
||||
...baseParams,
|
||||
lookback: 'h',
|
||||
alertInterval: '30s',
|
||||
alertThrottle: '30s',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResults, noDataResults, errorResults] = ungroupedResult;
|
||||
const [firedResults, noDataResults, errorResults, notifications] = ungroupedResult;
|
||||
expect(firedResults).toBe(60);
|
||||
expect(noDataResults).toBe(0);
|
||||
expect(errorResults).toBe(0);
|
||||
expect(notifications).toBe(60);
|
||||
});
|
||||
test('returns the expected results using a throttle interval longer than the alert interval', async () => {
|
||||
const [ungroupedResult] = await previewMetricThresholdAlert({
|
||||
...baseParams,
|
||||
lookback: 'h',
|
||||
alertInterval: '1m',
|
||||
alertThrottle: '3m',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResults, noDataResults, errorResults, notifications] = ungroupedResult;
|
||||
expect(firedResults).toBe(30);
|
||||
expect(noDataResults).toBe(0);
|
||||
expect(errorResults).toBe(0);
|
||||
expect(notifications).toBe(15);
|
||||
});
|
||||
});
|
||||
describe('querying with a groupBy parameter', () => {
|
||||
|
@ -56,15 +79,19 @@ describe('Previewing the metric threshold alert type', () => {
|
|||
},
|
||||
lookback: 'h',
|
||||
alertInterval: '1m',
|
||||
alertThrottle: '1m',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResultsA, noDataResultsA, errorResultsA] = resultA;
|
||||
const [firedResultsA, noDataResultsA, errorResultsA, notificationsA] = resultA;
|
||||
expect(firedResultsA).toBe(30);
|
||||
expect(noDataResultsA).toBe(0);
|
||||
expect(errorResultsA).toBe(0);
|
||||
const [firedResultsB, noDataResultsB, errorResultsB] = resultB;
|
||||
expect(notificationsA).toBe(30);
|
||||
const [firedResultsB, noDataResultsB, errorResultsB, notificationsB] = resultB;
|
||||
expect(firedResultsB).toBe(60);
|
||||
expect(noDataResultsB).toBe(0);
|
||||
expect(errorResultsB).toBe(0);
|
||||
expect(notificationsB).toBe(60);
|
||||
});
|
||||
});
|
||||
describe('querying a data set with a period of No Data', () => {
|
||||
|
@ -82,11 +109,14 @@ describe('Previewing the metric threshold alert type', () => {
|
|||
},
|
||||
lookback: 'h',
|
||||
alertInterval: '1m',
|
||||
alertThrottle: '1m',
|
||||
alertOnNoData: true,
|
||||
});
|
||||
const [firedResults, noDataResults, errorResults] = ungroupedResult;
|
||||
const [firedResults, noDataResults, errorResults, notifications] = ungroupedResult;
|
||||
expect(firedResults).toBe(25);
|
||||
expect(noDataResults).toBe(10);
|
||||
expect(errorResults).toBe(0);
|
||||
expect(notifications).toBe(35);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,6 +28,8 @@ interface PreviewMetricThresholdAlertParams {
|
|||
config: InfraSource['configuration'];
|
||||
lookback: Unit;
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
alertOnNoData: boolean;
|
||||
end?: number;
|
||||
overrideLookbackIntervalInSeconds?: number;
|
||||
}
|
||||
|
@ -43,6 +45,8 @@ export const previewMetricThresholdAlert: (
|
|||
config,
|
||||
lookback,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
end = Date.now(),
|
||||
overrideLookbackIntervalInSeconds,
|
||||
},
|
||||
|
@ -77,6 +81,11 @@ export const previewMetricThresholdAlert: (
|
|||
// Now determine how to interpolate this histogram based on the alert interval
|
||||
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||
const alertResultsPerExecution = alertIntervalInSeconds / bucketIntervalInSeconds;
|
||||
const throttleIntervalInSeconds = Math.max(
|
||||
getIntervalInSeconds(alertThrottle),
|
||||
alertIntervalInSeconds
|
||||
);
|
||||
|
||||
const previewResults = await Promise.all(
|
||||
groups.map(async (group) => {
|
||||
// Interpolate the buckets returned by evaluateAlert and return a count of how many of these
|
||||
|
@ -90,6 +99,12 @@ export const previewMetricThresholdAlert: (
|
|||
let numberOfTimesFired = 0;
|
||||
let numberOfNoDataResults = 0;
|
||||
let numberOfErrors = 0;
|
||||
let numberOfNotifications = 0;
|
||||
let throttleTracker = 0;
|
||||
const notifyWithThrottle = () => {
|
||||
if (throttleTracker === 0) numberOfNotifications++;
|
||||
throttleTracker += alertIntervalInSeconds;
|
||||
};
|
||||
for (let i = 0; i < numberOfExecutionBuckets; i++) {
|
||||
const mappedBucketIndex = Math.floor(i * alertResultsPerExecution);
|
||||
const allConditionsFiredInMappedBucket = alertResults.every(
|
||||
|
@ -102,11 +117,27 @@ export const previewMetricThresholdAlert: (
|
|||
const someConditionsErrorInMappedBucket = alertResults.some((alertResult) => {
|
||||
return alertResult[group].isError;
|
||||
});
|
||||
if (allConditionsFiredInMappedBucket) numberOfTimesFired++;
|
||||
if (someConditionsNoDataInMappedBucket) numberOfNoDataResults++;
|
||||
if (someConditionsErrorInMappedBucket) numberOfErrors++;
|
||||
if (someConditionsErrorInMappedBucket) {
|
||||
numberOfErrors++;
|
||||
if (alertOnNoData) {
|
||||
notifyWithThrottle();
|
||||
}
|
||||
return [numberOfTimesFired, numberOfNoDataResults, numberOfErrors];
|
||||
} else if (someConditionsNoDataInMappedBucket) {
|
||||
numberOfNoDataResults++;
|
||||
if (alertOnNoData) {
|
||||
notifyWithThrottle();
|
||||
}
|
||||
} else if (allConditionsFiredInMappedBucket) {
|
||||
numberOfTimesFired++;
|
||||
notifyWithThrottle();
|
||||
} else if (throttleTracker > 0) {
|
||||
throttleTracker += alertIntervalInSeconds;
|
||||
}
|
||||
if (throttleTracker >= throttleIntervalInSeconds) {
|
||||
throttleTracker = 0;
|
||||
}
|
||||
}
|
||||
return [numberOfTimesFired, numberOfNoDataResults, numberOfErrors, numberOfNotifications];
|
||||
})
|
||||
);
|
||||
return previewResults;
|
||||
|
@ -114,7 +145,15 @@ export const previewMetricThresholdAlert: (
|
|||
if (isTooManyBucketsPreviewException(e)) {
|
||||
// If there's too much data on the first request, recursively slice the lookback interval
|
||||
// until all the data can be retrieved
|
||||
const basePreviewParams = { callCluster, params, config, lookback, alertInterval };
|
||||
const basePreviewParams = {
|
||||
callCluster,
|
||||
params,
|
||||
config,
|
||||
lookback,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
};
|
||||
const { maxBuckets } = e;
|
||||
// If this is still the first iteration, try to get the number of groups in order to
|
||||
// calculate max buckets. If this fails, just estimate based on 1 group
|
||||
|
@ -159,7 +198,7 @@ export const previewMetricThresholdAlert: (
|
|||
.reduce((a, b) => {
|
||||
if (!a) return b;
|
||||
if (!b) return a;
|
||||
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
||||
return [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]];
|
||||
})
|
||||
);
|
||||
return zippedResult;
|
||||
|
|
|
@ -30,7 +30,16 @@ export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs)
|
|||
},
|
||||
},
|
||||
framework.router.handleLegacyErrors(async (requestContext, request, response) => {
|
||||
const { criteria, filterQuery, lookback, sourceId, alertType, alertInterval } = request.body;
|
||||
const {
|
||||
criteria,
|
||||
filterQuery,
|
||||
lookback,
|
||||
sourceId,
|
||||
alertType,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
} = request.body;
|
||||
|
||||
const callCluster = (endpoint: string, opts: Record<string, any>) => {
|
||||
return callWithRequest(requestContext, endpoint, opts);
|
||||
|
@ -51,22 +60,26 @@ export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs)
|
|||
lookback,
|
||||
config: source.configuration,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
});
|
||||
|
||||
const numberOfGroups = previewResult.length;
|
||||
const resultTotals = previewResult.reduce(
|
||||
(totals, [firedResult, noDataResult, errorResult]) => {
|
||||
(totals, [firedResult, noDataResult, errorResult, notifications]) => {
|
||||
return {
|
||||
...totals,
|
||||
fired: totals.fired + firedResult,
|
||||
noData: totals.noData + noDataResult,
|
||||
error: totals.error + errorResult,
|
||||
notifications: totals.notifications + notifications,
|
||||
};
|
||||
},
|
||||
{
|
||||
fired: 0,
|
||||
noData: 0,
|
||||
error: 0,
|
||||
notifications: 0,
|
||||
}
|
||||
);
|
||||
return response.ok({
|
||||
|
@ -84,22 +97,26 @@ export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs)
|
|||
lookback,
|
||||
source,
|
||||
alertInterval,
|
||||
alertThrottle,
|
||||
alertOnNoData,
|
||||
});
|
||||
|
||||
const numberOfGroups = previewResult.length;
|
||||
const resultTotals = previewResult.reduce(
|
||||
(totals, [firedResult, noDataResult, errorResult]) => {
|
||||
(totals, [firedResult, noDataResult, errorResult, notifications]) => {
|
||||
return {
|
||||
...totals,
|
||||
fired: totals.fired + firedResult,
|
||||
noData: totals.noData + noDataResult,
|
||||
error: totals.error + errorResult,
|
||||
notifications: totals.notifications + notifications,
|
||||
};
|
||||
},
|
||||
{
|
||||
fired: 0,
|
||||
noData: 0,
|
||||
error: 0,
|
||||
notifications: 0,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -8677,8 +8677,7 @@
|
|||
"xpack.infra.metrics.alertFlyout.expression.metric.whenLabel": "タイミング",
|
||||
"xpack.infra.metrics.alertFlyout.filterHelpText": "KQL式を使用して、アラートトリガーの範囲を制限します。",
|
||||
"xpack.infra.metrics.alertFlyout.filterLabel": "フィルター(任意)",
|
||||
"xpack.infra.metrics.alertFlyout.firedTime": "時間",
|
||||
"xpack.infra.metrics.alertFlyout.firedTimes": "回数",
|
||||
"xpack.infra.metrics.alertFlyout.firedTimes": "{fired, plural, one {# 時間} other {# 回数}}",
|
||||
"xpack.infra.metrics.alertFlyout.hourLabel": "時間",
|
||||
"xpack.infra.metrics.alertFlyout.lastDayLabel": "昨日",
|
||||
"xpack.infra.metrics.alertFlyout.lastHourLabel": "過去1時間",
|
||||
|
|
|
@ -8683,8 +8683,7 @@
|
|||
"xpack.infra.metrics.alertFlyout.expression.metric.whenLabel": "当",
|
||||
"xpack.infra.metrics.alertFlyout.filterHelpText": "使用 KQL 表达式限制告警触发器的范围。",
|
||||
"xpack.infra.metrics.alertFlyout.filterLabel": "筛选(可选)",
|
||||
"xpack.infra.metrics.alertFlyout.firedTime": "次",
|
||||
"xpack.infra.metrics.alertFlyout.firedTimes": "次",
|
||||
"xpack.infra.metrics.alertFlyout.firedTimes": "{fired, plural, one {# 次} other {# 次}}",
|
||||
"xpack.infra.metrics.alertFlyout.hourLabel": "小时",
|
||||
"xpack.infra.metrics.alertFlyout.lastDayLabel": "昨天",
|
||||
"xpack.infra.metrics.alertFlyout.lastHourLabel": "上一小时",
|
||||
|
|
|
@ -248,6 +248,7 @@ export const AlertForm = ({
|
|||
<AlertParamsExpressionComponent
|
||||
alertParams={alert.params}
|
||||
alertInterval={`${alertInterval ?? 1}${alertIntervalUnit}`}
|
||||
alertThrottle={`${alertThrottle ?? 1}${alertThrottleUnit}`}
|
||||
errors={errors}
|
||||
setAlertParams={setAlertParams}
|
||||
setAlertProperty={setAlertProperty}
|
||||
|
|
|
@ -134,6 +134,7 @@ export interface AlertTypeParamsExpressionProps<
|
|||
> {
|
||||
alertParams: AlertParamsType;
|
||||
alertInterval: string;
|
||||
alertThrottle: string;
|
||||
setAlertParams: (property: string, value: any) => void;
|
||||
setAlertProperty: (key: string, value: any) => void;
|
||||
errors: IErrorObject;
|
||||
|
|
Loading…
Reference in a new issue