diff --git a/CHANGELOG.md b/CHANGELOG.md index 39383be71..a307b8764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,8 +82,10 @@ https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md#g - Possible breaking change for the librato and graphite outputs. Telegraf will no longer insert field names when the field is simply named `value`. This is because the `value` field is redundant in the graphite/librato context. +- Breaking change in jolokia plugin. See https://github.com/influxdata/telegraf/blob/master/plugins/inputs/jolokia/README.md ### Features +- [#1031](https://github.com/influxdata/telegraf/pull/1031): Jolokia plugin proxy mode. Thanks @saiello! - [#1009](https://github.com/influxdata/telegraf/pull/1009): Cassandra input plugin. Thanks @subhachandrachandra! - [#976](https://github.com/influxdata/telegraf/pull/976): Reduce allocations in the UDP and statsd inputs. - [#979](https://github.com/influxdata/telegraf/pull/979): Reduce allocations in the TCP listener. @@ -96,6 +98,7 @@ because the `value` field is redundant in the graphite/librato context. - [#1008](https://github.com/influxdata/telegraf/pull/1008): Adding memstats metrics to the influxdb plugin. ### Bugfixes +- [#1050](https://github.com/influxdata/telegraf/issues/1050): jolokia plugin - do not overwrite host tag. Thanks @saiello! - [#968](https://github.com/influxdata/telegraf/issues/968): Processes plugin gets unknown state when spaces are in (command name) - [#969](https://github.com/influxdata/telegraf/pull/969): ipmi_sensors: allow : in password. Thanks @awaw! - [#972](https://github.com/influxdata/telegraf/pull/972): dovecot: remove extra newline in dovecot command. Thanks @mrannanj! diff --git a/plugins/inputs/jolokia/README.md b/plugins/inputs/jolokia/README.md index cec3c95ce..05ade3d01 100644 --- a/plugins/inputs/jolokia/README.md +++ b/plugins/inputs/jolokia/README.md @@ -54,4 +54,4 @@ are collected for each server configured. See: https://jolokia.org/ # Measurements: -Jolokia plugin produces one measure for each metric configured, adding Server's `name`, `host` and `port` as tags. +Jolokia plugin produces one measure for each metric configured, adding Server's `server_name`, `server_host` and `server_port` as tags. diff --git a/plugins/inputs/jolokia/jolokia.go b/plugins/inputs/jolokia/jolokia.go index 64835366e..08256ce85 100644 --- a/plugins/inputs/jolokia/jolokia.go +++ b/plugins/inputs/jolokia/jolokia.go @@ -1,6 +1,7 @@ package jolokia import ( + "bytes" "encoding/json" "errors" "fmt" @@ -8,7 +9,6 @@ import ( "net/http" "net/url" "time" - "bytes" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" @@ -23,10 +23,10 @@ type Server struct { } type Metric struct { - Name string - Mbean string + Name string + Mbean string Attribute string - Path string + Path string } type JolokiaClient interface { @@ -44,28 +44,28 @@ func (c JolokiaClientImpl) MakeRequest(req *http.Request) (*http.Response, error type Jolokia struct { jClient JolokiaClient Context string - Mode string + Mode string Servers []Server Metrics []Metric - Proxy Server + Proxy Server } -func (j *Jolokia) SampleConfig() string { - return ` - # This is the context root used to compose the jolokia url +const sampleConfig = ` + ## This is the context root used to compose the jolokia url context = "/jolokia" - # This specifies the mode used + ## This specifies the mode used # mode = "proxy" # - # When in proxy mode this section is used to specify further proxy address configurations. - # Remember to change servers addresses + ## When in proxy mode this section is used to specify further + ## proxy address configurations. + ## Remember to change host address to fit your environment. # [inputs.jolokia.proxy] - # host = "127.0.0.1" - # port = "8080" + # host = "127.0.0.1" + # port = "8080" - # List of servers exposing jolokia read service + ## List of servers exposing jolokia read service [[inputs.jolokia.servers]] name = "as-server-01" host = "127.0.0.1" @@ -86,14 +86,17 @@ func (j *Jolokia) SampleConfig() string { [[inputs.jolokia.metrics]] name = "thread_count" mbean = "java.lang:type=Threading" - attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount" + attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount" ## This collect number of class loaded/unloaded counts metrics. [[inputs.jolokia.metrics]] name = "class_count" mbean = "java.lang:type=ClassLoading" - attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount" + attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount" ` + +func (j *Jolokia) SampleConfig() string { + return sampleConfig } func (j *Jolokia) Description() string { @@ -133,7 +136,8 @@ func (j *Jolokia) doRequest(req *http.Request) (map[string]interface{}, error) { if status, ok := jsonOut["status"]; ok { if status != float64(200) { - return nil, fmt.Errorf("Not expected status value in response body: %3.f", status) + return nil, fmt.Errorf("Not expected status value in response body: %3.f", + status) } } else { return nil, fmt.Errorf("Missing status in response body") @@ -142,148 +146,122 @@ func (j *Jolokia) doRequest(req *http.Request) (map[string]interface{}, error) { return jsonOut, nil } -func (j *Jolokia) getAttr(requestUrl *url.URL) (map[string]interface{}, error) { - // Create + send request - req, err := http.NewRequest("GET", requestUrl.String(), nil) - if err != nil { - return nil, err - } - - return j.doRequest(req) -} - - -func (j *Jolokia) collectMeasurement(measurement string, out map[string]interface{}, fields map[string]interface{}) { - - if values, ok := out["value"]; ok { - switch t := values.(type) { - case map[string]interface{}: - for k, v := range t { - fields[measurement+"_"+k] = v - } - case interface{}: - fields[measurement] = t - } - } else { - fmt.Printf("Missing key 'value' in output response\n") - } - -} - - -func (j *Jolokia) Gather(acc telegraf.Accumulator) error { +func (j *Jolokia) prepareRequest(server Server, metric Metric) (*http.Request, error) { + var jolokiaUrl *url.URL context := j.Context // Usually "/jolokia" - servers := j.Servers - metrics := j.Metrics - tags := make(map[string]string) - mode := j.Mode - if( mode == "agent" || mode == ""){ + // Create bodyContent + bodyContent := map[string]interface{}{ + "type": "read", + "mbean": metric.Mbean, + } - for _, server := range servers { - tags["server"] = server.Name - tags["port"] = server.Port - tags["host"] = server.Host - fields := make(map[string]interface{}) - for _, metric := range metrics { + if metric.Attribute != "" { + bodyContent["attribute"] = metric.Attribute + if metric.Path != "" { + bodyContent["path"] = metric.Path + } + } - measurement := metric.Name - jmxPath := "/" + metric.Mbean - if metric.Attribute != "" { - jmxPath = jmxPath + "/" + metric.Attribute + // Add target, only in proxy mode + if j.Mode == "proxy" { + serviceUrl := fmt.Sprintf("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", + server.Host, server.Port) - if metric.Path != "" { - jmxPath = jmxPath + "/" + metric.Path - } - } - - // Prepare URL - requestUrl, err := url.Parse("http://" + server.Host + ":" + - server.Port + context + "/read" + jmxPath) - if err != nil { - return err - } - if server.Username != "" || server.Password != "" { - requestUrl.User = url.UserPassword(server.Username, server.Password) - } - out, _ := j.getAttr(requestUrl) - j.collectMeasurement(measurement, out, fields) - } - acc.AddFields("jolokia", fields, tags) + target := map[string]string{ + "url": serviceUrl, } - } else if ( mode == "proxy") { + if server.Username != "" { + target["user"] = server.Username + } + + if server.Password != "" { + target["password"] = server.Password + } + + bodyContent["target"] = target proxy := j.Proxy // Prepare ProxyURL - proxyURL, err := url.Parse("http://" + proxy.Host + ":" + - proxy.Port + context) + proxyUrl, err := url.Parse("http://" + proxy.Host + ":" + proxy.Port + context) if err != nil { - return err + return nil, err } if proxy.Username != "" || proxy.Password != "" { - proxyURL.User = url.UserPassword(proxy.Username, proxy.Password) + proxyUrl.User = url.UserPassword(proxy.Username, proxy.Password) } - for _, server := range servers { - tags["server"] = server.Name - tags["port"] = server.Port - tags["host"] = server.Host - fields := make(map[string]interface{}) - for _, metric := range metrics { + jolokiaUrl = proxyUrl - measurement := metric.Name - // Prepare URL - serviceUrl := fmt.Sprintf("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", server.Host, server.Port) + } else { + serverUrl, err := url.Parse("http://" + server.Host + ":" + server.Port + context) + if err != nil { + return nil, err + } + if server.Username != "" || server.Password != "" { + serverUrl.User = url.UserPassword(server.Username, server.Password) + } - target := map[string]string{ - "url": serviceUrl, - } + jolokiaUrl = serverUrl + } - if server.Username != "" { - target["user"] = server.Username - } + requestBody, err := json.Marshal(bodyContent) - if server.Password != "" { - target["password"] = server.Password - } + req, err := http.NewRequest("POST", jolokiaUrl.String(), bytes.NewBuffer(requestBody)) - // Create + send request - bodyContent := map[string]interface{}{ - "type": "read", - "mbean": metric.Mbean, - "target": target, - } + if err != nil { + return nil, err + } - if metric.Attribute != "" { - bodyContent["attribute"] = metric.Attribute - if metric.Path != "" { - bodyContent["path"] = metric.Path - } - } + req.Header.Add("Content-type", "application/json") - requestBody, err := json.Marshal(bodyContent) + return req, nil +} - req, err := http.NewRequest("POST", proxyURL.String(), bytes.NewBuffer(requestBody)) +func (j *Jolokia) Gather(acc telegraf.Accumulator) error { + servers := j.Servers + metrics := j.Metrics + tags := make(map[string]string) - if err != nil { - return err - } + for _, server := range servers { + tags["server_name"] = server.Name + tags["server_port"] = server.Port + tags["server_host"] = server.Host + fields := make(map[string]interface{}) - req.Header.Add("Content-type", "application/json") + for _, metric := range metrics { + measurement := metric.Name + + req, err := j.prepareRequest(server, metric) + if err != nil { + return err + } + + out, err := j.doRequest(req) + + if err != nil { + fmt.Printf("Error handling response: %s\n", err) + } else { + + if values, ok := out["value"]; ok { + switch t := values.(type) { + case map[string]interface{}: + for k, v := range t { + fields[measurement+"_"+k] = v + } + case interface{}: + fields[measurement] = t + } + } else { + fmt.Printf("Missing key 'value' in output response\n") + } - out, err := j.doRequest(req) - - if err != nil { - fmt.Printf("Error handling response: %s\n", err) - }else { - j.collectMeasurement(measurement, out, fields) - } } - acc.AddFields("jolokia", fields, tags) } + acc.AddFields("jolokia", fields, tags) } return nil diff --git a/plugins/inputs/jolokia/jolokia_test.go b/plugins/inputs/jolokia/jolokia_test.go index eb8fb12da..ff0c0e49d 100644 --- a/plugins/inputs/jolokia/jolokia_test.go +++ b/plugins/inputs/jolokia/jolokia_test.go @@ -48,7 +48,7 @@ const empty = "" var Servers = []Server{Server{Name: "as1", Host: "127.0.0.1", Port: "8080"}} var HeapMetric = Metric{Name: "heap_memory_usage", - Mbean: "java.lang:type=Memory", Attribute: "HeapMemoryUsage" } + Mbean: "java.lang:type=Memory", Attribute: "HeapMemoryUsage"} var UsedHeapMetric = Metric{Name: "heap_memory_usage", Mbean: "java.lang:type=Memory", Attribute: "HeapMemoryUsage"} @@ -96,9 +96,9 @@ func TestHttpJsonMultiValue(t *testing.T) { "heap_memory_usage_used": 203288528.0, } tags := map[string]string{ - "host": "127.0.0.1", - "port": "8080", - "server": "as1", + "server_host": "127.0.0.1", + "server_port": "8080", + "server_name": "as1", } acc.AssertContainsTaggedFields(t, "jolokia", fields, tags) } @@ -117,7 +117,6 @@ func TestHttpJsonOn404(t *testing.T) { assert.Equal(t, 0, len(acc.Metrics)) } - // Test that the proper values are ignored or collected func TestHttpInvalidJson(t *testing.T) {