mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 12:31:07 +01:00
lib/promrelabel: allows regex capture groups in target_label like Prometheus does
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/569
This commit is contained in:
parent
ac3700ed1e
commit
5820c0ffb7
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
@ -124,6 +125,9 @@ func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedR
|
||||
Modulus: modulus,
|
||||
Replacement: replacement,
|
||||
Action: action,
|
||||
|
||||
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
|
||||
hasCaptureGroupInReplacement: strings.Contains(replacement, "$"),
|
||||
})
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ type ParsedRelabelConfig struct {
|
||||
Modulus uint64
|
||||
Replacement string
|
||||
Action string
|
||||
|
||||
hasCaptureGroupInTargetLabel bool
|
||||
hasCaptureGroupInReplacement bool
|
||||
}
|
||||
|
||||
// String returns human-readable representation for prc.
|
||||
@ -103,51 +106,52 @@ func FinalizeLabels(dst, src []prompbmarshal.Label) []prompbmarshal.Label {
|
||||
return dst
|
||||
}
|
||||
|
||||
// applyRelabelConfig applies relabeling according to cfg.
|
||||
// applyRelabelConfig applies relabeling according to prc.
|
||||
//
|
||||
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
|
||||
func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *ParsedRelabelConfig) []prompbmarshal.Label {
|
||||
func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *ParsedRelabelConfig) []prompbmarshal.Label {
|
||||
src := labels[labelsOffset:]
|
||||
switch cfg.Action {
|
||||
switch prc.Action {
|
||||
case "replace":
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = concatLabelValues(bb.B[:0], src, cfg.SourceLabels, cfg.Separator)
|
||||
if len(bb.B) == 0 && cfg.Regex == defaultRegexForRelabelConfig && !strings.Contains(cfg.Replacement, "$") {
|
||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
if len(bb.B) == 0 && prc.Regex == defaultRegexForRelabelConfig && !prc.hasCaptureGroupInReplacement && !prc.hasCaptureGroupInTargetLabel {
|
||||
// Fast path for the following rule that just sets label value:
|
||||
// - target_label: foobar
|
||||
// replacement: something-here
|
||||
relabelBufPool.Put(bb)
|
||||
return setLabelValue(labels, labelsOffset, cfg.TargetLabel, cfg.Replacement)
|
||||
return setLabelValue(labels, labelsOffset, prc.TargetLabel, prc.Replacement)
|
||||
}
|
||||
match := cfg.Regex.FindSubmatchIndex(bb.B)
|
||||
match := prc.Regex.FindSubmatchIndex(bb.B)
|
||||
if match == nil {
|
||||
// Fast path - nothing to replace.
|
||||
relabelBufPool.Put(bb)
|
||||
return labels
|
||||
}
|
||||
sourceStr := bytesutil.ToUnsafeString(bb.B)
|
||||
value := relabelBufPool.Get()
|
||||
value.B = cfg.Regex.ExpandString(value.B[:0], cfg.Replacement, sourceStr, match)
|
||||
nameStr := prc.TargetLabel
|
||||
if prc.hasCaptureGroupInTargetLabel {
|
||||
nameStr = prc.expandCaptureGroups(nameStr, sourceStr, match)
|
||||
}
|
||||
valueStr := prc.expandCaptureGroups(prc.Replacement, sourceStr, match)
|
||||
relabelBufPool.Put(bb)
|
||||
valueStr := string(value.B)
|
||||
relabelBufPool.Put(value)
|
||||
return setLabelValue(labels, labelsOffset, cfg.TargetLabel, valueStr)
|
||||
return setLabelValue(labels, labelsOffset, nameStr, valueStr)
|
||||
case "replace_all":
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = concatLabelValues(bb.B[:0], src, cfg.SourceLabels, cfg.Separator)
|
||||
if !cfg.Regex.Match(bb.B) {
|
||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
if !prc.Regex.Match(bb.B) {
|
||||
// Fast path - nothing to replace.
|
||||
relabelBufPool.Put(bb)
|
||||
return labels
|
||||
}
|
||||
sourceStr := string(bb.B) // Make a copy of bb, since it can be returned from ReplaceAllString
|
||||
relabelBufPool.Put(bb)
|
||||
valueStr := cfg.Regex.ReplaceAllString(sourceStr, cfg.Replacement)
|
||||
return setLabelValue(labels, labelsOffset, cfg.TargetLabel, valueStr)
|
||||
valueStr := prc.Regex.ReplaceAllString(sourceStr, prc.Replacement)
|
||||
return setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr)
|
||||
case "keep":
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = concatLabelValues(bb.B[:0], src, cfg.SourceLabels, cfg.Separator)
|
||||
keep := cfg.Regex.Match(bb.B)
|
||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
keep := prc.Regex.Match(bb.B)
|
||||
relabelBufPool.Put(bb)
|
||||
if !keep {
|
||||
return labels[:labelsOffset]
|
||||
@ -155,8 +159,8 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
return labels
|
||||
case "drop":
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = concatLabelValues(bb.B[:0], src, cfg.SourceLabels, cfg.Separator)
|
||||
drop := cfg.Regex.Match(bb.B)
|
||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
drop := prc.Regex.Match(bb.B)
|
||||
relabelBufPool.Put(bb)
|
||||
if drop {
|
||||
return labels[:labelsOffset]
|
||||
@ -164,20 +168,20 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
return labels
|
||||
case "hashmod":
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = concatLabelValues(bb.B[:0], src, cfg.SourceLabels, cfg.Separator)
|
||||
h := xxhash.Sum64(bb.B) % cfg.Modulus
|
||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
h := xxhash.Sum64(bb.B) % prc.Modulus
|
||||
value := strconv.Itoa(int(h))
|
||||
relabelBufPool.Put(bb)
|
||||
return setLabelValue(labels, labelsOffset, cfg.TargetLabel, value)
|
||||
return setLabelValue(labels, labelsOffset, prc.TargetLabel, value)
|
||||
case "labelmap":
|
||||
for i := range src {
|
||||
label := &src[i]
|
||||
match := cfg.Regex.FindStringSubmatchIndex(label.Name)
|
||||
match := prc.Regex.FindStringSubmatchIndex(label.Name)
|
||||
if match == nil {
|
||||
continue
|
||||
}
|
||||
value := relabelBufPool.Get()
|
||||
value.B = cfg.Regex.ExpandString(value.B[:0], cfg.Replacement, label.Name, match)
|
||||
value.B = prc.Regex.ExpandString(value.B[:0], prc.Replacement, label.Name, match)
|
||||
label.Name = string(value.B)
|
||||
relabelBufPool.Put(value)
|
||||
}
|
||||
@ -185,16 +189,16 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
case "labelmap_all":
|
||||
for i := range src {
|
||||
label := &src[i]
|
||||
if !cfg.Regex.MatchString(label.Name) {
|
||||
if !prc.Regex.MatchString(label.Name) {
|
||||
continue
|
||||
}
|
||||
label.Name = cfg.Regex.ReplaceAllString(label.Name, cfg.Replacement)
|
||||
label.Name = prc.Regex.ReplaceAllString(label.Name, prc.Replacement)
|
||||
}
|
||||
return labels
|
||||
case "labeldrop":
|
||||
keepSrc := true
|
||||
for i := range src {
|
||||
if cfg.Regex.MatchString(src[i].Name) {
|
||||
if prc.Regex.MatchString(src[i].Name) {
|
||||
keepSrc = false
|
||||
break
|
||||
}
|
||||
@ -205,7 +209,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
dst := labels[:labelsOffset]
|
||||
for i := range src {
|
||||
label := &src[i]
|
||||
if !cfg.Regex.MatchString(label.Name) {
|
||||
if !prc.Regex.MatchString(label.Name) {
|
||||
dst = append(dst, *label)
|
||||
}
|
||||
}
|
||||
@ -213,7 +217,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
case "labelkeep":
|
||||
keepSrc := true
|
||||
for i := range src {
|
||||
if !cfg.Regex.MatchString(src[i].Name) {
|
||||
if !prc.Regex.MatchString(src[i].Name) {
|
||||
keepSrc = false
|
||||
break
|
||||
}
|
||||
@ -224,17 +228,25 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, cfg *Par
|
||||
dst := labels[:labelsOffset]
|
||||
for i := range src {
|
||||
label := &src[i]
|
||||
if cfg.Regex.MatchString(label.Name) {
|
||||
if prc.Regex.MatchString(label.Name) {
|
||||
dst = append(dst, *label)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
default:
|
||||
logger.Panicf("BUG: unknown `action`: %q", cfg.Action)
|
||||
logger.Panicf("BUG: unknown `action`: %q", prc.Action)
|
||||
return labels
|
||||
}
|
||||
}
|
||||
|
||||
func (prc *ParsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string {
|
||||
bb := relabelBufPool.Get()
|
||||
bb.B = prc.Regex.ExpandString(bb.B[:0], template, source, match)
|
||||
s := string(bb.B)
|
||||
relabelBufPool.Put(bb)
|
||||
return s
|
||||
}
|
||||
|
||||
var relabelBufPool bytesutil.ByteBufferPool
|
||||
|
||||
func concatLabelValues(dst []byte, labels []prompbmarshal.Label, labelNames []string, separator string) []byte {
|
||||
|
@ -57,28 +57,31 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
t.Run("replace-miss", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, nil, false, []prompbmarshal.Label{})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, nil, false, []prompbmarshal.Label{})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -93,11 +96,12 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: regexp.MustCompile(".+"),
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: regexp.MustCompile(".+"),
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -114,12 +118,13 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
t.Run("replace-hit", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"xxx", "foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1-b",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"xxx", "foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1-b",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -137,31 +142,62 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("replace-hit-target-label-with-capture-group", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"xxx", "foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "bar-$1",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1-b",
|
||||
hasCaptureGroupInTargetLabel: true,
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
Name: "xxx",
|
||||
Value: "yyy",
|
||||
},
|
||||
}, false, []prompbmarshal.Label{
|
||||
{
|
||||
Name: "bar-yyy;",
|
||||
Value: "a-yyy;-b",
|
||||
},
|
||||
{
|
||||
Name: "xxx",
|
||||
Value: "yyy",
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("replace_all-miss", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace_all",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace_all",
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, nil, false, []prompbmarshal.Label{})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, nil, false, []prompbmarshal.Label{})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -176,11 +212,12 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
})
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: regexp.MustCompile(".+"),
|
||||
Replacement: "$1",
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "bar",
|
||||
Regex: regexp.MustCompile(".+"),
|
||||
Replacement: "$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -197,12 +234,13 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
t.Run("replace_all-hit", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"xxx", "foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "xxx",
|
||||
Regex: regexp.MustCompile("(;)"),
|
||||
Replacement: "-$1-",
|
||||
Action: "replace_all",
|
||||
SourceLabels: []string{"xxx", "foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "xxx",
|
||||
Regex: regexp.MustCompile("(;)"),
|
||||
Replacement: "-$1-",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
@ -219,11 +257,12 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
t.Run("replace-add-multi-labels", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"xxx"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"xxx"},
|
||||
TargetLabel: "bar",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
{
|
||||
Action: "replace",
|
||||
@ -263,11 +302,12 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||
t.Run("replace-self", func(t *testing.T) {
|
||||
f([]ParsedRelabelConfig{
|
||||
{
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "foo",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1",
|
||||
Action: "replace",
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "foo",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "a-$1",
|
||||
hasCaptureGroupInReplacement: true,
|
||||
},
|
||||
}, []prompbmarshal.Label{
|
||||
{
|
||||
|
@ -952,6 +952,14 @@ scrape_configs:
|
||||
AuthConfig: &promauth.Config{},
|
||||
},
|
||||
})
|
||||
|
||||
prcs, err := promrelabel.ParseRelabelConfigs(nil, []promrelabel.RelabelConfig{{
|
||||
SourceLabels: []string{"foo"},
|
||||
TargetLabel: "abc",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when parsing relabel configs: %s", err)
|
||||
}
|
||||
f(`
|
||||
scrape_configs:
|
||||
- job_name: foo
|
||||
@ -987,17 +995,8 @@ scrape_configs:
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
AuthConfig: &promauth.Config{},
|
||||
MetricRelabelConfigs: []promrelabel.ParsedRelabelConfig{
|
||||
{
|
||||
SourceLabels: []string{"foo"},
|
||||
Separator: ";",
|
||||
TargetLabel: "abc",
|
||||
Regex: defaultRegexForRelabelConfig,
|
||||
Replacement: "$1",
|
||||
Action: "replace",
|
||||
},
|
||||
},
|
||||
AuthConfig: &promauth.Config{},
|
||||
MetricRelabelConfigs: prcs,
|
||||
},
|
||||
})
|
||||
f(`
|
||||
|
Loading…
Reference in New Issue
Block a user