diff --git a/CHANGELOG.md b/CHANGELOG.md index f14bcbf26..2121b7ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/cmd/karma/alerts.go b/cmd/karma/alerts.go index 63ae38090..5863eaacd 100644 --- a/cmd/karma/alerts.go +++ b/cmd/karma/alerts.go @@ -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 } diff --git a/cmd/karma/tests/testscript/059_log_full_config_env.txt b/cmd/karma/tests/testscript/059_log_full_config_env.txt index e43f35969..3a7f1f713 100644 --- a/cmd/karma/tests/testscript/059_log_full_config_env.txt +++ b/cmd/karma/tests/testscript/059_log_full_config_env.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/060_log_full_config_file.txt b/cmd/karma/tests/testscript/060_log_full_config_file.txt index 6d495bd40..b17451070 100644 --- a/cmd/karma/tests/testscript/060_log_full_config_file.txt +++ b/cmd/karma/tests/testscript/060_log_full_config_file.txt @@ -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: diff --git a/cmd/karma/tests/testscript/065_proxy-with-readonly.txt b/cmd/karma/tests/testscript/065_proxy-with-readonly.txt index 8e4fffc62..dbe0116db 100644 --- a/cmd/karma/tests/testscript/065_proxy-with-readonly.txt +++ b/cmd/karma/tests/testscript/065_proxy-with-readonly.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/066_proxy.txt b/cmd/karma/tests/testscript/066_proxy.txt index a3ef6988c..3c9470074 100644 --- a/cmd/karma/tests/testscript/066_proxy.txt +++ b/cmd/karma/tests/testscript/066_proxy.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/067_readonly.txt b/cmd/karma/tests/testscript/067_readonly.txt index 043cb2b45..e7b46334c 100644 --- a/cmd/karma/tests/testscript/067_readonly.txt +++ b/cmd/karma/tests/testscript/067_readonly.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/068_sentry.txt b/cmd/karma/tests/testscript/068_sentry.txt index 0ca4020fe..22d02d528 100644 --- a/cmd/karma/tests/testscript/068_sentry.txt +++ b/cmd/karma/tests/testscript/068_sentry.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/070_upper_case_keys.txt b/cmd/karma/tests/testscript/070_upper_case_keys.txt index 85e874753..83c70d84e 100644 --- a/cmd/karma/tests/testscript/070_upper_case_keys.txt +++ b/cmd/karma/tests/testscript/070_upper_case_keys.txt @@ -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:" diff --git a/cmd/karma/tests/testscript/097_proxy_url_config.txt b/cmd/karma/tests/testscript/097_proxy_url_config.txt index bfdf3d347..c934acac4 100644 --- a/cmd/karma/tests/testscript/097_proxy_url_config.txt +++ b/cmd/karma/tests/testscript/097_proxy_url_config.txt @@ -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:" diff --git a/cmd/karma/views_test.go b/cmd/karma/views_test.go index 153f09496..ba272cf98 100644 --- a/cmd/karma/views_test.go +++ b/cmd/karma/views_test.go @@ -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) diff --git a/demo/karma.yaml b/demo/karma.yaml index 866705c8e..55897819c 100644 --- a/demo/karma.yaml +++ b/demo/karma.yaml @@ -54,6 +54,12 @@ grid: critical: 1 warning: 2 info: 3 + auto: + order: + - severity + - cluster + ignore: + - region karma: name: karma-demo labels: diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index de010b2a6..b964edf25 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -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: diff --git a/internal/config/config.go b/internal/config/config.go index 107608be5..fe49abe4f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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", diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 7e2f56a34..ed333d762 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -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{} diff --git a/internal/config/models.go b/internal/config/models.go index e6fc1c452..6b8969f3a 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -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