diff --git a/Godeps b/Godeps index 3a4e9fb1b..46f23f770 100644 --- a/Godeps +++ b/Godeps @@ -47,6 +47,9 @@ github.com/prometheus/common e8eabff8812b05acf522b45fdcd725a785188e37 github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8 github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f github.com/shirou/gopsutil 4d0c402af66c78735c5ccf820dc2ca7de5e4ff08 +github.com/signalfx/com_signalfx_metrics_protobuf 1477d7f5730360be99fcaf7e7d7c56e03be2eabc +github.com/signalfx/gohistogram 1ccfd2ff508314074672f4450a917011a2060408 +github.com/signalfx/golib 93fae626851610c921ba9dfef122b6216d013903 github.com/soniah/gosnmp eb32571c2410868d85849ad67d1e51d01273eb84 github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744 github.com/stretchr/testify 1f4a1643a57e798696635ea4c126e9127adb7d3c @@ -60,5 +63,7 @@ golang.org/x/net 6acef71eb69611914f7a30939ea9f6e194c78172 golang.org/x/text a71fd10341b064c10f4a81ceac72bcf70f26ea34 gopkg.in/dancannon/gorethink.v1 7d1af5be49cb5ecc7b177bf387d232050299d6ef gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715 +gopkg.in/logfmt.v0 a0ff333161fe5c2daed0ba52d1792bd3d2531b94 gopkg.in/mgo.v2 d90005c5262a3463800497ea5a89aed5fe22c886 +gopkg.in/stack.v1 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4 diff --git a/README.md b/README.md index ebe3ed516..62d6f8ada 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ want to add support for another service or third-party API. * [opentsdb](https://github.com/influxdata/telegraf/tree/master/plugins/outputs/opentsdb) * [prometheus](https://github.com/influxdata/telegraf/tree/master/plugins/outputs/prometheus_client) * [riemann](https://github.com/influxdata/telegraf/tree/master/plugins/outputs/riemann) +* [signalfx](https://github.com/influxdata/telegraf/tree/master/plugins/outputs/signalfx) ## Contributing diff --git a/plugins/outputs/all/all.go b/plugins/outputs/all/all.go index 28354e7e4..786644510 100644 --- a/plugins/outputs/all/all.go +++ b/plugins/outputs/all/all.go @@ -19,4 +19,5 @@ import ( _ "github.com/influxdata/telegraf/plugins/outputs/opentsdb" _ "github.com/influxdata/telegraf/plugins/outputs/prometheus_client" _ "github.com/influxdata/telegraf/plugins/outputs/riemann" + _ "github.com/influxdata/telegraf/plugins/outputs/signalfx" ) diff --git a/plugins/outputs/signalfx/README.md b/plugins/outputs/signalfx/README.md new file mode 100644 index 000000000..03630abde --- /dev/null +++ b/plugins/outputs/signalfx/README.md @@ -0,0 +1,31 @@ +# SignalFx Output Plugin + +This plugin writes to [SignalFx](https://signalfx.com/) via HTTP.
+For each Telegraf metric a SignalFx gauge datapoint is written per field.
+The datapoint's metric name is a concatention of the Telegraf metric name and the field name.
+Tags are written as datapoint dimensions. + +### Configuration: + +```toml +# Send Telegraf metrics to SignalFx +[[outputs.signalfx]] + ## Your organization's SignalFx API access token. + auth_token = "SuperSecretToken" + + ## Optional HTTP User Agent value; Overrides the default. + # user_agent = "Telegraf collector" + + ## Optional SignalFX API endpoint value; Overrides the default. + # endpoint = "https://ingest.signalfx.com/v2/datapoint" +``` + +### Required parameters: + +* `auth_token`: Your organization's SignalFx API access token. + + +### Optional parameters: + +* `user_agent`: HTTP User Agent. +* `endpoint`: SignalFX API endpoint. diff --git a/plugins/outputs/signalfx/signalfx.go b/plugins/outputs/signalfx/signalfx.go new file mode 100644 index 000000000..41a32d715 --- /dev/null +++ b/plugins/outputs/signalfx/signalfx.go @@ -0,0 +1,92 @@ +package signalfx + +import ( + "log" + + "golang.org/x/net/context" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/outputs" + + "github.com/signalfx/golib/datapoint" + "github.com/signalfx/golib/sfxclient" +) + +type SignalFx struct { + AuthToken string `toml:"auth_token"` + UserAgent string `toml:"user_agent"` + Endpoint string `toml:"endpoint"` + + sink *sfxclient.HTTPDatapointSink +} + +var sampleConfig = ` + ## Your organization's SignalFx API access token. + auth_token = "SuperSecretToken" + + ## Optional HTTP User Agent value; Overrides the default. + # user_agent = "Telegraf collector" + + ## Optional SignalFX API endpoint value; Overrides the default. + # endpoint = "https://ingest.signalfx.com/v2/datapoint" +` + +func (s *SignalFx) Description() string { + return "Send Telegraf metrics to SignalFx" +} + +func (s *SignalFx) SampleConfig() string { + return sampleConfig +} + +func (s *SignalFx) Connect() error { + s.sink = sfxclient.NewHTTPDatapointSink() + s.sink.AuthToken = s.AuthToken + if len(s.UserAgent) > 0 { + s.sink.UserAgent = s.UserAgent + } + if len(s.Endpoint) > 0 { + s.sink.Endpoint = s.Endpoint + } + + return nil +} + +func (s *SignalFx) Close() error { + return nil +} + +func (s *SignalFx) Write(metrics []telegraf.Metric) error { + var datapoints []*datapoint.Datapoint + for _, metric := range metrics { + // One SignalFx metric per field. + for fieldName, fieldValue := range metric.Fields() { + var value datapoint.Value + switch fieldValue.(type) { + case float64: + value = datapoint.NewFloatValue(fieldValue.(float64)) + case int64: + value = datapoint.NewIntValue(fieldValue.(int64)) + default: + log.Printf("Unhandled type %T for field %s\n", fieldValue, fieldName) + continue + } + + metricName := metric.Name() + "." + fieldName + datapoint := datapoint.New(metricName, metric.Tags(), value, datapoint.Gauge, metric.Time()) + datapoints = append(datapoints, datapoint) + } + } + + ctx := context.Background() + err := s.sink.AddDatapoints(ctx, datapoints) + if err != nil { + return err + } + + return nil +} + +func init() { + outputs.Add("signalfx", func() telegraf.Output { return &SignalFx{} }) +} diff --git a/plugins/outputs/signalfx/signalfx_test.go b/plugins/outputs/signalfx/signalfx_test.go new file mode 100644 index 000000000..0320de3fa --- /dev/null +++ b/plugins/outputs/signalfx/signalfx_test.go @@ -0,0 +1,32 @@ +package signalfx + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/influxdata/telegraf/testutil" + + "github.com/stretchr/testify/require" +) + +func TestHTTPSignalFx(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + // https://developers.signalfx.com/docs/datapoint + fmt.Fprintln(w, `"OK"`) + })) + defer ts.Close() + + i := SignalFx{ + AuthToken: "Whatever", + Endpoint: ts.URL, + } + + err := i.Connect() + require.NoError(t, err) + err = i.Write(testutil.MockMetrics()) + require.NoError(t, err) +}