jolokia: add proxy mode

This commit is contained in:
Simone Aiello 2016-04-14 23:00:41 +02:00
parent e428d11add
commit dc160b307e
3 changed files with 212 additions and 63 deletions

View File

@ -5,13 +5,22 @@
```toml ```toml
[[inputs.jolokia]] [[inputs.jolokia]]
## This is the context root used to compose the jolokia url ## This is the context root used to compose the jolokia url
context = "/jolokia/read" context = "/jolokia"
## List of servers exposing jolokia read service # 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
# [inputs.jolokia.proxy]
# host = "127.0.0.1"
# port = "8080"
# List of servers exposing jolokia read service
[[inputs.jolokia.servers]] [[inputs.jolokia.servers]]
name = "stable" name = "as-server-01"
host = "192.168.103.2" host = "127.0.0.1"
port = "8180" port = "8080"
# username = "myuser" # username = "myuser"
# password = "mypassword" # password = "mypassword"
@ -21,17 +30,20 @@
## This collect all heap memory usage metrics. ## This collect all heap memory usage metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "heap_memory_usage" name = "heap_memory_usage"
jmx = "/java.lang:type=Memory/HeapMemoryUsage" mbean = "java.lang:type=Memory"
attribute = "HeapMemoryUsage"
## This collect thread counts metrics. ## This collect thread counts metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "thread_count" name = "thread_count"
jmx = "/java.lang:type=Threading/TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount" mbean = "java.lang:type=Threading"
attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount"
## This collect number of class loaded/unloaded counts metrics. ## This collect number of class loaded/unloaded counts metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "class_count" name = "class_count"
jmx = "/java.lang:type=ClassLoading/LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount" mbean = "java.lang:type=ClassLoading"
attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount"
``` ```
#### Description #### Description

