From 1449c8b88791125c1a6df1381f7fe97762dc1a4c Mon Sep 17 00:00:00 2001 From: Henry Hu Date: Mon, 1 Feb 2016 11:43:38 +0800 Subject: [PATCH] Add Graphite line protocol parsing to exec plugin closes #637 --- Godeps | 2 +- Godeps_windows | 2 +- README.md | 2 +- internal/encoding/encoder.go | 31 ++ internal/encoding/graphite/config.go | 135 +++++++++ internal/encoding/graphite/errors.go | 14 + internal/encoding/graphite/parser.go | 414 +++++++++++++++++++++++++++ internal/encoding/influx/parser.go | 48 ++++ internal/encoding/json/parser.go | 68 +++++ plugins/inputs/exec/README.md | 117 +++++++- plugins/inputs/exec/exec.go | 158 +++++++--- plugins/inputs/exec/exec_test.go | 20 +- 12 files changed, 951 insertions(+), 60 deletions(-) create mode 100644 internal/encoding/encoder.go create mode 100644 internal/encoding/graphite/config.go create mode 100644 internal/encoding/graphite/errors.go create mode 100644 internal/encoding/graphite/parser.go create mode 100644 internal/encoding/influx/parser.go create mode 100644 internal/encoding/json/parser.go diff --git a/Godeps b/Godeps index 3393a1cee..7e43ed610 100644 --- a/Godeps +++ b/Godeps @@ -56,4 +56,4 @@ golang.org/x/text 6d3c22c4525a4da167968fa2479be5524d2e8bd0 gopkg.in/dancannon/gorethink.v1 6f088135ff288deb9d5546f4c71919207f891a70 gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715 gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64 -gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 +gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 \ No newline at end of file diff --git a/Godeps_windows b/Godeps_windows index 8f147ed87..829e2cb35 100644 --- a/Godeps_windows +++ b/Godeps_windows @@ -60,4 +60,4 @@ golang.org/x/text 6fc2e00a0d64b1f7fc1212dae5b0c939cf6d9ac4 gopkg.in/dancannon/gorethink.v1 6f088135ff288deb9d5546f4c71919207f891a70 gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715 gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64 -gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 +gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 \ No newline at end of file diff --git a/README.md b/README.md index b079df888..c5db3c6e2 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ Currently implemented sources: * disque * docker * elasticsearch -* exec (generic JSON-emitting executable plugin) +* exec (generic executable plugin, support JSON, influx and graphite) * haproxy * httpjson (generic JSON-emitting http service plugin) * influxdb diff --git a/internal/encoding/encoder.go b/internal/encoding/encoder.go new file mode 100644 index 000000000..129906ce5 --- /dev/null +++ b/internal/encoding/encoder.go @@ -0,0 +1,31 @@ +package encoding + +import ( + "fmt" + + "github.com/influxdata/telegraf" +) + +type Parser interface { + InitConfig(configs map[string]interface{}) error + Parse(buf []byte) ([]telegraf.Metric, error) + ParseLine(line string) (telegraf.Metric, error) +} + +type Creator func() Parser + +var Parsers = map[string]Creator{} + +func Add(name string, creator Creator) { + Parsers[name] = creator +} + +func NewParser(dataFormat string, configs map[string]interface{}) (parser Parser, err error) { + creator := Parsers[dataFormat] + if creator == nil { + return nil, fmt.Errorf("Unsupported data format: %s. ", dataFormat) + } + parser = creator() + err = parser.InitConfig(configs) + return parser, err +} diff --git a/internal/encoding/graphite/config.go b/internal/encoding/graphite/config.go new file mode 100644 index 000000000..7a5c759e7 --- /dev/null +++ b/internal/encoding/graphite/config.go @@ -0,0 +1,135 @@ +package graphite + +import ( + "fmt" + "strings" +) + +const ( + // DefaultSeparator is the default join character to use when joining multiple + // measurment parts in a template. + DefaultSeparator = "." +) + +// Config represents the configuration for Graphite endpoints. +type Config struct { + Separator string + Templates []string +} + +// Validate validates the config's templates and tags. +func (c *Config) Validate() error { + if err := c.validateTemplates(); err != nil { + return err + } + + return nil +} + +func (c *Config) validateTemplates() error { + // map to keep track of filters we see + filters := map[string]struct{}{} + + for i, t := range c.Templates { + parts := strings.Fields(t) + // Ensure template string is non-empty + if len(parts) == 0 { + return fmt.Errorf("missing template at position: %d", i) + } + if len(parts) == 1 && parts[0] == "" { + return fmt.Errorf("missing template at position: %d", i) + } + + if len(parts) > 3 { + return fmt.Errorf("invalid template format: '%s'", t) + } + + template := t + filter := "" + tags := "" + if len(parts) >= 2 { + // We could have