Skip to content

Commit

Permalink
[AO] Create a new rule under Observability plugin based on the metric…
Browse files Browse the repository at this point in the history
…s threshold rule (#158665)

## Summary
### NOTE: This is a draft PR to MVP the new rule combination
(Threshold). More PRs to follow up
It fixes #158260 by providing
the _new_ **Threshold rule**
It fixes #159326

<img width="586" alt="Screenshot 2023-05-30 at 17 55 32"
src="https://github.com/elastic/kibana/assets/6838659/0e485266-d93f-442e-81f4-77aa673ed497">


## ✅ Done 

- [x] Clone the Metric threshold and update the imports 
- [x] The rule is listed in the rule creation flyout with its params and
preview charts
- [x] Working Rule registry
- [x] Working Rule executor 
- [x] Working feature id in the rule registry 
- [x] Working alerts table and alert summary
- [x] Use DataView instead of the Metrics indices under settings
- [x] Update the i18n keys 
- [x] Fix/Update failing checks/tests. Green CI ✅ 
- [x] Hide it behind a feature flag
`xpack.observability.unsafe.thresholdRule.enabled`




## 🏗️ To be done (could be irrelevant, or create a separate issue for
it):
- [ ] <del> Remove the `metrics` word </del>
- [ ] <del> Update file and variable names to match the new rule
context.</del>
- [ ] <del> Rearrange files, constants, and exports </del>

## 🎯 DoD 
Having the rule working like the Metric threshold one and seeing its
related alerts.

---------
  • Loading branch information
