mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
feat(slo): Add more context variable to burn rate rule (#153647)
This commit is contained in:
parent
a38dfe96c9
commit
4531ee489f
10 changed files with 82 additions and 32 deletions
|
@ -38,8 +38,12 @@ export const registerObservabilityRuleTypes = (
|
|||
defaultActionMessage: i18n.translate(
|
||||
'xpack.observability.slo.rules.burnRate.defaultActionMessage',
|
||||
{
|
||||
defaultMessage: `\\{\\{rule.name\\}\\} is firing:
|
||||
- Reason: \\{\\{context.reason\\}\\}`,
|
||||
defaultMessage: `The rule \\{\\{rule.name\\}\\} for the SLO '\\{\\{context.sloName\\}\\}' is firing:
|
||||
- Reason: \\{\\{context.reason\\}\\}
|
||||
- The burn rate over the last \\{\\{context.longWindow.duration\\}\\} is \\{\\{context.longWindow.burnRate\\}\\}
|
||||
- The burn rate over the last \\{\\{context.shortWindow.duration\\}\\} is \\{\\{context.shortWindow.burnRate\\}\\}
|
||||
- The burn rate threshold is set to \\{\\{context.burnRateThreshold\\}\\}
|
||||
- View in the SLO details page: \\{\\{context.viewInAppUrl\\}\\}`,
|
||||
}
|
||||
),
|
||||
});
|
||||
|
|
|
@ -6,15 +6,16 @@
|
|||
*/
|
||||
|
||||
import { PluginSetupContract } from '@kbn/alerting-plugin/server';
|
||||
import { Logger } from '@kbn/core/server';
|
||||
import { IBasePath, Logger } from '@kbn/core/server';
|
||||
import { createLifecycleExecutor, IRuleDataClient } from '@kbn/rule-registry-plugin/server';
|
||||
import { sloBurnRateRuleType } from './slo_burn_rate';
|
||||
|
||||
export function registerRuleTypes(
|
||||
alertingPlugin: PluginSetupContract,
|
||||
logger: Logger,
|
||||
ruleDataClient: IRuleDataClient
|
||||
ruleDataClient: IRuleDataClient,
|
||||
basePath: IBasePath
|
||||
) {
|
||||
const createLifecycleRuleExecutor = createLifecycleExecutor(logger.get('rules'), ruleDataClient);
|
||||
alertingPlugin.registerType(sloBurnRateRuleType(createLifecycleRuleExecutor));
|
||||
alertingPlugin.registerType(sloBurnRateRuleType(createLifecycleRuleExecutor, basePath));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { IUiSettingsClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { IBasePath, IUiSettingsClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import {
|
||||
ElasticsearchClientMock,
|
||||
elasticsearchServiceMock,
|
||||
|
@ -25,7 +25,7 @@ import {
|
|||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { FIRED_ACTION, getRuleExecutor } from './executor';
|
||||
import { ALERT_ACTION, getRuleExecutor } from './executor';
|
||||
import { aStoredSLO, createSLO } from '../../../services/slo/fixtures/slo';
|
||||
import { SLO } from '../../../domain/models';
|
||||
import { SharePluginStart } from '@kbn/share-plugin/server';
|
||||
|
@ -60,6 +60,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
let esClientMock: ElasticsearchClientMock;
|
||||
let soClientMock: jest.Mocked<SavedObjectsClientContract>;
|
||||
let loggerMock: jest.Mocked<MockedLogger>;
|
||||
const basePathMock = { publicBaseUrl: 'https://kibana.dev' } as IBasePath;
|
||||
let alertWithLifecycleMock: jest.MockedFn<LifecycleAlertService>;
|
||||
let alertFactoryMock: jest.Mocked<
|
||||
PublicAlertFactory<BurnRateAlertState, BurnRateAlertContext, BurnRateAllowedActionGroups>
|
||||
|
@ -102,7 +103,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
|
||||
it('throws when the slo is not found', async () => {
|
||||
soClientMock.get.mockRejectedValue(new Error('NotFound'));
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
|
||||
await expect(
|
||||
executor({
|
||||
|
@ -123,7 +124,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
it('returns early when the slo is disabled', async () => {
|
||||
const slo = createSLO({ objective: { target: 0.9 }, enabled: false });
|
||||
soClientMock.get.mockResolvedValue(aStoredSLO(slo));
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
|
||||
const result = await executor({
|
||||
params: someRuleParams({ sloId: slo.id, burnRateThreshold: BURN_RATE_THRESHOLD }),
|
||||
|
@ -152,7 +153,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
);
|
||||
alertFactoryMock.done.mockReturnValueOnce({ getRecoveredAlerts: () => [] });
|
||||
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
await executor({
|
||||
params: someRuleParams({ sloId: slo.id, burnRateThreshold: BURN_RATE_THRESHOLD }),
|
||||
startedAt: new Date(),
|
||||
|
@ -177,7 +178,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
);
|
||||
alertFactoryMock.done.mockReturnValueOnce({ getRecoveredAlerts: () => [] });
|
||||
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
await executor({
|
||||
params: someRuleParams({ sloId: slo.id, burnRateThreshold: BURN_RATE_THRESHOLD }),
|
||||
startedAt: new Date(),
|
||||
|
@ -202,7 +203,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
);
|
||||
alertFactoryMock.done.mockReturnValueOnce({ getRecoveredAlerts: () => [] });
|
||||
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
await executor({
|
||||
params: someRuleParams({ sloId: slo.id, threshold: BURN_RATE_THRESHOLD }),
|
||||
startedAt: new Date(),
|
||||
|
@ -232,7 +233,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
alertWithLifecycleMock.mockImplementation(() => alertMock as any);
|
||||
alertFactoryMock.done.mockReturnValueOnce({ getRecoveredAlerts: () => [] });
|
||||
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
await executor({
|
||||
params: someRuleParams({ sloId: slo.id, burnRateThreshold: BURN_RATE_THRESHOLD }),
|
||||
startedAt: new Date(),
|
||||
|
@ -256,7 +257,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
},
|
||||
});
|
||||
expect(alertMock.scheduleActions).toBeCalledWith(
|
||||
FIRED_ACTION.id,
|
||||
ALERT_ACTION.id,
|
||||
expect.objectContaining({
|
||||
longWindow: { burnRate: 2, duration: '1h' },
|
||||
shortWindow: { burnRate: 2, duration: '5m' },
|
||||
|
@ -279,7 +280,7 @@ describe('BurnRateRuleExecutor', () => {
|
|||
};
|
||||
alertFactoryMock.done.mockReturnValueOnce({ getRecoveredAlerts: () => [alertMock] as any });
|
||||
|
||||
const executor = getRuleExecutor();
|
||||
const executor = getRuleExecutor({ basePath: basePathMock });
|
||||
|
||||
await executor({
|
||||
params: someRuleParams({ sloId: slo.id, burnRateThreshold: BURN_RATE_THRESHOLD }),
|
||||
|
|
|
@ -15,6 +15,8 @@ import {
|
|||
import { LifecycleRuleExecutor } from '@kbn/rule-registry-plugin/server';
|
||||
import { ExecutorType } from '@kbn/alerting-plugin/server';
|
||||
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/server';
|
||||
import { IBasePath } from '@kbn/core/server';
|
||||
import { Duration, toDurationUnit } from '../../../domain/models';
|
||||
import { DefaultSLIClient, KibanaSavedObjectsSLORepository } from '../../../services/slo';
|
||||
import { computeBurnRate } from '../../../domain/services';
|
||||
|
@ -30,7 +32,11 @@ import {
|
|||
const SHORT_WINDOW = 'SHORT_WINDOW';
|
||||
const LONG_WINDOW = 'LONG_WINDOW';
|
||||
|
||||
export const getRuleExecutor = (): LifecycleRuleExecutor<
|
||||
export const getRuleExecutor = ({
|
||||
basePath,
|
||||
}: {
|
||||
basePath: IBasePath;
|
||||
}): LifecycleRuleExecutor<
|
||||
BurnRateRuleParams,
|
||||
BurnRateRuleTypeState,
|
||||
BurnRateAlertState,
|
||||
|
@ -41,6 +47,7 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
services,
|
||||
params,
|
||||
startedAt,
|
||||
spaceId,
|
||||
}): ReturnType<
|
||||
ExecutorType<
|
||||
BurnRateRuleParams,
|
||||
|
@ -86,6 +93,12 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
longWindowBurnRate >= params.burnRateThreshold &&
|
||||
shortWindowBurnRate >= params.burnRateThreshold;
|
||||
|
||||
const viewInAppUrl = addSpaceIdToPath(
|
||||
basePath.publicBaseUrl,
|
||||
spaceId,
|
||||
`/app/observability/slos/${slo.id}`
|
||||
);
|
||||
|
||||
if (shouldAlert) {
|
||||
const reason = buildReason(
|
||||
longWindowDuration,
|
||||
|
@ -101,6 +114,9 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
shortWindow: { burnRate: shortWindowBurnRate, duration: shortWindowDuration.format() },
|
||||
burnRateThreshold: params.burnRateThreshold,
|
||||
timestamp: startedAt.toISOString(),
|
||||
viewInAppUrl,
|
||||
sloId: slo.id,
|
||||
sloName: slo.name,
|
||||
};
|
||||
|
||||
const alert = alertWithLifecycle({
|
||||
|
@ -112,7 +128,7 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
},
|
||||
});
|
||||
|
||||
alert.scheduleActions(FIRED_ACTION.id, context);
|
||||
alert.scheduleActions(ALERT_ACTION.id, context);
|
||||
alert.replaceState({ alertState: AlertStates.ALERT });
|
||||
}
|
||||
|
||||
|
@ -124,6 +140,9 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
shortWindow: { burnRate: shortWindowBurnRate, duration: shortWindowDuration.format() },
|
||||
burnRateThreshold: params.burnRateThreshold,
|
||||
timestamp: startedAt.toISOString(),
|
||||
viewInAppUrl,
|
||||
sloId: slo.id,
|
||||
sloName: slo.name,
|
||||
};
|
||||
|
||||
recoveredAlert.setContext(context);
|
||||
|
@ -132,10 +151,10 @@ export const getRuleExecutor = (): LifecycleRuleExecutor<
|
|||
return { state: {} };
|
||||
};
|
||||
|
||||
const FIRED_ACTION_ID = 'slo.burnRate.fired';
|
||||
export const FIRED_ACTION = {
|
||||
id: FIRED_ACTION_ID,
|
||||
name: i18n.translate('xpack.observability.slo.alerting.burnRate.fired', {
|
||||
const ALERT_ACTION_ID = 'slo.burnRate.alert';
|
||||
export const ALERT_ACTION = {
|
||||
id: ALERT_ACTION_ID,
|
||||
name: i18n.translate('xpack.observability.slo.alerting.burnRate.alertAction', {
|
||||
defaultMessage: 'Alert',
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -10,11 +10,12 @@ import { i18n } from '@kbn/i18n';
|
|||
import { LicenseType } from '@kbn/licensing-plugin/server';
|
||||
import { createLifecycleExecutor } from '@kbn/rule-registry-plugin/server';
|
||||
import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils';
|
||||
import { IBasePath } from '@kbn/core/server';
|
||||
import { sloFeatureId } from '../../../../common';
|
||||
import { SLO_RULE_REGISTRATION_CONTEXT } from '../../../common/constants';
|
||||
|
||||
import { SLO_BURN_RATE_RULE_ID } from '../../../../common/constants';
|
||||
import { FIRED_ACTION, getRuleExecutor } from './executor';
|
||||
import { ALERT_ACTION, getRuleExecutor } from './executor';
|
||||
|
||||
const durationSchema = schema.object({
|
||||
value: schema.number(),
|
||||
|
@ -23,7 +24,10 @@ const durationSchema = schema.object({
|
|||
|
||||
type CreateLifecycleExecutor = ReturnType<typeof createLifecycleExecutor>;
|
||||
|
||||
export function sloBurnRateRuleType(createLifecycleRuleExecutor: CreateLifecycleExecutor) {
|
||||
export function sloBurnRateRuleType(
|
||||
createLifecycleRuleExecutor: CreateLifecycleExecutor,
|
||||
basePath: IBasePath
|
||||
) {
|
||||
return {
|
||||
id: SLO_BURN_RATE_RULE_ID,
|
||||
name: i18n.translate('xpack.observability.slo.rules.burnRate.name', {
|
||||
|
@ -38,12 +42,12 @@ export function sloBurnRateRuleType(createLifecycleRuleExecutor: CreateLifecycle
|
|||
shortWindow: durationSchema,
|
||||
}),
|
||||
},
|
||||
defaultActionGroupId: FIRED_ACTION.id,
|
||||
actionGroups: [FIRED_ACTION],
|
||||
defaultActionGroupId: ALERT_ACTION.id,
|
||||
actionGroups: [ALERT_ACTION],
|
||||
producer: sloFeatureId,
|
||||
minimumLicenseRequired: 'platinum' as LicenseType,
|
||||
isExportable: true,
|
||||
executor: createLifecycleRuleExecutor(getRuleExecutor()),
|
||||
executor: createLifecycleRuleExecutor(getRuleExecutor({ basePath })),
|
||||
doesSetRecoveryContext: true,
|
||||
actionVariables: {
|
||||
context: [
|
||||
|
@ -52,6 +56,9 @@ export function sloBurnRateRuleType(createLifecycleRuleExecutor: CreateLifecycle
|
|||
{ name: 'burnRateThreshold', description: thresholdActionVariableDescription },
|
||||
{ name: 'longWindow', description: windowActionVariableDescription },
|
||||
{ name: 'shortWindow', description: windowActionVariableDescription },
|
||||
{ name: 'viewInAppUrl', description: viewInAppUrlActionVariableDescription },
|
||||
{ name: 'sloId', description: sloIdActionVariableDescription },
|
||||
{ name: 'sloName', description: sloNameActionVariableDescription },
|
||||
],
|
||||
},
|
||||
alerts: {
|
||||
|
@ -89,3 +96,24 @@ export const timestampActionVariableDescription = i18n.translate(
|
|||
defaultMessage: 'A timestamp of when the alert was detected.',
|
||||
}
|
||||
);
|
||||
|
||||
export const viewInAppUrlActionVariableDescription = i18n.translate(
|
||||
'xpack.observability.slo.alerting.viewInAppUrlDescription',
|
||||
{
|
||||
defaultMessage: 'The url to the SLO details page to help with further investigation.',
|
||||
}
|
||||
);
|
||||
|
||||
export const sloIdActionVariableDescription = i18n.translate(
|
||||
'xpack.observability.slo.alerting.sloIdDescription',
|
||||
{
|
||||
defaultMessage: 'The SLO unique identifier.',
|
||||
}
|
||||
);
|
||||
|
||||
export const sloNameActionVariableDescription = i18n.translate(
|
||||
'xpack.observability.slo.alerting.sloNameDescription',
|
||||
{
|
||||
defaultMessage: 'The SLO name.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
AlertInstanceContext as AlertContext,
|
||||
AlertInstanceState as AlertState,
|
||||
} from '@kbn/alerting-plugin/common';
|
||||
import { FIRED_ACTION } from './executor';
|
||||
import { ALERT_ACTION } from './executor';
|
||||
|
||||
export enum AlertStates {
|
||||
OK,
|
||||
|
@ -27,4 +27,4 @@ export type BurnRateRuleParams = {
|
|||
export type BurnRateRuleTypeState = RuleTypeState; // no specific rule state
|
||||
export type BurnRateAlertState = AlertState; // no specific alert state
|
||||
export type BurnRateAlertContext = AlertContext; // no specific alert context
|
||||
export type BurnRateAllowedActionGroups = ActionGroupIdsOf<typeof FIRED_ACTION>;
|
||||
export type BurnRateAllowedActionGroups = ActionGroupIdsOf<typeof ALERT_ACTION>;
|
||||
|
|
|
@ -231,7 +231,7 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
},
|
||||
],
|
||||
});
|
||||
registerRuleTypes(plugins.alerting, this.logger, ruleDataClient);
|
||||
registerRuleTypes(plugins.alerting, this.logger, ruleDataClient, core.http.basePath);
|
||||
registerSloUsageCollector(plugins.usageCollection);
|
||||
|
||||
registerRoutes({
|
||||
|
|
|
@ -26052,7 +26052,6 @@
|
|||
"xpack.observability.seriesEditor.show": "Afficher la série",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingDescription": "Limiter le nombre de services dans un groupe de services donné",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingName": "Nombre maximum de services dans un groupe de services",
|
||||
"xpack.observability.slo.alerting.burnRate.fired": "Alerte",
|
||||
"xpack.observability.slo.alerting.reasonDescription": "Une description concise de la raison du signalement",
|
||||
"xpack.observability.slo.alerting.thresholdDescription": "Valeur de seuil du taux d'avancement.",
|
||||
"xpack.observability.slo.alerting.timestampDescription": "Horodatage du moment où l'alerte a été détectée.",
|
||||
|
|
|
@ -26033,7 +26033,6 @@
|
|||
"xpack.observability.seriesEditor.show": "系列を表示",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingDescription": "特定のサービスグループのサービス数を制限",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingName": "サービスグループの最大サービス",
|
||||
"xpack.observability.slo.alerting.burnRate.fired": "アラート",
|
||||
"xpack.observability.slo.alerting.reasonDescription": "アラートの理由の簡潔な説明",
|
||||
"xpack.observability.slo.alerting.thresholdDescription": "バーンレートしきい値。",
|
||||
"xpack.observability.slo.alerting.timestampDescription": "アラートが検出された時点のタイムスタンプ。",
|
||||
|
|
|
@ -26049,7 +26049,6 @@
|
|||
"xpack.observability.seriesEditor.show": "显示序列",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingDescription": "限制给定服务组中服务的数量",
|
||||
"xpack.observability.serviceGroupMaxServicesUiSettingName": "服务组中的最大服务数",
|
||||
"xpack.observability.slo.alerting.burnRate.fired": "告警",
|
||||
"xpack.observability.slo.alerting.reasonDescription": "告警原因的简洁描述",
|
||||
"xpack.observability.slo.alerting.thresholdDescription": "消耗速度阈值。",
|
||||
"xpack.observability.slo.alerting.timestampDescription": "检测到告警时的时间戳。",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue