Add option to use strict sanitization rules to wavefront output (#5664)

This commit is contained in:
Pierre Tessier 2019-04-02 14:47:25 -04:00 committed by Daniel Nelson
parent fb01b8ba28
commit ff81811720
3 changed files with 68 additions and 0 deletions

View File

@ -32,6 +32,10 @@ This plugin writes to a [Wavefront](https://www.wavefront.com) proxy, in Wavefro
## When true will convert all _ (underscore) characters in final metric name. default is true
#convert_paths = true
## Use Strict rules to sanitize metric and tag names from invalid characters
## When enabled forward slash (/) and comma (,) will be accpeted
#use_strict = false
## Use Regex to sanitize metric and tag names from invalid characters
## Regex is more thorough, but significantly slower. default is false
#use_regex = false

View File

@ -22,6 +22,7 @@ type Wavefront struct {
ConvertPaths bool
ConvertBool bool
UseRegex bool
UseStrict bool
SourceOverride []string
StringToNumber map[string][]map[string]float64
@ -37,6 +38,14 @@ var sanitizedChars = strings.NewReplacer(
"=", "-",
)
// catch many of the invalid chars that could appear in a metric or tag name
var strictSanitizedChars = strings.NewReplacer(
"!", "-", "@", "-", "#", "-", "$", "-", "%", "-", "^", "-", "&", "-",
"*", "-", "(", "-", ")", "-", "+", "-", "`", "-", "'", "-", "\"", "-",
"[", "-", "]", "-", "{", "-", "}", "-", ":", "-", ";", "-", "<", "-",
">", "-", "?", "-", "\\", "-", "|", "-", " ", "-", "=", "-",
)
// instead of Replacer which may miss some special characters we can use a regex pattern, but this is significantly slower than Replacer
var sanitizedRegex = regexp.MustCompile("[^a-zA-Z\\d_.-]")
@ -71,6 +80,10 @@ var sampleConfig = `
## When true will convert all _ (underscore) characters in final metric name. default is true
#convert_paths = true
## Use Strict rules to sanitize metric and tag names from invalid characters
## When enabled forward slash (/) and comma (,) will be accpeted
#use_strict = false
## Use Regex to sanitize metric and tag names from invalid characters
## Regex is more thorough, but significantly slower. default is false
#use_regex = false
@ -163,6 +176,8 @@ func buildMetrics(m telegraf.Metric, w *Wavefront) []*MetricPoint {
if w.UseRegex {
name = sanitizedRegex.ReplaceAllLiteralString(name, "-")
} else if w.UseStrict {
name = strictSanitizedChars.Replace(name)
} else {
name = sanitizedChars.Replace(name)
}
@ -238,6 +253,8 @@ func buildTags(mTags map[string]string, w *Wavefront) (string, map[string]string
var key string
if w.UseRegex {
key = sanitizedRegex.ReplaceAllLiteralString(k, "-")
} else if w.UseStrict {
key = strictSanitizedChars.Replace(k)
} else {
key = sanitizedChars.Replace(k)
}

View File

@ -50,6 +50,13 @@ func TestBuildMetrics(t *testing.T) {
{Metric: w.Prefix + "testing.metric2", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag1": "value1"}},
},
},
{
testutil.TestMetric(float64(1), "testing_just/another,metric:float", "metric2"),
[]MetricPoint{
{Metric: w.Prefix + "testing.just-another-metric-float", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag1": "value1"}},
{Metric: w.Prefix + "testing.metric2", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag1": "value1"}},
},
},
{
testMetric1,
[]MetricPoint{{Metric: w.Prefix + "test.simple.metric", Value: 123, Timestamp: timestamp, Source: "testHost", Tags: map[string]string{"tag1": "value1"}}},
@ -67,6 +74,46 @@ func TestBuildMetrics(t *testing.T) {
}
func TestBuildMetricsStrict(t *testing.T) {
w := defaultWavefront()
w.Prefix = "testthis."
w.UseStrict = true
pathReplacer = strings.NewReplacer("_", w.MetricSeparator)
var timestamp int64 = 1257894000
var metricTests = []struct {
metric telegraf.Metric
metricPoints []MetricPoint
}{
{
testutil.TestMetric(float64(1), "testing_just*a%metric:float", "metric2"),
[]MetricPoint{
{Metric: w.Prefix + "testing.just-a-metric-float", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag1": "value1"}},
{Metric: w.Prefix + "testing.metric2", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag1": "value1"}},
},
},
{
testutil.TestMetric(float64(1), "testing_just/another,metric:float", "metric2"),
[]MetricPoint{
{Metric: w.Prefix + "testing.just/another,metric-float", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag/1": "value1", "tag,2": "value2"}},
{Metric: w.Prefix + "testing.metric2", Value: 1, Timestamp: timestamp, Tags: map[string]string{"tag/1": "value1", "tag,2": "value2"}},
},
},
}
for _, mt := range metricTests {
ml := buildMetrics(mt.metric, w)
for i, line := range ml {
if mt.metricPoints[i].Metric != line.Metric || mt.metricPoints[i].Value != line.Value {
t.Errorf("\nexpected\t%+v %+v\nreceived\t%+v %+v\n", mt.metricPoints[i].Metric, mt.metricPoints[i].Value, line.Metric, line.Value)
}
}
}
}
func TestBuildMetricsWithSimpleFields(t *testing.T) {
w := defaultWavefront()
w.Prefix = "testthis."