mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Log threhsod, SLO burn rate] Save the ECS group by fields at the AAD root level (#189260)
Related to #183220 ## Summary This PR saves ECS groups in the Alert As Data (AAD) document for the log threshold and SLO burn rate rules. |Rule|AAD document| |---|---| |SLO burn rate|| |Log threshold|| ### 🧪 How to test - Create a log threshold and SLO burn rate rule with multiple groups (both ECS and non-ECS fields) - Check the related AAD document; you should be able to see the ECS fields at the root level and not see non-ECS fields there - Check the same information for the recovered alerts - Rules without group by should work as before --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
9c242ac773
commit
b109e75e64
4 changed files with 42 additions and 17 deletions
|
@ -31,7 +31,7 @@ import {
|
|||
PublicAlertsClient,
|
||||
RecoveredAlertData,
|
||||
} from '@kbn/alerting-plugin/server/alerts_client/types';
|
||||
import { type Group } from '@kbn/observability-alerting-rule-utils';
|
||||
import { getEcsGroups, type Group } from '@kbn/observability-alerting-rule-utils';
|
||||
|
||||
import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map';
|
||||
import { decodeOrThrow } from '@kbn/io-ts-utils';
|
||||
|
@ -191,6 +191,7 @@ export const createLogThresholdExecutor =
|
|||
[ALERT_CONTEXT]: alertContext,
|
||||
[ALERT_GROUP]: groups,
|
||||
...flattenAdditionalContext(rootLevelContext),
|
||||
...getEcsGroups(groups),
|
||||
};
|
||||
|
||||
alertsClient.setAlertData({
|
||||
|
|
|
@ -338,19 +338,22 @@ describe('BurnRateRuleExecutor', () => {
|
|||
});
|
||||
|
||||
it('schedules an alert when both windows of first window definition burn rate have reached the threshold', async () => {
|
||||
const slo = createSLO({ objective: { target: 0.9 }, groupBy: ['group.by.field'] });
|
||||
const slo = createSLO({
|
||||
objective: { target: 0.9 },
|
||||
groupBy: ['group.by.field', 'client.geo.continent_name'],
|
||||
});
|
||||
const ruleParams = someRuleParamsWithWindows({ sloId: slo.id });
|
||||
soClientMock.find.mockResolvedValueOnce(createFindResponse([slo]));
|
||||
const buckets = [
|
||||
{
|
||||
instanceId: 'foo',
|
||||
instanceId: 'foo,asia',
|
||||
windows: [
|
||||
{ shortWindowBurnRate: 2.1, longWindowBurnRate: 2.3 },
|
||||
{ shortWindowBurnRate: 0.9, longWindowBurnRate: 1.2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
instanceId: 'bar',
|
||||
instanceId: 'bar,asia',
|
||||
windows: [
|
||||
{ shortWindowBurnRate: 2.2, longWindowBurnRate: 2.5 },
|
||||
{ shortWindowBurnRate: 0.9, longWindowBurnRate: 1.2 },
|
||||
|
@ -391,75 +394,93 @@ describe('BurnRateRuleExecutor', () => {
|
|||
});
|
||||
|
||||
expect(servicesMock.alertsClient?.report).toBeCalledWith({
|
||||
id: 'foo',
|
||||
id: 'foo,asia',
|
||||
actionGroup: ALERT_ACTION.id,
|
||||
state: {
|
||||
alertState: AlertStates.ALERT,
|
||||
},
|
||||
payload: {
|
||||
[ALERT_REASON]:
|
||||
'CRITICAL: The burn rate for the past 1h is 2.3 and for the past 5m is 2.1 for foo. Alert when above 2 for both windows',
|
||||
'CRITICAL: The burn rate for the past 1h is 2.3 and for the past 5m is 2.1 for foo,asia. Alert when above 2 for both windows',
|
||||
[ALERT_EVALUATION_THRESHOLD]: 2,
|
||||
[ALERT_EVALUATION_VALUE]: 2.1,
|
||||
[SLO_ID_FIELD]: slo.id,
|
||||
[SLO_REVISION_FIELD]: slo.revision,
|
||||
[SLO_INSTANCE_ID_FIELD]: 'foo',
|
||||
[SLO_INSTANCE_ID_FIELD]: 'foo,asia',
|
||||
[ALERT_GROUP]: [
|
||||
{
|
||||
field: 'group.by.field',
|
||||
value: 'foo',
|
||||
},
|
||||
{
|
||||
field: 'client.geo.continent_name',
|
||||
value: 'asia',
|
||||
},
|
||||
],
|
||||
'client.geo.continent_name': 'asia',
|
||||
},
|
||||
});
|
||||
expect(servicesMock.alertsClient?.report).toBeCalledWith({
|
||||
id: 'bar',
|
||||
id: 'bar,asia',
|
||||
actionGroup: ALERT_ACTION.id,
|
||||
state: {
|
||||
alertState: AlertStates.ALERT,
|
||||
},
|
||||
payload: {
|
||||
[ALERT_REASON]:
|
||||
'CRITICAL: The burn rate for the past 1h is 2.5 and for the past 5m is 2.2 for bar. Alert when above 2 for both windows',
|
||||
'CRITICAL: The burn rate for the past 1h is 2.5 and for the past 5m is 2.2 for bar,asia. Alert when above 2 for both windows',
|
||||
[ALERT_EVALUATION_THRESHOLD]: 2,
|
||||
[ALERT_EVALUATION_VALUE]: 2.2,
|
||||
[SLO_ID_FIELD]: slo.id,
|
||||
[SLO_REVISION_FIELD]: slo.revision,
|
||||
[SLO_INSTANCE_ID_FIELD]: 'bar',
|
||||
[SLO_INSTANCE_ID_FIELD]: 'bar,asia',
|
||||
[ALERT_GROUP]: [
|
||||
{
|
||||
field: 'group.by.field',
|
||||
value: 'bar',
|
||||
},
|
||||
{
|
||||
field: 'client.geo.continent_name',
|
||||
value: 'asia',
|
||||
},
|
||||
],
|
||||
'client.geo.continent_name': 'asia',
|
||||
},
|
||||
});
|
||||
expect(servicesMock.alertsClient?.setAlertData).toHaveBeenCalledTimes(2);
|
||||
expect(servicesMock.alertsClient?.setAlertData).toHaveBeenNthCalledWith(1, {
|
||||
id: 'foo',
|
||||
id: 'foo,asia',
|
||||
context: expect.objectContaining({
|
||||
longWindow: { burnRate: 2.3, duration: '1h' },
|
||||
shortWindow: { burnRate: 2.1, duration: '5m' },
|
||||
burnRateThreshold: 2,
|
||||
reason:
|
||||
'CRITICAL: The burn rate for the past 1h is 2.3 and for the past 5m is 2.1 for foo. Alert when above 2 for both windows',
|
||||
'CRITICAL: The burn rate for the past 1h is 2.3 and for the past 5m is 2.1 for foo,asia. Alert when above 2 for both windows',
|
||||
alertDetailsUrl: 'mockedAlertsLocator > getLocation',
|
||||
}),
|
||||
});
|
||||
expect(servicesMock.alertsClient?.setAlertData).toHaveBeenNthCalledWith(2, {
|
||||
id: 'bar',
|
||||
id: 'bar,asia',
|
||||
context: expect.objectContaining({
|
||||
longWindow: { burnRate: 2.5, duration: '1h' },
|
||||
shortWindow: { burnRate: 2.2, duration: '5m' },
|
||||
burnRateThreshold: 2,
|
||||
reason:
|
||||
'CRITICAL: The burn rate for the past 1h is 2.5 and for the past 5m is 2.2 for bar. Alert when above 2 for both windows',
|
||||
'CRITICAL: The burn rate for the past 1h is 2.5 and for the past 5m is 2.2 for bar,asia. Alert when above 2 for both windows',
|
||||
alertDetailsUrl: 'mockedAlertsLocator > getLocation',
|
||||
}),
|
||||
});
|
||||
|
||||
expect(alertsLocatorMock.getLocation).toBeCalledWith({
|
||||
expect(alertsLocatorMock.getLocation).toHaveBeenCalledTimes(2);
|
||||
expect(alertsLocatorMock.getLocation).toHaveBeenNthCalledWith(1, {
|
||||
baseUrl: 'https://kibana.dev',
|
||||
kuery: 'kibana.alert.uuid: "uuid-foo"',
|
||||
kuery: 'kibana.alert.uuid: "uuid-foo,asia"',
|
||||
rangeFrom: expect.stringMatching(ISO_DATE_REGEX),
|
||||
spaceId: 'irrelevant',
|
||||
});
|
||||
expect(alertsLocatorMock.getLocation).toHaveBeenNthCalledWith(2, {
|
||||
baseUrl: 'https://kibana.dev',
|
||||
kuery: 'kibana.alert.uuid: "uuid-bar,asia"',
|
||||
rangeFrom: expect.stringMatching(ISO_DATE_REGEX),
|
||||
spaceId: 'irrelevant',
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { getEcsGroups } from '@kbn/observability-alerting-rule-utils';
|
||||
import {
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
|
@ -184,6 +185,7 @@ export const getRuleExecutor = ({
|
|||
[SLO_ID_FIELD]: slo.id,
|
||||
[SLO_REVISION_FIELD]: slo.revision,
|
||||
[SLO_INSTANCE_ID_FIELD]: instanceId,
|
||||
...getEcsGroups(groups),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
"@kbn/react-kibana-context-render",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/core-theme-browser",
|
||||
"@kbn/ebt-tools"
|
||||
"@kbn/ebt-tools",
|
||||
"@kbn/observability-alerting-rule-utils"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue