Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding conjoined-field-names for graphite templates #5562

Merged
merged 10 commits into from
Feb 18, 2016
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [#5598](https://github.com/influxdata/influxdb/pull/5598): Client: Add Ping to v2 client @PSUdaemon
- [#4125](https://github.com/influxdata/influxdb/pull/4125): Admin UI: Fetch and display server version on connect. Thanks @alexiri!
- [#5602](https://github.com/influxdata/influxdb/pull/5602): Simplify cluster startup for scripting and deployment
- [#5562](https://github.com/influxdata/influxdb/pull/5562): Graphite: Support matching fields multiple times (@chrusty)
- [#5666](https://github.com/influxdata/influxdb/pull/5666): Manage dependencies with gdm
- [#5512](https://github.com/influxdata/influxdb/pull/5512): HTTP: Add config option to enable HTTP JSON write path which is now disabled by default.

Expand Down
2 changes: 2 additions & 0 deletions services/graphite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Additional tags can be added to a metric that don't exist on the received metric

A field key can be specified by using the keyword _field_. By default if no _field_ keyword is specified then the metric will be written to a field named _value_.

The field key can also be derived from the second "half" of the input metric-name by specifying ```field*``` (eg ```measurement.measurement.field*```). This cannot be used in conjunction with "measurement*"!

When using the current default engine _BZ1_, it's recommended to use a single field per value for performance reasons.

When using the _TSM1_ engine it's possible to amend measurement metrics with additional fields, e.g:
Expand Down
23 changes: 20 additions & 3 deletions services/graphite/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,16 +215,30 @@ func NewTemplate(pattern string, defaultTags models.Tags, separator string) (*te
func (t *template) Apply(line string) (string, map[string]string, string, error) {
fields := strings.Split(line, ".")
var (
measurement []string
tags = make(map[string][]string)
field string
measurement []string
tags = make(map[string][]string)
field string
hasFieldWildcard = false
hasMeasurementWildcard = false
)

// Set any default tags
for k, v := range t.defaultTags {
tags[k] = append(tags[k], v)
}

// See if an invalid combination has been specified in the template:
for _, tag := range t.tags {
if tag == "measurement*" {
hasMeasurementWildcard = true
} else if tag == "field*" {
hasFieldWildcard = true
}
}
if hasFieldWildcard && hasMeasurementWildcard {
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
Expand All @@ -237,6 +251,9 @@ func (t *template) Apply(line string) (string, map[string]string, string, error)
return "", nil, "", fmt.Errorf("'field' can only be used once in each template: %q", line)
}
field = fields[i]
} else if tag == "field*" {
field = strings.Join(fields[i:], t.separator)
break
} else if tag == "measurement*" {
measurement = append(measurement, fields[i:]...)
break
Expand Down
13 changes: 13 additions & 0 deletions services/graphite/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ func TestTemplateApply(t *testing.T) {
measurement: "cpu.load",
tags: map[string]string{"zone": "us-west"},
},
{
test: "conjoined fields",
input: "prod.us-west.server01.cpu.util.idle.percent",
template: "env.zone.host.measurement.measurement.field*",
measurement: "cpu.util",
tags: map[string]string{"env": "prod", "zone": "us-west", "host": "server01"},
},
}

for _, test := range tests {
Expand Down Expand Up @@ -193,6 +200,12 @@ func TestParse(t *testing.T) {
template: "measurement",
err: `field "cpu" time: strconv.ParseFloat: parsing "14199724z57825": invalid syntax`,
},
{
test: "measurement* and field* (invalid)",
input: `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 {
Expand Down