mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
feat(backend): add auto multi-grid config
This commit is contained in:
committed by
Łukasz Mierzwa
parent
d655faf393
commit
e0f52dacbd
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## [unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `grid:auto` config section for fine tuning automatic label selection
|
||||
for multi-grid, when multi-grid is configured to `Automatic selection`
|
||||
in the UI or when `ui:multiGridLabel` config section is set to `@auto`.
|
||||
|
||||
## v0.83
|
||||
|
||||
### Added
|
||||
|
||||
+24
-2
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/prymitive/karma/internal/config"
|
||||
"github.com/prymitive/karma/internal/filters"
|
||||
"github.com/prymitive/karma/internal/models"
|
||||
"github.com/prymitive/karma/internal/slices"
|
||||
"github.com/prymitive/karma/internal/uri"
|
||||
)
|
||||
|
||||
@@ -265,6 +266,24 @@ func sortGrids(r *http.Request, gridLabel string, gridsMap map[string]models.API
|
||||
return grids
|
||||
}
|
||||
|
||||
func isPreferredLabel(label, other string) bool {
|
||||
ai, aj := -1, -1
|
||||
for index, name := range config.Config.Grid.Auto.Order {
|
||||
if label == name {
|
||||
ai = index
|
||||
} else if other == name {
|
||||
aj = index
|
||||
}
|
||||
if ai >= 0 && aj >= 0 {
|
||||
return ai < aj
|
||||
}
|
||||
}
|
||||
if ai != aj {
|
||||
return aj < ai
|
||||
}
|
||||
return label < other
|
||||
}
|
||||
|
||||
func autoGridLabel(dedupedAlerts []models.AlertGroup) string {
|
||||
var alertsCount, alertGroupsCount int
|
||||
labelNameToValueCount := map[string]map[string]int{}
|
||||
@@ -287,6 +306,9 @@ func autoGridLabel(dedupedAlerts []models.AlertGroup) string {
|
||||
|
||||
candidates := map[string]int{}
|
||||
for key, vals := range labelNameToValueCount {
|
||||
if slices.StringInSlice(config.Config.Grid.Auto.Ignore, key) {
|
||||
continue
|
||||
}
|
||||
var total int
|
||||
uniqueValues := map[string]struct{}{}
|
||||
for val, cnt := range vals {
|
||||
@@ -303,11 +325,11 @@ func autoGridLabel(dedupedAlerts []models.AlertGroup) string {
|
||||
var lastLabel string
|
||||
var lastCnt int
|
||||
for key, uniqueValues := range candidates {
|
||||
log.Debug().Int("variants", uniqueValues).Str("label", key).Msg("Automatic grid label candidate")
|
||||
if uniqueValues == 1 || uniqueValues == alertsCount || uniqueValues == alertGroupsCount {
|
||||
continue
|
||||
}
|
||||
if lastCnt == 0 || uniqueValues < lastCnt || (uniqueValues == lastCnt && key > lastLabel) {
|
||||
log.Debug().Int("variants", uniqueValues).Str("label", key).Msg("Automatic grid label candidate")
|
||||
if lastCnt == 0 || uniqueValues < lastCnt || (uniqueValues == lastCnt && isPreferredLabel(key, lastLabel)) {
|
||||
lastLabel = key
|
||||
lastCnt = uniqueValues
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ env FILTERS_DEFAULT='@receiver=by-cluster-service @state=active'
|
||||
env GRID_SORTING_ORDER=label
|
||||
env GRID_SORTING_REVERSE=false
|
||||
env GRID_SORTING_LABEL=severity
|
||||
env GRID_AUTO_IGNORE="region instance"
|
||||
env GRID_AUTO_ORDER="severity cluster"
|
||||
|
||||
env KARMA_NAME=karma-demo
|
||||
|
||||
@@ -146,6 +148,11 @@ level=info msg=" reverse: false"
|
||||
level=info msg=" label: severity"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore:"
|
||||
level=info msg=" - '\"region'"
|
||||
level=info msg=" order:"
|
||||
level=info msg=" - '\"severity'"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma-demo"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -160,6 +160,13 @@ level=info msg=" severity:"
|
||||
level=info msg=" critical: \"1\""
|
||||
level=info msg=" info: \"3\""
|
||||
level=info msg=" warning: \"2\""
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore:"
|
||||
level=info msg=" - region"
|
||||
level=info msg=" - instance"
|
||||
level=info msg=" order:"
|
||||
level=info msg=" - severity"
|
||||
level=info msg=" - cluster"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma-demo"
|
||||
level=info msg="labels:"
|
||||
@@ -346,6 +353,13 @@ grid:
|
||||
critical: 1
|
||||
warning: 2
|
||||
info: 3
|
||||
auto:
|
||||
ignore:
|
||||
- region
|
||||
- instance
|
||||
order:
|
||||
- severity
|
||||
- cluster
|
||||
karma:
|
||||
name: karma-demo
|
||||
labels:
|
||||
|
||||
@@ -67,6 +67,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -67,6 +67,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -67,6 +67,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -69,6 +69,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -67,6 +67,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -67,6 +67,9 @@ level=info msg=" reverse: true"
|
||||
level=info msg=" label: alertname"
|
||||
level=info msg=" customValues:"
|
||||
level=info msg=" labels: {}"
|
||||
level=info msg=" auto:"
|
||||
level=info msg=" ignore: []"
|
||||
level=info msg=" order: []"
|
||||
level=info msg="karma:"
|
||||
level=info msg=" name: karma"
|
||||
level=info msg="labels:"
|
||||
|
||||
@@ -2660,6 +2660,8 @@ func TestAutoGrid(t *testing.T) {
|
||||
type testCaseT struct {
|
||||
q string
|
||||
gridLabel string
|
||||
ignore []string
|
||||
order []string
|
||||
}
|
||||
|
||||
testCases := []testCaseT{
|
||||
@@ -2671,6 +2673,42 @@ func TestAutoGrid(t *testing.T) {
|
||||
q: "gridLabel=@auto",
|
||||
gridLabel: "job",
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster!=prod",
|
||||
gridLabel: "cluster",
|
||||
ignore: []string{"job"},
|
||||
order: []string{"cluster"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster!=prod",
|
||||
gridLabel: "cluster",
|
||||
ignore: []string{},
|
||||
order: []string{"cluster"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster!=prod",
|
||||
gridLabel: "job",
|
||||
ignore: []string{},
|
||||
order: []string{"job", "cluster"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=job=node_exporter",
|
||||
gridLabel: "cluster",
|
||||
ignore: []string{},
|
||||
order: []string{"job", "cluster"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster=dev",
|
||||
gridLabel: "job",
|
||||
ignore: []string{},
|
||||
order: []string{"job", "cluster"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster=dev",
|
||||
gridLabel: "alertname",
|
||||
ignore: []string{},
|
||||
order: []string{},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=job",
|
||||
gridLabel: "job",
|
||||
@@ -2678,19 +2716,29 @@ func TestAutoGrid(t *testing.T) {
|
||||
{
|
||||
q: "gridLabel=@auto&q=instance=server5",
|
||||
gridLabel: "job",
|
||||
ignore: []string{"alertname"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=job=node_exporter",
|
||||
gridLabel: "cluster",
|
||||
ignore: []string{"alertname"},
|
||||
},
|
||||
{
|
||||
q: "gridLabel=@auto&q=cluster=prod",
|
||||
gridLabel: "job",
|
||||
ignore: []string{"alertname", "instance"},
|
||||
},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
config.Config.Grid.Auto.Ignore = []string{}
|
||||
config.Config.Grid.Auto.Order = []string{}
|
||||
}()
|
||||
|
||||
mockConfig()
|
||||
for _, tc := range testCases {
|
||||
config.Config.Grid.Auto.Ignore = tc.ignore
|
||||
config.Config.Grid.Auto.Order = tc.order
|
||||
for _, version := range mock.ListAllMocks() {
|
||||
t.Logf("Testing alerts using mock files from Alertmanager %s", version)
|
||||
mockAlerts(version)
|
||||
|
||||
@@ -54,6 +54,12 @@ grid:
|
||||
critical: 1
|
||||
warning: 2
|
||||
info: 3
|
||||
auto:
|
||||
order:
|
||||
- severity
|
||||
- cluster
|
||||
ignore:
|
||||
- region
|
||||
karma:
|
||||
name: karma-demo
|
||||
labels:
|
||||
|
||||
@@ -645,6 +645,9 @@ grid:
|
||||
label: string
|
||||
customValues:
|
||||
labels: dict
|
||||
auto:
|
||||
ignore: list of strings
|
||||
order: list of strings
|
||||
```
|
||||
|
||||
- `sorting:order` - default sort order for alert grid, valid values are:
|
||||
@@ -668,6 +671,13 @@ grid:
|
||||
instead of original string values.
|
||||
Note: this option is not available via environment variables, you can only set
|
||||
it via the config file.
|
||||
- `auto:ignore` - list of label names that should never be selected as multi-grid
|
||||
source label when multi-grid is configured to `Automatic selection` in the UI
|
||||
or when `ui:multiGridLabel` is set to `@auto`.
|
||||
- `auto:order` - preferred order for selecting labels to be used as multi-grid
|
||||
source label when multi-grid is configured to `Automatic selection` in the UI
|
||||
or when `ui:multiGridLabel` is set to `@auto`. If a label name is not present
|
||||
in this list labels with equal weight will be picked in alphabetic order.
|
||||
|
||||
Defaults:
|
||||
|
||||
@@ -679,6 +689,9 @@ grid:
|
||||
label: alertname
|
||||
customValues:
|
||||
labels: {}
|
||||
auto:
|
||||
ignore: []
|
||||
order: []
|
||||
```
|
||||
|
||||
Example with sorting using `severity` label and value mappings for it:
|
||||
|
||||
@@ -72,6 +72,7 @@ func SetupFlags(f *pflag.FlagSet) {
|
||||
"List of annotations to keep, all other annotations will be stripped")
|
||||
f.StringSlice("annotations.strip", []string{}, "List of annotations to ignore")
|
||||
f.StringSlice("annotations.actions", []string{}, "List of annotations that will be moved to the alert menu")
|
||||
f.StringSlice("annotations.order", []string{}, "Preferred order of annotation names")
|
||||
f.Bool("annotations.enableInsecureHTML", false, "Enable HTML strings in annotations to be parsed as HTML, enable at your own risk")
|
||||
|
||||
f.String("config.file", "", "Full path to the configuration file, 'karma.yaml' will be used if found in the current working directory")
|
||||
@@ -94,6 +95,8 @@ func SetupFlags(f *pflag.FlagSet) {
|
||||
f.String("grid.sorting.order", "startsAt", "Default sort order for alert grid")
|
||||
f.Bool("grid.sorting.reverse", true, "Reverse sort order")
|
||||
f.String("grid.sorting.label", "alertname", "Label name to use when sorting alert grid by label")
|
||||
f.StringSlice("grid.auto.ignore", []string{}, "List of label names not allowed for automatic multi-grid")
|
||||
f.StringSlice("grid.auto.order", []string{}, "Order of preference for selecting label names for automatic multi-grid")
|
||||
|
||||
f.Bool("log.config", false, "Log used configuration to log on startup")
|
||||
f.String("log.level", "info",
|
||||
|
||||
@@ -85,6 +85,9 @@ grid:
|
||||
label: alertname
|
||||
customValues:
|
||||
labels: {}
|
||||
auto:
|
||||
ignore: []
|
||||
order: []
|
||||
karma:
|
||||
name: another karma
|
||||
labels:
|
||||
@@ -330,10 +333,13 @@ func TestDefaultConfig(t *testing.T) {
|
||||
expectedConfig.Annotations.Keep = []string{}
|
||||
expectedConfig.Annotations.Strip = []string{}
|
||||
expectedConfig.Annotations.Actions = []string{}
|
||||
expectedConfig.Annotations.Order = []string{}
|
||||
expectedConfig.Labels.Keep = []string{}
|
||||
expectedConfig.Labels.Strip = []string{}
|
||||
expectedConfig.Labels.Color.Static = []string{}
|
||||
expectedConfig.Labels.Color.Unique = []string{}
|
||||
expectedConfig.Grid.Auto.Ignore = []string{}
|
||||
expectedConfig.Grid.Auto.Order = []string{}
|
||||
expectedConfig.Receivers.Keep = []string{}
|
||||
expectedConfig.Receivers.Strip = []string{}
|
||||
expectedConfig.SilenceForm.Strip.Labels = []string{}
|
||||
|
||||
@@ -123,6 +123,10 @@ type configSchema struct {
|
||||
Labels map[string]map[string]string
|
||||
} `yaml:"customValues" koanf:"customValues"`
|
||||
}
|
||||
Auto struct {
|
||||
Ignore []string
|
||||
Order []string
|
||||
}
|
||||
} `yaml:"grid"`
|
||||
Karma struct {
|
||||
Name string
|
||||
|
||||
Reference in New Issue
Block a user