View File

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
"bytes"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/inputs"
@ -23,7 +24,9 @@ type Server struct {
type Metric struct { type Metric struct {
Name string Name string
Jmx string Mbean string
Attribute string
Path string
} }
type JolokiaClient interface { type JolokiaClient interface {
@ -41,20 +44,32 @@ func (c JolokiaClientImpl) MakeRequest(req *http.Request) (*http.Response, error
type Jolokia struct { type Jolokia struct {
jClient JolokiaClient jClient JolokiaClient
Context string Context string
Mode string
Servers []Server Servers []Server
Metrics []Metric Metrics []Metric
Proxy Server
} }
func (j *Jolokia) SampleConfig() string { func (j *Jolokia) SampleConfig() string {
return ` return `
## This is the context root used to compose the jolokia url # This is the context root used to compose the jolokia url
context = "/jolokia/read" context = "/jolokia"
## List of servers exposing jolokia read service # 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
# [inputs.jolokia.proxy]
# host = "127.0.0.1"
# port = "8080"
# List of servers exposing jolokia read service
[[inputs.jolokia.servers]] [[inputs.jolokia.servers]]
name = "stable" name = "as-server-01"
host = "192.168.103.2" host = "127.0.0.1"
port = "8180" port = "8080"
# username = "myuser" # username = "myuser"
# password = "mypassword" # password = "mypassword"
@ -64,17 +79,20 @@ func (j *Jolokia) SampleConfig() string {
## This collect all heap memory usage metrics. ## This collect all heap memory usage metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "heap_memory_usage" name = "heap_memory_usage"
jmx = "/java.lang:type=Memory/HeapMemoryUsage" mbean = "java.lang:type=Memory"
attribute = "HeapMemoryUsage"
## This collect thread counts metrics. ## This collect thread counts metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "thread_count" name = "thread_count"
jmx = "/java.lang:type=Threading/TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount" mbean = "java.lang:type=Threading"
attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount"
## This collect number of class loaded/unloaded counts metrics. ## This collect number of class loaded/unloaded counts metrics.
[[inputs.jolokia.metrics]] [[inputs.jolokia.metrics]]
name = "class_count" name = "class_count"
jmx = "/java.lang:type=ClassLoading/LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount" mbean = "java.lang:type=ClassLoading"
attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount"
` `
} }
@ -82,12 +100,7 @@ func (j *Jolokia) Description() string {
return "Read JMX metrics through Jolokia" return "Read JMX metrics through Jolokia"
} }
func (j *Jolokia) getAttr(requestUrl *url.URL) (map[string]interface{}, error) { func (j *Jolokia) doRequest(req *http.Request) (map[string]interface{}, error) {
// Create + send request
req, err := http.NewRequest("GET", requestUrl.String(), nil)
if err != nil {
return nil, err
}
resp, err := j.jClient.MakeRequest(req) resp, err := j.jClient.MakeRequest(req)
if err != nil { if err != nil {
@ -98,7 +111,7 @@ func (j *Jolokia) getAttr(requestUrl *url.URL) (map[string]interface{}, error) {
// Process response // Process response
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)", err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
requestUrl, req.RequestURI,
resp.StatusCode, resp.StatusCode,
http.StatusText(resp.StatusCode), http.StatusText(resp.StatusCode),
http.StatusOK, http.StatusOK,
@ -118,36 +131,29 @@ func (j *Jolokia) getAttr(requestUrl *url.URL) (map[string]interface{}, error) {
return nil, errors.New("Error decoding JSON response") return nil, errors.New("Error decoding JSON response")
} }
if status, ok := jsonOut["status"]; ok {
if status != float64(200) {
return nil, fmt.Errorf("Not expected status value in response body: %3.f", status)
}
} else {
return nil, fmt.Errorf("Missing status in response body")
}
return jsonOut, nil return jsonOut, nil
} }
func (j *Jolokia) Gather(acc telegraf.Accumulator) error { func (j *Jolokia) getAttr(requestUrl *url.URL) (map[string]interface{}, error) {
context := j.Context //"/jolokia/read" // Create + send request
servers := j.Servers req, err := http.NewRequest("GET", requestUrl.String(), nil)
metrics := j.Metrics
tags := make(map[string]string)
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 {
measurement := metric.Name
jmxPath := metric.Jmx
// Prepare URL
requestUrl, err := url.Parse("http://" + server.Host + ":" +
server.Port + context + jmxPath)
if err != nil { if err != nil {
return err return nil, err
}
if server.Username != "" || server.Password != "" {
requestUrl.User = url.UserPassword(server.Username, server.Password)
} }
out, _ := j.getAttr(requestUrl) return j.doRequest(req)
}
func (j *Jolokia) collectMeasurement(measurement string, out map[string]interface{}, fields map[string]interface{}) {
if values, ok := out["value"]; ok { if values, ok := out["value"]; ok {
switch t := values.(type) { switch t := values.(type) {
@ -159,13 +165,127 @@ func (j *Jolokia) Gather(acc telegraf.Accumulator) error {
fields[measurement] = t fields[measurement] = t
} }
} else { } else {
fmt.Printf("Missing key 'value' in '%s' output response\n", fmt.Printf("Missing key 'value' in output response\n")
requestUrl.String()) }
}
func (j *Jolokia) Gather(acc telegraf.Accumulator) error {
context := j.Context // Usually "/jolokia"
servers := j.Servers
metrics := j.Metrics
tags := make(map[string]string)
mode := j.Mode
if( mode == "agent" || mode == ""){
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 {
measurement := metric.Name
jmxPath := "/" + metric.Mbean
if metric.Attribute != "" {
jmxPath = jmxPath + "/" + metric.Attribute
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)
}
} else if ( mode == "proxy") {
proxy := j.Proxy
// Prepare ProxyURL
proxyURL, err := url.Parse("http://" + proxy.Host + ":" +
proxy.Port + context)
if err != nil {
return err
}
if 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 {
measurement := metric.Name
// Prepare URL
serviceUrl := fmt.Sprintf("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", server.Host, server.Port)
target := map[string]string{
"url": serviceUrl,
}
if server.Username != "" {
target["user"] = server.Username
}
if server.Password != "" {
target["password"] = server.Password
}
// Create + send request
bodyContent := map[string]interface{}{
"type": "read",
"mbean": metric.Mbean,
"target": target,
}
if metric.Attribute != "" {
bodyContent["attribute"] = metric.Attribute
if metric.Path != "" {
bodyContent["path"] = metric.Path
}
}
requestBody, err := json.Marshal(bodyContent)
req, err := http.NewRequest("POST", proxyURL.String(), bytes.NewBuffer(requestBody))
if err != nil {
return err
}
req.Header.Add("Content-type", "application/json")
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 return nil
} }

View File

@ -47,8 +47,10 @@ const invalidJSON = "I don't think this is JSON"
const empty = "" const empty = ""
var Servers = []Server{Server{Name: "as1", Host: "127.0.0.1", Port: "8080"}} var Servers = []Server{Server{Name: "as1", Host: "127.0.0.1", Port: "8080"}}
var HeapMetric = Metric{Name: "heap_memory_usage", Jmx: "/java.lang:type=Memory/HeapMemoryUsage"} var HeapMetric = Metric{Name: "heap_memory_usage",
var UsedHeapMetric = Metric{Name: "heap_memory_usage", Jmx: "/java.lang:type=Memory/HeapMemoryUsage"} Mbean: "java.lang:type=Memory", Attribute: "HeapMemoryUsage" }
var UsedHeapMetric = Metric{Name: "heap_memory_usage",
Mbean: "java.lang:type=Memory", Attribute: "HeapMemoryUsage"}
type jolokiaClientStub struct { type jolokiaClientStub struct {
responseBody string responseBody string
@ -114,3 +116,18 @@ func TestHttpJsonOn404(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, len(acc.Metrics)) assert.Equal(t, 0, len(acc.Metrics))
} }
// Test that the proper values are ignored or collected
func TestHttpInvalidJson(t *testing.T) {
jolokia := genJolokiaClientStub(invalidJSON, 200, Servers,
[]Metric{UsedHeapMetric})
var acc testutil.Accumulator
acc.SetDebug(true)
err := jolokia.Gather(&acc)
assert.Nil(t, err)
assert.Equal(t, 0, len(acc.Metrics))
}