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:
Brian Brazil 2015-06-24 08:07:17 +01:00
parent 30fc04b240
commit 682f949ab1
6 changed files with 73 additions and 7 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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__]

View File

@ -0,0 +1,5 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- regex: abcdef
action: hashmod

View File

@ -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))
}

View File

@ -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 {