feat: support regular expressions in receiver configs

This allows using the `keep_re` and `strip_re` keys to specify which
receivers should be shown in the dashboard. This makes it easy to select
groups of receivers based on naming conventions.
This commit is contained in:
Erica Mays
2025-03-17 22:18:01 +00:00
committed by Łukasz Mierzwa
parent 60b0fa572a
commit d8d97b8914
13 changed files with 98 additions and 11 deletions
@@ -221,9 +221,11 @@ level=info msg="receivers:"
level=info msg=" keep:"
level=info msg=" - keep1"
level=info msg=" - keep2"
level=info msg=" keep_re: []"
level=info msg=" strip:"
level=info msg=" - strip1"
level=info msg=" - strip2"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 1s"
level=info msg=" comments:"
@@ -257,9 +257,11 @@ level=info msg="receivers:"
level=info msg=" keep:"
level=info msg=" - keep1"
level=info msg=" - keep2"
level=info msg=" keep_re: []"
level=info msg=" strip:"
level=info msg=" - strip1"
level=info msg=" - strip2"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 5m0s"
level=info msg=" comments:"
@@ -113,7 +113,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
+2
View File
@@ -113,7 +113,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
@@ -113,7 +113,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
@@ -123,7 +123,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
@@ -113,7 +113,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
@@ -92,7 +92,9 @@ level=info msg=" requests: false"
level=info msg=" timestamp: false"
level=info msg="receivers:"
level=info msg=" keep: []"
level=info msg=" keep_re: []"
level=info msg=" strip: []"
level=info msg=" strip_re: []"
level=info msg="silences:"
level=info msg=" expired: 10m0s"
level=info msg=" comments:"
+7 -1
View File
@@ -37,7 +37,13 @@ func DedupAlerts() []models.AlertGroup {
alert := alert // scopelint pin
// remove all alerts for receiver(s) that the user doesn't
// want to see in the UI
if transform.StripReceivers(config.Config.Receivers.Keep, config.Config.Receivers.Strip, alert.Receiver) {
if transform.StripReceivers(
config.Config.Receivers.Keep,
config.Config.Receivers.Strip,
config.Config.Receivers.CompiledKeepRegex,
config.Config.Receivers.CompiledStripRegex,
alert.Receiver,
) {
continue
}
+20
View File
@@ -126,8 +126,12 @@ func SetupFlags(f *pflag.FlagSet) {
f.StringSlice("receivers.keep", []string{},
"List of receivers to keep, all alerts with different receivers will be ignored")
f.StringSlice("receivers.keep_re", []string{},
"List of regular expressions to keep matching receivers, all other receivers will be ignored")
f.StringSlice("receivers.strip", []string{},
"List of receivers to not display alerts for")
f.StringSlice("receivers.strip_re", []string{},
"List of regular expressions to ignore matching receivers")
f.Duration("silences.expired", time.Minute*10, "Maximum age of expired silences to show on active alerts")
f.StringSlice("silenceForm.strip.labels", []string{}, "List of labels to ignore when auto-filling silence form from alerts")
@@ -413,6 +417,22 @@ func (config *configSchema) Read(flags *pflag.FlagSet) (string, error) {
}
}
config.Receivers.CompiledKeepRegex = make([]*regexp.Regexp, len(config.Receivers.KeepRegex))
for i, keepRegex := range config.Receivers.KeepRegex {
config.Receivers.CompiledKeepRegex[i], err = regex.CompileAnchored(keepRegex)
if err != nil {
return "", fmt.Errorf("keep regex rule '%s' is invalid: %w", keepRegex, err)
}
}
config.Receivers.CompiledStripRegex = make([]*regexp.Regexp, len(config.Receivers.StripRegex))
for i, stripRegex := range config.Receivers.KeepRegex {
config.Receivers.CompiledStripRegex[i], err = regex.CompileAnchored(stripRegex)
if err != nil {
return "", fmt.Errorf("strip regex rule '%s' is invalid: %w", stripRegex, err)
}
}
if !slices.StringInSlice([]string{"disabled", "startsAt", "label"}, config.Grid.Sorting.Order) {
return "", fmt.Errorf("invalid grid.sorting.order value '%s', allowed options: disabled, startsAt, label", config.Grid.Sorting.Order)
}
+6 -2
View File
@@ -195,8 +195,12 @@ type configSchema struct {
Timestamp bool
}
Receivers struct {
Keep []string
Strip []string
Keep []string
KeepRegex []string `yaml:"keep_re" koanf:"keep_re"`
CompiledKeepRegex []*regexp.Regexp `yaml:"-"`
Strip []string
StripRegex []string `yaml:"strip_re" koanf:"strip_re"`
CompiledStripRegex []*regexp.Regexp `yaml:"-"`
}
Silences struct {
Expired time.Duration
+8 -7
View File
@@ -40,13 +40,14 @@ func StripLables(keptLabels, ignoredLabels []string, keptLabelsRegex, ignoredLab
// StripReceivers allows filtering all alerts for specified receiver(s)
// it will return true if alert uses receiver that should be stripped
func StripReceivers(keptReceivers, ignoredReceivers []string, alertReceiver string) bool {
// true if we keep by default
keepAll := len(keptReceivers) == 0
// is this receiver on the whitelist ?
inKeep := slices.StringInSlice(keptReceivers, alertReceiver)
// is this receiver on the blacklist ?
inStrip := slices.StringInSlice(ignoredReceivers, alertReceiver)
func StripReceivers(keptReceivers, ignoredReceivers []string, keptReceiversRegex, ignoredReceiversRegex []*regexp.Regexp, alertReceiver string) bool {
// empty keep lists means keep everything by default
keepAll := len(keptReceivers) == 0 && len(keptReceiversRegex) == 0
// is explicitly marked to be kept
inKeep := slices.StringInSlice(keptReceivers, alertReceiver) || slices.MatchesAnyRegex(alertReceiver, keptReceiversRegex)
// is explicitly marked to be stripped
inStrip := slices.StringInSlice(ignoredReceivers, alertReceiver) || slices.MatchesAnyRegex(alertReceiver, ignoredReceiversRegex)
if (keepAll || inKeep) && !inStrip {
return false
}
+41 -1
View File
@@ -201,6 +201,8 @@ func getCompiledRegex(regexes []string, t *testing.T) []*regexp.Regexp {
type stripReceiverTest struct {
strip []string
keep []string
stripRe []string
keepRe []string
receiver string
stripped bool
}
@@ -209,44 +211,82 @@ var stripReceiverTests = []stripReceiverTest{
{
strip: []string{},
keep: []string{},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: false,
},
{
strip: []string{"default"},
keep: []string{},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: true,
},
{
strip: []string{"default"},
keep: []string{"default"},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: true,
},
{
strip: []string{},
keep: []string{"default"},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: false,
},
{
strip: []string{"foo", "bar"},
keep: []string{},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: false,
},
{
strip: []string{"foo", "default"},
keep: []string{"foo", "bar"},
stripRe: []string{},
keepRe: []string{},
receiver: "default",
stripped: true,
},
{
strip: []string{},
keep: []string{},
stripRe: []string{},
keepRe: []string{"default-.+"},
receiver: "default-foo",
stripped: false,
},
{
strip: []string{},
keep: []string{},
stripRe: []string{},
keepRe: []string{"default-.+"},
receiver: "foo-bar",
stripped: true,
},
{
strip: []string{},
keep: []string{"default-foo"},
stripRe: []string{"default-.+"},
keepRe: []string{},
receiver: "default-foo",
stripped: true,
},
}
func TestStripReceivers(t *testing.T) {
for _, testCase := range stripReceiverTests {
stripped := transform.StripReceivers(testCase.keep, testCase.strip, testCase.receiver)
keepRegex := getCompiledRegex(testCase.keepRe, t)
stripRegex := getCompiledRegex(testCase.keepRe, t)
stripped := transform.StripReceivers(testCase.keep, testCase.strip, keepRegex, stripRegex, testCase.receiver)
if stripped != testCase.stripped {
t.Errorf("StripReceivers failed, expected %v, got %v", testCase.stripped, stripped)
}