Add 'hashmod' relabel action.
This takes the modulus of a hash of some labels. Combined with a keep relabel action, this allows for sharding of targets across multiple prometheus servers.
This commit is contained in:
parent
30fc04b240
commit
682f949ab1
|
@ -455,9 +455,11 @@ const (
|
|||
// Performs a regex replacement.
|
||||
RelabelReplace RelabelAction = "replace"
|
||||
// Drops targets for which the input does not match the regex.
|
||||
RelabelKeep = "keep"
|
||||
RelabelKeep RelabelAction = "keep"
|
||||
// Drops targets for which the input does match the regex.
|
||||
RelabelDrop = "drop"
|
||||
RelabelDrop RelabelAction = "drop"
|
||||
// Sets a label to the modulus of a hash of labels.
|
||||
RelabelHashMod RelabelAction = "hashmod"
|
||||
)
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
|
@ -467,7 +469,7 @@ func (a *RelabelAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
return err
|
||||
}
|
||||
switch act := RelabelAction(strings.ToLower(s)); act {
|
||||
case RelabelReplace, RelabelKeep, RelabelDrop:
|
||||
case RelabelReplace, RelabelKeep, RelabelDrop, RelabelHashMod:
|
||||
*a = act
|
||||
return nil
|
||||
}
|
||||
|
@ -482,7 +484,9 @@ type RelabelConfig struct {
|
|||
// Separator is the string between concatenated values from the source labels.
|
||||
Separator string `yaml:"separator,omitempty"`
|
||||
// Regex against which the concatenation is matched.
|
||||
Regex *Regexp `yaml:"regex"`
|
||||
Regex *Regexp `yaml:"regex",omitempty`
|
||||
// Modulus to take of the hash of concatenated values from the source labels.
|
||||
Modulus uint64 `yaml:"modulus,omitempty"`
|
||||
// The label to which the resulting string is written in a replacement.
|
||||
TargetLabel clientmodel.LabelName `yaml:"target_label,omitempty"`
|
||||
// Replacement is the regex replacement pattern to be used.
|
||||
|
@ -501,9 +505,12 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Regex == nil {
|
||||
if c.Regex == nil && c.Action != RelabelHashMod {
|
||||
return fmt.Errorf("relabel configuration requires a regular expression")
|
||||
}
|
||||
if c.Modulus == 0 && c.Action == RelabelHashMod {
|
||||
return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus")
|
||||
}
|
||||
return checkOverflow(c.XXX, "relabel_config")
|
||||
}
|
||||
|
||||
|
@ -527,8 +534,11 @@ func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (re Regexp) MarshalYAML() (interface{}, error) {
|
||||
return re.String(), nil
|
||||
func (re *Regexp) MarshalYAML() (interface{}, error) {
|
||||
if re != nil {
|
||||
return re.String(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Duration encapsulates a time.Duration and makes it YAML marshallable.
|
||||
|
|
|
@ -114,6 +114,19 @@ var expectedConf = &Config{
|
|||
Separator: ";",
|
||||
Action: RelabelDrop,
|
||||
},
|
||||
{
|
||||
SourceLabels: clientmodel.LabelNames{"__address__"},
|
||||
TargetLabel: "__hash",
|
||||
Modulus: 8,
|
||||
Separator: ";",
|
||||
Action: RelabelHashMod,
|
||||
},
|
||||
{
|
||||
SourceLabels: clientmodel.LabelNames{"__hash"},
|
||||
Regex: &Regexp{*regexp.MustCompile("^1$")},
|
||||
Separator: ";",
|
||||
Action: RelabelKeep,
|
||||
},
|
||||
},
|
||||
MetricRelabelConfigs: []*RelabelConfig{
|
||||
{
|
||||
|
@ -194,6 +207,9 @@ var expectedErrors = []struct {
|
|||
}, {
|
||||
filename: "regex_missing.bad.yml",
|
||||
errMsg: "relabel configuration requires a regular expression",
|
||||
}, {
|
||||
filename: "modulus_missing.bad.yml",
|
||||
errMsg: "relabel configuration for hashmod requires non-zero modulus",
|
||||
}, {
|
||||
filename: "rules.bad.yml",
|
||||
errMsg: "invalid rule file path",
|
||||
|
|
|
@ -71,6 +71,13 @@ scrape_configs:
|
|||
- source_labels: [job]
|
||||
regex: (.*)some-[regex]$
|
||||
action: drop
|
||||
- source_labels: [__address__]
|
||||
modulus: 8
|
||||
target_label: __hash
|
||||
action: hashmod
|
||||
- source_labels: [__hash]
|
||||
regex: ^1$
|
||||
action: keep
|
||||
|
||||
metric_relabel_configs:
|
||||
- source_labels: [__name__]
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
scrape_configs:
|
||||
- job_name: prometheus
|
||||
relabel_configs:
|
||||
- regex: abcdef
|
||||
action: hashmod
|
|
@ -2,6 +2,7 @@ package retrieval
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
|
@ -56,6 +57,11 @@ func relabel(labels clientmodel.LabelSet, cfg *config.RelabelConfig) (clientmode
|
|||
} else {
|
||||
labels[cfg.TargetLabel] = clientmodel.LabelValue(res)
|
||||
}
|
||||
case config.RelabelHashMod:
|
||||
hasher := fnv.New64a()
|
||||
hasher.Write([]byte(val))
|
||||
mod := hasher.Sum64() % cfg.Modulus
|
||||
labels[cfg.TargetLabel] = clientmodel.LabelValue(fmt.Sprintf("%d", mod))
|
||||
default:
|
||||
panic(fmt.Errorf("retrieval.relabel: unknown relabel action type %q", cfg.Action))
|
||||
}
|
||||
|
|
|
@ -151,6 +151,28 @@ func TestRelabel(t *testing.T) {
|
|||
"a": "boo",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: clientmodel.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
},
|
||||
relabel: []*config.RelabelConfig{
|
||||
{
|
||||
SourceLabels: clientmodel.LabelNames{"c"},
|
||||
TargetLabel: clientmodel.LabelName("d"),
|
||||
Separator: ";",
|
||||
Action: config.RelabelHashMod,
|
||||
Modulus: 1000,
|
||||
},
|
||||
},
|
||||
output: clientmodel.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
"d": "58",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
|
Loading…
Reference in New Issue