fkanout committed Jun 9, 2023
1 parent e325d41 commit 2d4f19e
Show file tree
Hide file tree
Showing 133 changed files with 13,493 additions and 50 deletions.
4 changes: 4 additions & 0 deletions packages/kbn-check-mappings-update-cli/current_mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2164,6 +2164,10 @@
}
}
},
"threshold-explorer-view": {
"dynamic": false,
"properties": {}
},
"observability-onboarding-state": {
"properties": {
"state": {
Expand Down
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pageLoadAssetSize:
monitoring: 80000
navigation: 37269
newsfeed: 42228
observability: 95000
observability: 100000
observabilityOnboarding: 19573
observabilityShared: 52256
osquery: 107090
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"tag": "e2544392fe6563e215bb677abc8b01c2601ef2dc",
"task": "04f30bd7bae923f3a53c31ab3b9745a93872fc02",
"telemetry": "7b00bcf1c7b4f6db1192bb7405a6a63e78b699fd",
"threshold-explorer-view": "175306806f9fc8e13fcc1c8953ec4ba89bda1b70",
"ui-metric": "d227284528fd19904e9d972aea0a13716fc5fe24",
"upgrade-assistant-ml-upgrade-operation": "421f52731cb24e242d70672ba4725e169277efb3",
"upgrade-assistant-reindex-operation": "01f3c3e051659ace56492a73928987e717537a93",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ describe('split .kibana index into multiple system indices', () => {
"synthetics-privates-locations",
"tag",
"telemetry",
"threshold-explorer-view",
"ui-metric",
"upgrade-assistant-ml-upgrade-operation",
"upgrade-assistant-reindex-operation",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ const previouslyRegisteredTypes = [
'telemetry',
'timelion-sheet',
'tsvb-validation-telemetry',
'threshold-explorer-view',
'ui-counter',
'ui-metric',
'upgrade-assistant-ml-upgrade-operation',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ kibana_vars=(
xpack.observability.unsafe.alertDetails.metrics.enabled
xpack.observability.unsafe.alertDetails.logs.enabled
xpack.observability.unsafe.alertDetails.uptime.enabled
xpack.observability.unsafe.thresholdRule.enabled
xpack.reporting.capture.browser.autoDownload
xpack.reporting.capture.browser.chromium.disableSandbox
xpack.reporting.capture.browser.chromium.inspect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.observability.unsafe.alertDetails.metrics.enabled (boolean)',
'xpack.observability.unsafe.alertDetails.logs.enabled (boolean)',
'xpack.observability.unsafe.alertDetails.uptime.enabled (boolean)',
'xpack.observability.unsafe.thresholdRule.enabled (boolean)',
'xpack.observability_onboarding.ui.enabled (boolean)',
];
// We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/observability/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

import { i18n } from '@kbn/i18n';

export const SLO_BURN_RATE_RULE_ID = 'slo.rules.burnRate';
export const SLO_BURN_RATE_RULE_TYPE_ID = 'slo.rules.burnRate';
export const OBSERVABILITY_THRESHOLD_RULE_TYPE_ID = 'observability.threshold';

export const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g;
export const ALERT_STATUS_ALL = 'all';

export const ALERTS_URL_STORAGE_KEY = '_a';

export const ALERT_ACTION_ID = 'slo.burnRate.alert';
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/observability/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export {
} from './progressive_loading';

export const sloFeatureId = 'slo';

export const casesFeatureId = 'observabilityCases';

// The ID of the observability app. Should more appropriately be called
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { difference, first, values } from 'lodash';
import { euiPaletteColorBlind } from '@elastic/eui';

export enum Color {
color0 = 'color0',
color1 = 'color1',
color2 = 'color2',
color3 = 'color3',
color4 = 'color4',
color5 = 'color5',
color6 = 'color6',
color7 = 'color7',
color8 = 'color8',
color9 = 'color9',
}

export type Palette = {
[K in keyof typeof Color]: string;
};

const euiPalette = euiPaletteColorBlind();

export const defaultPalette: Palette = {
[Color.color0]: euiPalette[1], // (blue)
[Color.color1]: euiPalette[2], // (pink)
[Color.color2]: euiPalette[0], // (green-ish)
[Color.color3]: euiPalette[3], // (purple)
[Color.color4]: euiPalette[4], // (light pink)
[Color.color5]: euiPalette[5], // (yellow)
[Color.color6]: euiPalette[6], // (tan)
[Color.color7]: euiPalette[7], // (orange)
[Color.color8]: euiPalette[8], // (brown)
[Color.color9]: euiPalette[9], // (red)
};

export const createPaletteTransformer = (palette: Palette) => (color: Color) => palette[color];

export const colorTransformer = createPaletteTransformer(defaultPalette);

export const sampleColor = (usedColors: Color[] = []): Color => {
const available = difference(values(Color) as Color[], usedColors);
return first(available) || Color.color0;
};
21 changes: 21 additions & 0 deletions x-pack/plugins/observability/common/threshold_rule/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const SNAPSHOT_CUSTOM_AGGREGATIONS = ['avg', 'max', 'min', 'rate'] as const;
export const TIMESTAMP_FIELD = '@timestamp';
export const METRIC_EXPLORER_AGGREGATIONS = [
'avg',
'max',
'min',
'cardinality',
'rate',
'count',
'sum',
'p95',
'p99',
'custom',
] as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { InfraWaffleMapDataFormat } from './types';
import { createBytesFormatter } from './bytes';

describe('createDataFormatter', () => {
it('should format bytes as bytesDecimal', () => {
const formatter = createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal);
expect(formatter(1000000)).toBe('1 MB');
});
it('should format bytes as bitsDecimal', () => {
const formatter = createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal);
expect(formatter(1000000)).toBe('8 Mbit');
});
it('should format bytes as abbreviatedNumber', () => {
const formatter = createBytesFormatter(InfraWaffleMapDataFormat.abbreviatedNumber);
expect(formatter(1000000)).toBe('1 M');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { formatNumber } from './number';
import { InfraWaffleMapDataFormat } from './types';

/**
* The labels are derived from these two Wikipedia articles.
* https://en.wikipedia.org/wiki/Kilobit
* https://en.wikipedia.org/wiki/Kilobyte
*/
const LABELS = {
[InfraWaffleMapDataFormat.bytesDecimal]: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
[InfraWaffleMapDataFormat.bitsDecimal]: [
'bit',
'kbit',
'Mbit',
'Gbit',
'Tbit',
'Pbit',
'Ebit',
'Zbit',
'Ybit',
],
[InfraWaffleMapDataFormat.abbreviatedNumber]: ['', 'K', 'M', 'B', 'T'],
};

const BASES = {
[InfraWaffleMapDataFormat.bytesDecimal]: 1000,
[InfraWaffleMapDataFormat.bitsDecimal]: 1000,
[InfraWaffleMapDataFormat.abbreviatedNumber]: 1000,
};

/*
* This formatter always assumes you're input is bytes and the output is a string
* in whatever format you've defined. Bytes in Format Out.
*/
export const createBytesFormatter = (format: InfraWaffleMapDataFormat) => (bytes: number) => {
const labels = LABELS[format];
const base = BASES[format];
const value = format === InfraWaffleMapDataFormat.bitsDecimal ? bytes * 8 : bytes;
// Use an exponential equation to get the power to determine which label to use. If the power
// is greater then the max label then use the max label.
const power = Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(base)), labels.length - 1);
if (power < 0) {
return `${formatNumber(value)} ${labels[0]}`;
}
return `${formatNumber(value / Math.pow(base, power))} ${labels[power]}`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';

export function localizedDate(dateTime: number | Date, locale: string = i18n.getLocale()) {
const formatter = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
});

return formatter.format(dateTime);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const formatHighPrecision = (val: number) => {
return Number(val).toLocaleString('en', {
maximumFractionDigits: 5,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { createBytesFormatter } from './bytes';
import { formatNumber } from './number';
import { formatPercent } from './percent';
import { ThresholdFormatterType } from '../types';
import { formatHighPrecision } from './high_precision';
import { InfraWaffleMapDataFormat } from './types';

export const FORMATTERS = {
number: formatNumber,
// Because the implimentation for formatting large numbers is the same as formatting
// bytes we are re-using the same code, we just format the number using the abbreviated number format.
abbreviatedNumber: createBytesFormatter(InfraWaffleMapDataFormat.abbreviatedNumber),
// bytes in bytes formatted string out
bytes: createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal),
// bytes in bits formatted string out
bits: createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal),
percent: formatPercent,
highPrecision: formatHighPrecision,
};

export const createFormatter =
(format: ThresholdFormatterType, template: string = '{{value}}') =>
(val: string | number) => {
if (val == null) {
return '';
}
const fmtFn = FORMATTERS[format];
const value = fmtFn(Number(val));
return template.replace(/{{value}}/g, value);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const formatNumber = (val: number) => {
return Number(val).toLocaleString('en', {
maximumFractionDigits: 1,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { formatNumber } from './number';
export const formatPercent = (val: number) => {
return `${formatNumber(val * 100)}%`;
};
Loading

0 comments on commit 2d4f19e

Please sign in to comment.