forked from mirrors/statsd_exporter
promote formatLabels to mapper package
Signed-off-by: Wangchong Zhou <fffonion@gmail.com>
This commit is contained in:
parent
5262b2904c
commit
fcf11f02e5
3 changed files with 45 additions and 49 deletions
|
@ -24,17 +24,17 @@ var (
|
||||||
templateReplaceCaptureRE = regexp.MustCompile(`\$\{?([a-zA-Z0-9_\$]+)\}?`)
|
templateReplaceCaptureRE = regexp.MustCompile(`\$\{?([a-zA-Z0-9_\$]+)\}?`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type templateFormatter struct {
|
type TemplateFormatter struct {
|
||||||
captureIndexes []int
|
captureIndexes []int
|
||||||
captureCount int
|
captureCount int
|
||||||
fmtString string
|
fmtString string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTemplateFormatter(valueExpr string, captureCount int) *templateFormatter {
|
func NewTemplateFormatter(valueExpr string, captureCount int) *TemplateFormatter {
|
||||||
matches := templateReplaceCaptureRE.FindAllStringSubmatch(valueExpr, -1)
|
matches := templateReplaceCaptureRE.FindAllStringSubmatch(valueExpr, -1)
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
// if no regex reference found, keep it as it is
|
// if no regex reference found, keep it as it is
|
||||||
return &templateFormatter{captureCount: 0, fmtString: valueExpr}
|
return &TemplateFormatter{captureCount: 0, fmtString: valueExpr}
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexes []int
|
var indexes []int
|
||||||
|
@ -51,14 +51,14 @@ func newTemplateFormatter(valueExpr string, captureCount int) *templateFormatter
|
||||||
indexes = append(indexes, idx-1)
|
indexes = append(indexes, idx-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &templateFormatter{
|
return &TemplateFormatter{
|
||||||
captureIndexes: indexes,
|
captureIndexes: indexes,
|
||||||
captureCount: len(indexes),
|
captureCount: len(indexes),
|
||||||
fmtString: valueFormatter,
|
fmtString: valueFormatter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formatter *templateFormatter) format(captures map[int]string) string {
|
func (formatter *TemplateFormatter) Format(captures []string) string {
|
||||||
if formatter.captureCount == 0 {
|
if formatter.captureCount == 0 {
|
||||||
// no label substitution, keep as it is
|
// no label substitution, keep as it is
|
||||||
return formatter.fmtString
|
return formatter.fmtString
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/common/log"
|
"github.com/prometheus/common/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,10 +25,8 @@ type mappingState struct {
|
||||||
minRemainingLength int
|
minRemainingLength int
|
||||||
maxRemainingLength int
|
maxRemainingLength int
|
||||||
// result* members are nil unless there's a metric ends with this state
|
// result* members are nil unless there's a metric ends with this state
|
||||||
result interface{}
|
Result interface{}
|
||||||
resultPriority int
|
ResultPriority int
|
||||||
resultNameFormatter *templateFormatter
|
|
||||||
resultLabelsFormatter map[string]*templateFormatter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type fsmBacktrackStackCursor struct {
|
type fsmBacktrackStackCursor struct {
|
||||||
|
@ -69,8 +66,7 @@ func NewFSM(metricTypes []string, maxPossibleTransitions int, orderingDisabled b
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddState adds a state into the existing FSM
|
// AddState adds a state into the existing FSM
|
||||||
func (f *FSM) AddState(match string, name string, labels prometheus.Labels, matchMetricType string,
|
func (f *FSM) AddState(match string, name string, matchMetricType string, maxPossibleTransitions int, result interface{}) int {
|
||||||
maxPossibleTransitions int, result interface{}) {
|
|
||||||
// first split by "."
|
// first split by "."
|
||||||
matchFields := strings.Split(match, ".")
|
matchFields := strings.Split(match, ".")
|
||||||
// fill into our FSM
|
// fill into our FSM
|
||||||
|
@ -97,7 +93,7 @@ func (f *FSM) AddState(match string, name string, labels prometheus.Labels, matc
|
||||||
root.transitions[field] = state
|
root.transitions[field] = state
|
||||||
// if this is last field, set result to currentMapping instance
|
// if this is last field, set result to currentMapping instance
|
||||||
if i == len(matchFields)-1 {
|
if i == len(matchFields)-1 {
|
||||||
root.transitions[field].result = result
|
root.transitions[field].Result = result
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(*state).maxRemainingLength = max(len(matchFields)-i-1, (*state).maxRemainingLength)
|
(*state).maxRemainingLength = max(len(matchFields)-i-1, (*state).maxRemainingLength)
|
||||||
|
@ -112,26 +108,18 @@ func (f *FSM) AddState(match string, name string, labels prometheus.Labels, matc
|
||||||
}
|
}
|
||||||
finalStates = append(finalStates, root)
|
finalStates = append(finalStates, root)
|
||||||
}
|
}
|
||||||
nameFmt := newTemplateFormatter(name, captureCount)
|
|
||||||
|
|
||||||
currentLabelFormatter := make(map[string]*templateFormatter, captureCount)
|
|
||||||
for label, valueExpr := range labels {
|
|
||||||
lblFmt := newTemplateFormatter(valueExpr, captureCount)
|
|
||||||
currentLabelFormatter[label] = lblFmt
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, state := range finalStates {
|
for _, state := range finalStates {
|
||||||
state.resultNameFormatter = nameFmt
|
state.ResultPriority = f.statesCount
|
||||||
state.resultLabelsFormatter = currentLabelFormatter
|
|
||||||
state.resultPriority = f.statesCount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.statesCount++
|
f.statesCount++
|
||||||
|
|
||||||
|
return captureCount
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMapping implements a mapping algorithm for Glob pattern
|
// GetMapping implements a mapping algorithm for Glob pattern
|
||||||
func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (interface{}, string, prometheus.Labels, bool) {
|
func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (*mappingState, []string) {
|
||||||
matchFields := strings.Split(statsdMetric, ".")
|
matchFields := strings.Split(statsdMetric, ".")
|
||||||
currentState := f.root.transitions[statsdMetricType]
|
currentState := f.root.transitions[statsdMetricType]
|
||||||
|
|
||||||
|
@ -142,7 +130,7 @@ func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (interfac
|
||||||
// the return variable
|
// the return variable
|
||||||
var finalState *mappingState
|
var finalState *mappingState
|
||||||
|
|
||||||
captures := make(map[int]string, len(matchFields))
|
captures := make([]string, len(matchFields))
|
||||||
// keep track of captured group so we don't need to do append() on captures
|
// keep track of captured group so we don't need to do append() on captures
|
||||||
captureIdx := 0
|
captureIdx := 0
|
||||||
filedsCount := len(matchFields)
|
filedsCount := len(matchFields)
|
||||||
|
@ -191,11 +179,11 @@ func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (interfac
|
||||||
} // backtrack will resume from here
|
} // backtrack will resume from here
|
||||||
|
|
||||||
// do we reach a final state?
|
// do we reach a final state?
|
||||||
if state.result != nil && i == filedsCount-1 {
|
if state.Result != nil && i == filedsCount-1 {
|
||||||
if f.OrderingDisabled {
|
if f.OrderingDisabled {
|
||||||
finalState = state
|
finalState = state
|
||||||
return formatLabels(finalState, captures)
|
return finalState, captures
|
||||||
} else if finalState == nil || finalState.resultPriority > state.resultPriority {
|
} else if finalState == nil || finalState.ResultPriority > state.ResultPriority {
|
||||||
// if we care about ordering, try to find a result with highest prioity
|
// if we care about ordering, try to find a result with highest prioity
|
||||||
finalState = state
|
finalState = state
|
||||||
}
|
}
|
||||||
|
@ -230,7 +218,7 @@ func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (interfac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatLabels(finalState, captures)
|
return finalState, captures
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestIfNeedBacktracking test if backtrack is needed for current mappings
|
// TestIfNeedBacktracking test if backtrack is needed for current mappings
|
||||||
|
@ -323,17 +311,3 @@ func TestIfNeedBacktracking(mappings []string, orderingDisabled bool) bool {
|
||||||
|
|
||||||
return !orderingDisabled || backtrackingNeeded
|
return !orderingDisabled || backtrackingNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatLabels(finalState *mappingState, captures map[int]string) (interface{}, string, prometheus.Labels, bool) {
|
|
||||||
// format name and labels
|
|
||||||
if finalState != nil {
|
|
||||||
name := finalState.resultNameFormatter.format(captures)
|
|
||||||
|
|
||||||
labels := prometheus.Labels{}
|
|
||||||
for key, formatter := range finalState.resultLabelsFormatter {
|
|
||||||
labels[key] = formatter.format(captures)
|
|
||||||
}
|
|
||||||
return finalState.result, name, labels, true
|
|
||||||
}
|
|
||||||
return nil, "", nil, false
|
|
||||||
}
|
|
||||||
|
|
|
@ -57,8 +57,11 @@ type matchMetricType string
|
||||||
type MetricMapping struct {
|
type MetricMapping struct {
|
||||||
Match string `yaml:"match"`
|
Match string `yaml:"match"`
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
nameFormatter *fsm.TemplateFormatter
|
||||||
regex *regexp.Regexp
|
regex *regexp.Regexp
|
||||||
Labels prometheus.Labels `yaml:"labels"`
|
Labels prometheus.Labels `yaml:"labels"`
|
||||||
|
labelKeys []string
|
||||||
|
labelFormatters []*fsm.TemplateFormatter
|
||||||
TimerType TimerType `yaml:"timer_type"`
|
TimerType TimerType `yaml:"timer_type"`
|
||||||
Buckets []float64 `yaml:"buckets"`
|
Buckets []float64 `yaml:"buckets"`
|
||||||
Quantiles []metricObjective `yaml:"quantiles"`
|
Quantiles []metricObjective `yaml:"quantiles"`
|
||||||
|
@ -137,9 +140,22 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string) error {
|
||||||
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.FSM.AddState(currentMapping.Match, currentMapping.Name, currentMapping.Labels, string(currentMapping.MatchMetricType),
|
captureCount := n.FSM.AddState(currentMapping.Match, currentMapping.Name, string(currentMapping.MatchMetricType),
|
||||||
remainingMappingsCount, currentMapping)
|
remainingMappingsCount, currentMapping)
|
||||||
|
|
||||||
|
currentMapping.nameFormatter = fsm.NewTemplateFormatter(currentMapping.Name, captureCount)
|
||||||
|
|
||||||
|
labelKeys := make([]string, len(currentMapping.Labels))
|
||||||
|
labelFormatters := make([]*fsm.TemplateFormatter, len(currentMapping.Labels))
|
||||||
|
labelIndex := 0
|
||||||
|
for label, valueExpr := range currentMapping.Labels {
|
||||||
|
labelKeys[labelIndex] = label
|
||||||
|
labelFormatters[labelIndex] = fsm.NewTemplateFormatter(valueExpr, captureCount)
|
||||||
|
labelIndex++
|
||||||
|
}
|
||||||
|
currentMapping.labelFormatters = labelFormatters
|
||||||
|
currentMapping.labelKeys = labelKeys
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if regex, err := regexp.Compile(currentMapping.Match); err != nil {
|
if regex, err := regexp.Compile(currentMapping.Match); err != nil {
|
||||||
return fmt.Errorf("invalid regex %s in mapping: %v", currentMapping.Match, err)
|
return fmt.Errorf("invalid regex %s in mapping: %v", currentMapping.Match, err)
|
||||||
|
@ -200,11 +216,17 @@ func (m *MetricMapper) InitFromFile(fileName string) error {
|
||||||
func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricType) (*MetricMapping, prometheus.Labels, bool) {
|
func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricType) (*MetricMapping, prometheus.Labels, bool) {
|
||||||
// glob matching
|
// glob matching
|
||||||
if m.doFSM {
|
if m.doFSM {
|
||||||
mapping, mappingName, labels, matched := m.FSM.GetMapping(statsdMetric, string(statsdMetricType))
|
finalState, captures := m.FSM.GetMapping(statsdMetric, string(statsdMetricType))
|
||||||
if matched {
|
if finalState != nil && finalState.Result != nil {
|
||||||
mapping.(*MetricMapping).Name = mappingName
|
result := finalState.Result.(*MetricMapping)
|
||||||
return mapping.(*MetricMapping), labels, matched
|
result.Name = result.nameFormatter.Format(captures)
|
||||||
} else if !matched && !m.doRegex {
|
|
||||||
|
labels := prometheus.Labels{}
|
||||||
|
for index, formatter := range result.labelFormatters {
|
||||||
|
labels[result.labelKeys[index]] = formatter.Format(captures)
|
||||||
|
}
|
||||||
|
return result, labels, true
|
||||||
|
} else if !m.doRegex {
|
||||||
// if there's no regex match type, return immediately
|
// if there's no regex match type, return immediately
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue