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

HTTP output plugin #2491

Merged
merged 20 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/outputs/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/outputs/file"
_ "github.com/influxdata/telegraf/plugins/outputs/graphite"
_ "github.com/influxdata/telegraf/plugins/outputs/graylog"
_ "github.com/influxdata/telegraf/plugins/outputs/http"
_ "github.com/influxdata/telegraf/plugins/outputs/influxdb"
_ "github.com/influxdata/telegraf/plugins/outputs/instrumental"
_ "github.com/influxdata/telegraf/plugins/outputs/kafka"
Expand Down
28 changes: 28 additions & 0 deletions plugins/outputs/http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# HTTP Output Plugin

This plugin writes to a HTTP Server using the `POST Method`.

Data collected from telegraf is sent in the Request Body.

### Configuration:

```toml
# Send telegraf metrics to HTTP Server(s)
[[outputs.http]]
## It requires a url name.
## Will be transmitted telegraf metrics to the HTTP Server using the below URL.
## Note that not support the HTTPS.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add support for the regular HTTPS options once this is merged, but this code will support connecting to a HTTPS url as it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to do additional work on HTTPS after you first merge it? I have not worked on this part.

url = "http://127.0.0.1:8080/metric"
## Configure dial timeout in seconds. Default : 3
timeout = 3
## http_headers option can add a custom header to the request.
## Content-Type is required http header in http plugin.
## so content-type of HTTP specification (plain/text, application/json, etc...) must be filled out.
[outputs.http.headers]
Content-Type = "plain/text"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check this configuration format.

## Data format to output.
## Each data format has it's own unique set of configuration options, read
## more about them here:
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
data_format = "influx"
```
151 changes: 151 additions & 0 deletions plugins/outputs/http/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package http

import (
"bytes"
"fmt"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/outputs"
"github.com/influxdata/telegraf/plugins/serializers"
"io/ioutil"
"net/http"
"strings"
"time"
)

var sampleConfig = `
## It requires a url name.
## Will be transmitted telegraf metrics to the HTTP Server using the below URL.
## Note that not support the HTTPS.
url = "http://127.0.0.1:8080/metric"
## Configure dial timeout in seconds. Default : 3
timeout = 3
## http_headers option can add a custom header to the request.
## Content-Type is required http header in http plugin.
## so content-type of HTTP specification (plain/text, application/json, etc...) must be filled out.
[outputs.http.headers]
Content-Type = "plain/text"
## Data format to output.
## Each data format has it's own unique set of configuration options, read
## more about them here:
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
data_format = "influx"
`

const (
POST = "POST"

DEFAULT_TIME_OUT = 3

CONTENT_TYPE = "content-type"
)

type Http struct {
// http required option
URL string `toml:"url"`
Headers map[string]string

// Option with http default value
Timeout int `toml:"timeout"`

client http.Client
serializer serializers.Serializer
}

func (h *Http) SetSerializer(serializer serializers.Serializer) {
h.serializer = serializer
}

// Connect to the Output
func (h *Http) Connect() error {
h.client = http.Client{
Transport: &http.Transport{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Proxy: ProxyFromEnvironment to transport.

Proxy: http.ProxyFromEnvironment,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the Proxy: ProxyFromEnvironment to transport.

},
Timeout: time.Duration(h.Timeout) * time.Second,
}

var isValid bool

for k := range h.Headers {
if strings.ToLower(k) == CONTENT_TYPE {
isValid = true
}
}

if !isValid {
return fmt.Errorf("E! httpHeader require content-type!")
}

return nil
}

// Close is not implemented. Because http.Client not provided connection close policy. Instead, uses the response.Body.Close() pattern.
func (h *Http) Close() error {
return nil
}

// Description A plugin that can transmit metrics over HTTP
func (h *Http) Description() string {
return "A plugin that can transmit metrics over HTTP"
}

// SampleConfig provides sample example for developer
func (h *Http) SampleConfig() string {
return sampleConfig
}

// Writes metrics over HTTP POST
func (h *Http) Write(metrics []telegraf.Metric) error {
reqBody, err := h.serializer.SerializeBatch(metrics)

if err != nil {
return fmt.Errorf("E! Error serializing some metrics: %s", err.Error())
}

if err := h.write(reqBody); err != nil {
return err
}

return nil
}

func (h *Http) write(reqBody []byte) error {
req, err := http.NewRequest(POST, h.URL, bytes.NewBuffer(reqBody))

for k, v := range h.Headers {
req.Header.Set(k, v)
}

resp, err := h.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)

if err := h.isOk(resp, err); err != nil {
return err
}

return nil
}

func (h *Http) isOk(resp *http.Response, err error) error {
if resp == nil || err != nil {
return fmt.Errorf("E! %s request failed! %s.", h.URL, err.Error())
}

if resp.StatusCode < 200 || resp.StatusCode > 209 {
return fmt.Errorf("received bad status code, %d\n", resp.StatusCode)
}

return nil
}

func init() {
outputs.Add("http", func() telegraf.Output {
return &Http{
Timeout: DEFAULT_TIME_OUT,
}
})
}
Loading