@@ -133,7 +133,7 @@ func (p *GraphiteParser) Parse(buf []byte) ([]telegraf.Metric, error) {
|
||||
}
|
||||
|
||||
if errStr != "" {
|
||||
return metrics, fmt.Errorf(errStr)
|
||||
return metrics, fmt.Errorf(strings.TrimSpace(errStr))
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
@@ -267,13 +267,13 @@ func (t *template) Apply(line string) (string, map[string]string, string, error)
|
||||
fields := strings.Split(line, ".")
|
||||
var (
|
||||
measurement []string
|
||||
tags = make(map[string]string)
|
||||
tags = make(map[string][]string)
|
||||
field []string
|
||||
)
|
||||
|
||||
// Set any default tags
|
||||
for k, v := range t.defaultTags {
|
||||
tags[k] = v
|
||||
tags[k] = append(tags[k], v)
|
||||
}
|
||||
|
||||
// See if an invalid combination has been specified in the template:
|
||||
@@ -285,30 +285,43 @@ func (t *template) Apply(line string) (string, map[string]string, string, error)
|
||||
}
|
||||
}
|
||||
if t.greedyField && t.greedyMeasurement {
|
||||
return "", nil, "", fmt.Errorf("either 'field*' or 'measurement*' can be used in each template (but not both together): %q", strings.Join(t.tags, t.separator))
|
||||
return "", nil, "",
|
||||
fmt.Errorf("either 'field*' or 'measurement*' can be used in each "+
|
||||
"template (but not both together): %q",
|
||||
strings.Join(t.tags, t.separator))
|
||||
}
|
||||
|
||||
for i, tag := range t.tags {
|
||||
if i >= len(fields) {
|
||||
continue
|
||||
}
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if tag == "measurement" {
|
||||
switch tag {
|
||||
case "measurement":
|
||||
measurement = append(measurement, fields[i])
|
||||
} else if tag == "field" {
|
||||
case "field":
|
||||
field = append(field, fields[i])
|
||||
} else if tag == "field*" {
|
||||
case "field*":
|
||||
field = append(field, fields[i:]...)
|
||||
break
|
||||
} else if tag == "measurement*" {
|
||||
case "measurement*":
|
||||
measurement = append(measurement, fields[i:]...)
|
||||
break
|
||||
} else if tag != "" {
|
||||
tags[tag] = fields[i]
|
||||
default:
|
||||
tags[tag] = append(tags[tag], fields[i])
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(measurement, t.separator), tags, strings.Join(field, t.separator), nil
|
||||
// Convert to map of strings.
|
||||
outtags := make(map[string]string)
|
||||
for k, values := range tags {
|
||||
outtags[k] = strings.Join(values, t.separator)
|
||||
}
|
||||
|
||||
return strings.Join(measurement, t.separator), outtags, strings.Join(field, t.separator), nil
|
||||
}
|
||||
|
||||
// matcher determines which template should be applied to a given metric
|
||||
|
||||
@@ -61,6 +61,13 @@ func TestTemplateApply(t *testing.T) {
|
||||
measurement: "cpu",
|
||||
tags: map[string]string{"hostname": "server01", "region": "us-west"},
|
||||
},
|
||||
{
|
||||
test: "metric with multiple tags",
|
||||
input: "server01.example.org.cpu.us-west",
|
||||
template: "hostname.hostname.hostname.measurement.region",
|
||||
measurement: "cpu",
|
||||
tags: map[string]string{"hostname": "server01.example.org", "region": "us-west"},
|
||||
},
|
||||
{
|
||||
test: "no metric",
|
||||
tags: make(map[string]string),
|
||||
@@ -142,7 +149,7 @@ func TestParseMissingMeasurement(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
func TestParseLine(t *testing.T) {
|
||||
testTime := time.Now().Round(time.Second)
|
||||
epochTime := testTime.Unix()
|
||||
strTime := strconv.FormatInt(epochTime, 10)
|
||||
@@ -243,6 +250,107 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
testTime := time.Now().Round(time.Second)
|
||||
epochTime := testTime.Unix()
|
||||
strTime := strconv.FormatInt(epochTime, 10)
|
||||
|
||||
var tests = []struct {
|
||||
test string
|
||||
input []byte
|
||||
measurement string
|
||||
tags map[string]string
|
||||
value float64
|
||||
time time.Time
|
||||
template string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
test: "normal case",
|
||||
input: []byte(`cpu.foo.bar 50 ` + strTime),
|
||||
template: "measurement.foo.bar",
|
||||
measurement: "cpu",
|
||||
tags: map[string]string{
|
||||
"foo": "foo",
|
||||
"bar": "bar",
|
||||
},
|
||||
value: 50,
|
||||
time: testTime,
|
||||
},
|
||||
{
|
||||
test: "metric only with float value",
|
||||
input: []byte(`cpu 50.554 ` + strTime),
|
||||
measurement: "cpu",
|
||||
template: "measurement",
|
||||
value: 50.554,
|
||||
time: testTime,
|
||||
},
|
||||
{
|
||||
test: "missing metric",
|
||||
input: []byte(`1419972457825`),
|
||||
template: "measurement",
|
||||
err: `received "1419972457825" which doesn't have required fields`,
|
||||
},
|
||||
{
|
||||
test: "should error parsing invalid float",
|
||||
input: []byte(`cpu 50.554z 1419972457825`),
|
||||
template: "measurement",
|
||||
err: `field "cpu" value: strconv.ParseFloat: parsing "50.554z": invalid syntax`,
|
||||
},
|
||||
{
|
||||
test: "should error parsing invalid int",
|
||||
input: []byte(`cpu 50z 1419972457825`),
|
||||
template: "measurement",
|
||||
err: `field "cpu" value: strconv.ParseFloat: parsing "50z": invalid syntax`,
|
||||
},
|
||||
{
|
||||
test: "should error parsing invalid time",
|
||||
input: []byte(`cpu 50.554 14199724z57825`),
|
||||
template: "measurement",
|
||||
err: `field "cpu" time: strconv.ParseFloat: parsing "14199724z57825": invalid syntax`,
|
||||
},
|
||||
{
|
||||
test: "measurement* and field* (invalid)",
|
||||
input: []byte(`prod.us-west.server01.cpu.util.idle.percent 99.99 1419972457825`),
|
||||
template: "env.zone.host.measurement*.field*",
|
||||
err: `either 'field*' or 'measurement*' can be used in each template (but not both together): "env.zone.host.measurement*.field*"`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
p, err := NewGraphiteParser("", []string{test.template}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating graphite parser: %v", err)
|
||||
}
|
||||
|
||||
metrics, err := p.Parse(test.input)
|
||||
if errstr(err) != test.err {
|
||||
t.Fatalf("err does not match. expected [%v], got [%v]", test.err, err)
|
||||
}
|
||||
if err != nil {
|
||||
// If we erred out,it was intended and the following tests won't work
|
||||
continue
|
||||
}
|
||||
if metrics[0].Name() != test.measurement {
|
||||
t.Fatalf("name parse failer. expected %v, got %v",
|
||||
test.measurement, metrics[0].Name())
|
||||
}
|
||||
if len(metrics[0].Tags()) != len(test.tags) {
|
||||
t.Fatalf("tags len mismatch. expected %d, got %d",
|
||||
len(test.tags), len(metrics[0].Tags()))
|
||||
}
|
||||
f := metrics[0].Fields()["value"].(float64)
|
||||
if metrics[0].Fields()["value"] != f {
|
||||
t.Fatalf("floatValue value mismatch. expected %v, got %v",
|
||||
test.value, f)
|
||||
}
|
||||
if metrics[0].Time().UnixNano()/1000000 != test.time.UnixNano()/1000000 {
|
||||
t.Fatalf("time value mismatch. expected %v, got %v",
|
||||
test.time.UnixNano(), metrics[0].Time().UnixNano())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNaN(t *testing.T) {
|
||||
p, err := NewGraphiteParser("", []string{"measurement*"}, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user