2015-02-18 21:40:55 +00:00
|
|
|
// Copyright 2013 The Prometheus Authors
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
2013-07-05 17:31:53 +00:00
|
|
|
//
|
2015-02-18 21:40:55 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2013-07-05 17:31:53 +00:00
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
package mapper
|
2013-07-05 17:31:53 +00:00
|
|
|
|
|
|
|
import (
|
2018-07-19 21:57:36 +00:00
|
|
|
"bufio"
|
2013-07-05 17:31:53 +00:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2018-07-19 21:57:36 +00:00
|
|
|
"os"
|
2013-07-05 17:31:53 +00:00
|
|
|
"regexp"
|
2018-07-19 21:57:36 +00:00
|
|
|
"strconv"
|
2013-07-05 17:31:53 +00:00
|
|
|
"strings"
|
2013-07-08 18:37:12 +00:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2018-07-19 21:57:36 +00:00
|
|
|
"github.com/prometheus/common/log"
|
2017-03-13 01:11:05 +00:00
|
|
|
yaml "gopkg.in/yaml.v2"
|
2013-07-05 17:31:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-01-16 13:16:15 +00:00
|
|
|
statsdMetricRE = `[a-zA-Z_](-?[a-zA-Z0-9_])+`
|
|
|
|
templateReplaceRE = `(\$\{?\d+\}?)`
|
2015-10-09 22:03:21 +00:00
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
metricLineRE = regexp.MustCompile(`^(\*\.|` + statsdMetricRE + `\.)+(\*|` + statsdMetricRE + `)$`)
|
|
|
|
metricNameRE = regexp.MustCompile(`^([a-zA-Z_]|` + templateReplaceRE + `)([a-zA-Z0-9_]|` + templateReplaceRE + `)*$`)
|
|
|
|
labelNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]+$`)
|
|
|
|
|
|
|
|
templateReplaceCaptureRE = regexp.MustCompile(`\$\{?([a-zA-Z0-9_\$]+)\}?`)
|
2013-07-05 17:31:53 +00:00
|
|
|
)
|
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
type mapperConfigDefaults struct {
|
2018-09-12 00:31:26 +00:00
|
|
|
TimerType TimerType `yaml:"timer_type"`
|
|
|
|
Buckets []float64 `yaml:"buckets"`
|
|
|
|
Quantiles []metricObjective `yaml:"quantiles"`
|
|
|
|
MatchType MatchType `yaml:"match_type"`
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type mappingState struct {
|
2018-09-12 00:31:26 +00:00
|
|
|
transitionsMap map[string]*mappingState
|
|
|
|
transitionsArray []*mappingState
|
2018-07-19 21:57:36 +00:00
|
|
|
// result is nil unless there's a metric ends with this state
|
|
|
|
result *MetricMapping
|
2013-07-05 17:31:53 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
type MetricMapper struct {
|
2017-03-13 01:11:05 +00:00
|
|
|
Defaults mapperConfigDefaults `yaml:"defaults"`
|
2018-08-10 12:28:38 +00:00
|
|
|
Mappings []MetricMapping `yaml:"mappings"`
|
2018-07-19 21:57:36 +00:00
|
|
|
FSM *mappingState
|
2018-09-12 00:31:26 +00:00
|
|
|
// if this is true, that means at least one matching rule is regex type
|
|
|
|
doRegex bool
|
|
|
|
dumpFSMPath string
|
|
|
|
mutex sync.Mutex
|
2018-08-14 09:31:38 +00:00
|
|
|
|
|
|
|
MappingsCount prometheus.Gauge
|
2013-07-05 17:31:53 +00:00
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
type templateFormatter struct {
|
|
|
|
captureIndexes []int
|
|
|
|
captureCount int
|
|
|
|
fmtString string
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
|
|
|
|
2018-06-14 06:34:44 +00:00
|
|
|
type matchMetricType string
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
type MetricMapping struct {
|
2018-06-14 06:34:44 +00:00
|
|
|
Match string `yaml:"match"`
|
|
|
|
Name string `yaml:"name"`
|
2018-09-12 00:31:26 +00:00
|
|
|
NameFormatter templateFormatter
|
2018-06-14 06:34:44 +00:00
|
|
|
regex *regexp.Regexp
|
|
|
|
Labels prometheus.Labels `yaml:"labels"`
|
2018-09-12 00:31:26 +00:00
|
|
|
LabelsFormatter map[string]templateFormatter
|
2018-08-10 12:28:38 +00:00
|
|
|
TimerType TimerType `yaml:"timer_type"`
|
2018-06-14 06:34:44 +00:00
|
|
|
Buckets []float64 `yaml:"buckets"`
|
2018-08-09 02:41:41 +00:00
|
|
|
Quantiles []metricObjective `yaml:"quantiles"`
|
2018-08-10 12:28:38 +00:00
|
|
|
MatchType MatchType `yaml:"match_type"`
|
2018-06-14 06:34:44 +00:00
|
|
|
HelpText string `yaml:"help"`
|
2018-08-10 12:28:38 +00:00
|
|
|
Action ActionType `yaml:"action"`
|
|
|
|
MatchMetricType MetricType `yaml:"match_metric_type"`
|
2017-03-13 01:11:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 02:41:41 +00:00
|
|
|
type metricObjective struct {
|
|
|
|
Quantile float64 `yaml:"quantile"`
|
|
|
|
Error float64 `yaml:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var defaultQuantiles = []metricObjective{
|
|
|
|
{Quantile: 0.5, Error: 0.05},
|
|
|
|
{Quantile: 0.9, Error: 0.01},
|
|
|
|
{Quantile: 0.99, Error: 0.001},
|
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
func generateFormatter(valueExpr string, captureCount int) (templateFormatter, error) {
|
|
|
|
matches := templateReplaceCaptureRE.FindAllStringSubmatch(valueExpr, -1)
|
|
|
|
if len(matches) == 0 {
|
|
|
|
// if no regex reference found, keep it as it is
|
|
|
|
return templateFormatter{captureCount: 0, fmtString: valueExpr}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var indexes []int
|
|
|
|
valueFormatter := valueExpr
|
|
|
|
for _, match := range matches {
|
|
|
|
idx, err := strconv.Atoi(match[len(match)-1])
|
|
|
|
if err != nil || idx > captureCount || idx < 1 {
|
|
|
|
// if index larger than captured count or using unsupported named capture group,
|
|
|
|
// replace with empty string
|
|
|
|
valueFormatter = strings.Replace(valueFormatter, match[0], "", -1)
|
|
|
|
} else {
|
|
|
|
valueFormatter = strings.Replace(valueFormatter, match[0], "%s", -1)
|
|
|
|
// note: the regex reference variable $? starts from 1
|
|
|
|
indexes = append(indexes, idx-1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return templateFormatter{
|
|
|
|
captureIndexes: indexes,
|
|
|
|
captureCount: len(indexes),
|
|
|
|
fmtString: valueFormatter,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatTemplate(formatter templateFormatter, captures map[int]string) string {
|
|
|
|
if formatter.captureCount == 0 {
|
|
|
|
// no label substitution, keep as it is
|
|
|
|
return formatter.fmtString
|
|
|
|
} else {
|
|
|
|
indexes := formatter.captureIndexes
|
|
|
|
vargs := make([]interface{}, formatter.captureCount)
|
|
|
|
for i, idx := range indexes {
|
|
|
|
vargs[i] = captures[idx]
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(formatter.fmtString, vargs...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
func (m *MetricMapper) InitFromYAMLString(fileContents string) error {
|
|
|
|
var n MetricMapper
|
2017-03-13 01:11:05 +00:00
|
|
|
|
|
|
|
if err := yaml.Unmarshal([]byte(fileContents), &n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.Defaults.Buckets == nil || len(n.Defaults.Buckets) == 0 {
|
|
|
|
n.Defaults.Buckets = prometheus.DefBuckets
|
|
|
|
}
|
|
|
|
|
2018-08-09 02:41:41 +00:00
|
|
|
if n.Defaults.Quantiles == nil || len(n.Defaults.Quantiles) == 0 {
|
|
|
|
n.Defaults.Quantiles = defaultQuantiles
|
|
|
|
}
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
if n.Defaults.MatchType == MatchTypeDefault {
|
|
|
|
n.Defaults.MatchType = MatchTypeGlob
|
2017-09-29 07:57:17 +00:00
|
|
|
}
|
|
|
|
|
2018-07-19 21:57:36 +00:00
|
|
|
maxPossibleTransitions := len(n.Mappings)
|
|
|
|
|
|
|
|
n.FSM = &mappingState{}
|
2018-09-12 00:31:26 +00:00
|
|
|
n.FSM.transitionsMap = make(map[string]*mappingState, maxPossibleTransitions)
|
2018-07-19 21:57:36 +00:00
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
for i := range n.Mappings {
|
2018-07-19 21:57:36 +00:00
|
|
|
maxPossibleTransitions--
|
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
currentMapping := &n.Mappings[i]
|
|
|
|
|
2017-09-29 07:57:17 +00:00
|
|
|
// check that label is correct
|
2017-11-13 12:21:29 +00:00
|
|
|
for k := range currentMapping.Labels {
|
2018-01-16 13:16:15 +00:00
|
|
|
if !labelNameRE.MatchString(k) {
|
2017-08-01 11:07:44 +00:00
|
|
|
return fmt.Errorf("invalid label key: %s", k)
|
2017-07-27 20:58:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-13 12:21:29 +00:00
|
|
|
if currentMapping.Name == "" {
|
2017-08-01 11:01:06 +00:00
|
|
|
return fmt.Errorf("line %d: metric mapping didn't set a metric name", i)
|
2017-07-27 20:58:10 +00:00
|
|
|
}
|
2017-11-13 12:21:29 +00:00
|
|
|
|
|
|
|
if !metricNameRE.MatchString(currentMapping.Name) {
|
|
|
|
return fmt.Errorf("metric name '%s' doesn't match regex '%s'", currentMapping.Name, metricNameRE)
|
|
|
|
}
|
|
|
|
|
2017-09-29 07:57:17 +00:00
|
|
|
if currentMapping.MatchType == "" {
|
|
|
|
currentMapping.MatchType = n.Defaults.MatchType
|
|
|
|
}
|
2017-07-27 20:58:10 +00:00
|
|
|
|
2018-01-02 22:21:50 +00:00
|
|
|
if currentMapping.Action == "" {
|
2018-08-10 12:28:38 +00:00
|
|
|
currentMapping.Action = ActionTypeMap
|
2018-01-02 22:21:50 +00:00
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
if currentMapping.MatchType == MatchTypeGlob {
|
|
|
|
if !metricLineRE.MatchString(currentMapping.Match) {
|
|
|
|
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
|
|
|
}
|
|
|
|
|
2018-07-19 21:57:36 +00:00
|
|
|
// first split by "."
|
|
|
|
matchFields := strings.Split(currentMapping.Match, ".")
|
|
|
|
// fill into our FSM
|
|
|
|
root := n.FSM
|
|
|
|
captureCount := 0
|
|
|
|
for i, field := range matchFields {
|
2018-09-12 00:31:26 +00:00
|
|
|
state, prs := root.transitionsMap[field]
|
2018-07-19 21:57:36 +00:00
|
|
|
if !prs {
|
|
|
|
state = &mappingState{}
|
2018-09-12 00:31:26 +00:00
|
|
|
(*state).transitionsMap = make(map[string]*mappingState, maxPossibleTransitions)
|
|
|
|
root.transitionsMap[field] = state
|
2018-07-19 21:57:36 +00:00
|
|
|
// if this is last field, set result to currentMapping instance
|
|
|
|
if i == len(matchFields)-1 {
|
2018-09-12 00:31:26 +00:00
|
|
|
root.transitionsMap[field].result = currentMapping
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if field == "*" {
|
|
|
|
captureCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
// goto next state
|
|
|
|
root = state
|
|
|
|
}
|
2018-09-12 00:31:26 +00:00
|
|
|
nameFmt, err := generateFormatter(currentMapping.Name, captureCount)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
currentMapping.NameFormatter = nameFmt
|
|
|
|
|
|
|
|
currentLabelFormatter := make(map[string]templateFormatter, captureCount)
|
2018-07-19 21:57:36 +00:00
|
|
|
for label, valueExpr := range currentMapping.Labels {
|
2018-09-12 00:31:26 +00:00
|
|
|
lblFmt, err := generateFormatter(valueExpr, captureCount)
|
2018-07-19 21:57:36 +00:00
|
|
|
if err != nil {
|
2018-09-12 00:31:26 +00:00
|
|
|
return err
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
2018-09-12 00:31:26 +00:00
|
|
|
currentLabelFormatter[label] = lblFmt
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
|
|
|
currentMapping.LabelsFormatter = currentLabelFormatter
|
2018-09-12 00:31:26 +00:00
|
|
|
} else {
|
|
|
|
if regex, err := regexp.Compile(currentMapping.Match); err != nil {
|
|
|
|
return fmt.Errorf("invalid regex %s in mapping: %v", currentMapping.Match, err)
|
|
|
|
} else {
|
|
|
|
currentMapping.regex = regex
|
|
|
|
}
|
|
|
|
m.doRegex = true
|
|
|
|
} /*else if currentMapping.MatchType == MatchTypeGlob {
|
2017-09-29 07:57:17 +00:00
|
|
|
if !metricLineRE.MatchString(currentMapping.Match) {
|
|
|
|
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
|
|
|
}
|
|
|
|
// Translate the glob-style metric match line into a proper regex that we
|
|
|
|
// can use to match metrics later on.
|
|
|
|
metricRe := strings.Replace(currentMapping.Match, ".", "\\.", -1)
|
2017-09-20 10:27:12 +00:00
|
|
|
metricRe = strings.Replace(metricRe, "*", "([^.]*)", -1)
|
2018-02-22 22:15:30 +00:00
|
|
|
if regex, err := regexp.Compile("^" + metricRe + "$"); err != nil {
|
|
|
|
return fmt.Errorf("invalid match %s. cannot compile regex in mapping: %v", currentMapping.Match, err)
|
|
|
|
} else {
|
|
|
|
currentMapping.regex = regex
|
|
|
|
}
|
2018-09-12 00:31:26 +00:00
|
|
|
} */
|
2017-03-13 01:11:05 +00:00
|
|
|
|
|
|
|
if currentMapping.TimerType == "" {
|
|
|
|
currentMapping.TimerType = n.Defaults.TimerType
|
|
|
|
}
|
|
|
|
|
|
|
|
if currentMapping.Buckets == nil || len(currentMapping.Buckets) == 0 {
|
|
|
|
currentMapping.Buckets = n.Defaults.Buckets
|
|
|
|
}
|
2017-07-27 20:58:10 +00:00
|
|
|
|
2018-08-09 02:41:41 +00:00
|
|
|
if currentMapping.Quantiles == nil || len(currentMapping.Quantiles) == 0 {
|
|
|
|
currentMapping.Quantiles = n.Defaults.Quantiles
|
|
|
|
}
|
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
|
|
|
|
|
|
|
m.Defaults = n.Defaults
|
|
|
|
m.Mappings = n.Mappings
|
2018-09-12 00:31:26 +00:00
|
|
|
if len(n.FSM.transitionsMap) > 0 || len(n.FSM.transitionsArray) > 0 {
|
2018-07-19 21:57:36 +00:00
|
|
|
m.FSM = n.FSM
|
2018-09-12 00:31:26 +00:00
|
|
|
if m.dumpFSMPath != "" {
|
|
|
|
dumpFSM(m.dumpFSMPath, m.FSM)
|
|
|
|
}
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
2017-03-13 01:11:05 +00:00
|
|
|
|
2018-08-14 09:31:38 +00:00
|
|
|
if m.MappingsCount != nil {
|
|
|
|
m.MappingsCount.Set(float64(len(n.Mappings)))
|
|
|
|
}
|
2017-03-13 01:11:05 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
func (m *MetricMapper) SetDumpFSMPath(path string) error {
|
|
|
|
m.dumpFSMPath = path
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func dumpFSM(fileName string, root *mappingState) {
|
2018-07-19 21:57:36 +00:00
|
|
|
log.Infoln("Start dumping FSM to", fileName)
|
|
|
|
idx := 0
|
|
|
|
states := make(map[int]*mappingState)
|
|
|
|
states[idx] = root
|
|
|
|
|
|
|
|
f, _ := os.Create(fileName)
|
|
|
|
w := bufio.NewWriter(f)
|
|
|
|
w.WriteString("digraph g {\n")
|
|
|
|
w.WriteString("rankdir=LR\n") // make it vertical
|
|
|
|
w.WriteString("node [ label=\"\",style=filled,fillcolor=white,shape=circle ]\n") // remove label of node
|
|
|
|
|
|
|
|
for idx < len(states) {
|
2018-09-12 00:31:26 +00:00
|
|
|
for field, transition := range states[idx].transitionsMap {
|
2018-07-19 21:57:36 +00:00
|
|
|
states[len(states)] = transition
|
|
|
|
w.WriteString(fmt.Sprintf("%d -> %d [label = \"%s\"];\n", idx, len(states)-1, field))
|
2018-09-12 00:31:26 +00:00
|
|
|
if transition.transitionsMap == nil || len(transition.transitionsMap) == 0 {
|
2018-07-19 21:57:36 +00:00
|
|
|
w.WriteString(fmt.Sprintf("%d [color=\"#82B366\",fillcolor=\"#D5E8D4\"];\n", len(states)-1))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx++
|
|
|
|
}
|
|
|
|
w.WriteString(fmt.Sprintf("0 [color=\"#D6B656\",fillcolor=\"#FFF2CC\"];\n"))
|
|
|
|
w.WriteString("}")
|
|
|
|
w.Flush()
|
|
|
|
log.Infoln("Finish dumping FSM")
|
|
|
|
}
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
func (m *MetricMapper) InitFromFile(fileName string) error {
|
2013-07-05 17:31:53 +00:00
|
|
|
mappingStr, err := ioutil.ReadFile(fileName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-10 12:28:38 +00:00
|
|
|
return m.InitFromYAMLString(string(mappingStr))
|
2013-07-05 17:31:53 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 12:28:38 +00:00
|
|
|
func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricType) (*MetricMapping, prometheus.Labels, bool) {
|
2018-09-12 00:31:26 +00:00
|
|
|
// glob matching
|
2018-07-19 21:57:36 +00:00
|
|
|
if root := m.FSM; root != nil {
|
|
|
|
matchFields := strings.Split(statsdMetric, ".")
|
|
|
|
captures := make(map[int]string, len(matchFields))
|
|
|
|
captureIdx := 0
|
|
|
|
filedsCount := len(matchFields)
|
|
|
|
for i, field := range matchFields {
|
2018-09-12 00:31:26 +00:00
|
|
|
if root.transitionsMap == nil {
|
2018-07-19 21:57:36 +00:00
|
|
|
break
|
|
|
|
}
|
2018-09-12 00:31:26 +00:00
|
|
|
state, prs := root.transitionsMap[field]
|
2018-07-19 21:57:36 +00:00
|
|
|
if !prs {
|
2018-09-12 00:31:26 +00:00
|
|
|
state, prs = root.transitionsMap["*"]
|
2018-07-19 21:57:36 +00:00
|
|
|
if !prs {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
captures[captureIdx] = field
|
|
|
|
captureIdx++
|
|
|
|
}
|
|
|
|
if state.result != nil && i == filedsCount-1 {
|
|
|
|
mapping := *state.result
|
2018-09-12 00:31:26 +00:00
|
|
|
state.result.Name = formatTemplate(mapping.NameFormatter, captures)
|
|
|
|
|
2018-07-19 21:57:36 +00:00
|
|
|
labels := prometheus.Labels{}
|
|
|
|
for label := range mapping.Labels {
|
2018-09-12 00:31:26 +00:00
|
|
|
labels[label] = formatTemplate(mapping.LabelsFormatter[label], captures)
|
2018-07-19 21:57:36 +00:00
|
|
|
}
|
|
|
|
return state.result, labels, true
|
|
|
|
}
|
|
|
|
root = state
|
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
// if there's no regex match type, return immediately
|
|
|
|
if !m.doRegex {
|
2018-07-19 21:57:36 +00:00
|
|
|
return nil, nil, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-12 00:31:26 +00:00
|
|
|
// regex matching
|
2013-07-08 18:37:12 +00:00
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
for _, mapping := range m.Mappings {
|
2018-09-12 00:31:26 +00:00
|
|
|
// if a rule don't have regex matching type, the regex field is unset
|
|
|
|
if mapping.regex == nil {
|
|
|
|
continue
|
|
|
|
}
|
2013-07-05 17:31:53 +00:00
|
|
|
matches := mapping.regex.FindStringSubmatchIndex(statsdMetric)
|
|
|
|
if len(matches) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-01-16 13:16:15 +00:00
|
|
|
mapping.Name = string(mapping.regex.ExpandString(
|
|
|
|
[]byte{},
|
|
|
|
mapping.Name,
|
|
|
|
statsdMetric,
|
|
|
|
matches,
|
|
|
|
))
|
|
|
|
|
2018-06-14 06:34:44 +00:00
|
|
|
if mt := mapping.MatchMetricType; mt != "" && mt != statsdMetricType {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-06-26 13:56:21 +00:00
|
|
|
labels := prometheus.Labels{}
|
2017-03-13 01:11:05 +00:00
|
|
|
for label, valueExpr := range mapping.Labels {
|
2013-07-05 17:31:53 +00:00
|
|
|
value := mapping.regex.ExpandString([]byte{}, valueExpr, statsdMetric, matches)
|
|
|
|
labels[label] = string(value)
|
|
|
|
}
|
2018-01-16 13:16:15 +00:00
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
return &mapping, labels, true
|
2013-07-05 17:31:53 +00:00
|
|
|
}
|
|
|
|
|
2017-03-13 01:11:05 +00:00
|
|
|
return nil, nil, false
|
2013-07-05 17:31:53 +00:00
|
|
|
}
|