Fix template pattern partial wildcard matching (#6135)

This commit is contained in:
George 2019-07-19 22:14:15 +02:00 committed by Daniel Nelson
parent 5c9923a20a
commit 77b1a43539
2 changed files with 80 additions and 13 deletions

View File

@ -3,6 +3,7 @@ package templating
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -20,3 +21,57 @@ func TestEngineAlternateSeparator(t *testing.T) {
}, tags) }, tags)
require.Equal(t, "", field) require.Equal(t, "", field)
} }
func TestEngineWithWildcardTemplate(t *testing.T) {
var (
defaultTmpl, err = NewDefaultTemplateWithPattern("measurement*")
templates = []string{
"taskmanagerTask.alarm-detector.Assign.alarmDefinitionId metricsType.process.nodeId.x.alarmDefinitionId.measurement.field rule=1",
"taskmanagerTask.*.*.*.* metricsType.process.nodeId.measurement rule=2",
}
)
require.NoError(t, err)
engine, err := NewEngine(".", defaultTmpl, templates)
require.NoError(t, err)
for _, testCase := range []struct {
line string
measurement string
field string
tags map[string]string
}{
{
line: "taskmanagerTask.alarm-detector.Assign.alarmDefinitionId.timeout_errors.duration.p75",
measurement: "duration",
field: "p75",
tags: map[string]string{
"metricsType": "taskmanagerTask",
"process": "alarm-detector",
"nodeId": "Assign",
"x": "alarmDefinitionId",
"alarmDefinitionId": "timeout_errors",
"rule": "1",
},
},
{
line: "taskmanagerTask.alarm-detector.Assign.numRecordsInPerSecond.m5_rate",
measurement: "numRecordsInPerSecond",
tags: map[string]string{
"metricsType": "taskmanagerTask",
"process": "alarm-detector",
"nodeId": "Assign",
"rule": "2",
},
},
} {
t.Run(testCase.line, func(t *testing.T) {
measurement, tags, field, err := engine.Apply(testCase.line)
require.NoError(t, err)
assert.Equal(t, testCase.measurement, measurement)
assert.Equal(t, testCase.field, field)
assert.Equal(t, testCase.tags, tags)
})
}
}

View File

@ -55,32 +55,44 @@ func (n *node) search(line string) *Template {
// recursiveSearch performs the actual recursive search // recursiveSearch performs the actual recursive search
func (n *node) recursiveSearch(lineParts []string) *Template { func (n *node) recursiveSearch(lineParts []string) *Template {
// Nothing to search // nothing to search
if len(lineParts) == 0 || len(n.children) == 0 { if len(lineParts) == 0 || len(n.children) == 0 {
return n.template return n.template
} }
// If last element is a wildcard, don't include it in this search since it's sorted var (
// to the end but lexicographically it would not always be and sort.Search assumes hasWildcard bool
// the slice is sorted. length = len(n.children)
length := len(n.children) )
if n.children[length-1].value == "*" {
// exclude last child from search if it is a wildcard. sort.Search expects
// a lexicographically sorted set of children and we have artificially sorted
// wildcards to the end of the child set
// wildcards will be searched seperately if no exact match is found
if hasWildcard = n.children[length-1].value == "*"; hasWildcard {
length-- length--
} }
// Find the index of child with an exact match
i := sort.Search(length, func(i int) bool { i := sort.Search(length, func(i int) bool {
return n.children[i].value >= lineParts[0] return n.children[i].value >= lineParts[0]
}) })
// Found an exact match, so search that child sub-tree // given an exact match is found within children set
if i < len(n.children) && n.children[i].value == lineParts[0] { if i < length && n.children[i].value == lineParts[0] {
return n.children[i].recursiveSearch(lineParts[1:]) // decend into the matching node
if tmpl := n.children[i].recursiveSearch(lineParts[1:]); tmpl != nil {
// given a template is found return it
return tmpl
} }
// Not an exact match, see if we have a wildcard child to search
if n.children[len(n.children)-1].value == "*" {
return n.children[len(n.children)-1].recursiveSearch(lineParts[1:])
} }
// given no template is found and the last child is a wildcard
if hasWildcard {
// also search the wildcard child node
return n.children[length].recursiveSearch(lineParts[1:])
}
// fallback to returning template at this node
return n.template return n.template
} }