telegraf/plugins/inputs/jolokia/jolokia.go

302 lines
6.9 KiB
Go
Raw Normal View History

2015-10-28 08:13:22 +00:00
package jolokia
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
2016-02-29 16:52:58 +00:00
"time"
2016-04-14 21:00:41 +00:00
"bytes"
2015-10-28 08:13:22 +00:00
"github.com/influxdata/telegraf"
2016-01-20 18:57:35 +00:00
"github.com/influxdata/telegraf/plugins/inputs"
2015-10-28 08:13:22 +00:00
)
type Server struct {
Name string
Host string
Username string
Password string
Port string
2015-10-28 08:13:22 +00:00
}
type Metric struct {
2015-10-29 13:48:39 +00:00
Name string
2016-04-14 21:00:41 +00:00
Mbean string
Attribute string
Path string
2015-10-28 08:13:22 +00:00
}
type JolokiaClient interface {
MakeRequest(req *http.Request) (*http.Response, error)
}
type JolokiaClientImpl struct {
client *http.Client
}
func (c JolokiaClientImpl) MakeRequest(req *http.Request) (*http.Response, error) {
return c.client.Do(req)
}
2015-10-28 08:13:22 +00:00
type Jolokia struct {
jClient JolokiaClient
2015-10-29 13:48:39 +00:00
Context string
2016-04-14 21:00:41 +00:00
Mode string
2015-10-29 13:48:39 +00:00
Servers []Server
Metrics []Metric
2016-04-14 21:00:41 +00:00
Proxy Server
2015-10-28 08:13:22 +00:00
}
func (j *Jolokia) SampleConfig() string {
2015-10-29 13:51:15 +00:00
return `
2016-04-14 21:00:41 +00:00
# This is the context root used to compose the jolokia url
context = "/jolokia"
2015-10-28 08:13:22 +00:00
2016-04-14 21:00:41 +00:00
# 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
2016-01-07 20:39:43 +00:00
[[inputs.jolokia.servers]]
2016-04-14 21:00:41 +00:00
name = "as-server-01"
host = "127.0.0.1"
port = "8080"
# username = "myuser"
# password = "mypassword"
2015-10-28 08:13:22 +00:00
## List of metrics collected on above servers
## Each metric consists in a name, a jmx path and either
## a pass or drop slice attribute.
## This collect all heap memory usage metrics.
2016-01-07 20:39:43 +00:00
[[inputs.jolokia.metrics]]
2015-11-03 21:00:23 +00:00
name = "heap_memory_usage"
2016-04-14 21:00:41 +00:00
mbean = "java.lang:type=Memory"
attribute = "HeapMemoryUsage"
## This collect thread counts metrics.
[[inputs.jolokia.metrics]]
name = "thread_count"
2016-04-14 21:00:41 +00:00
mbean = "java.lang:type=Threading"
attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount"
## This collect number of class loaded/unloaded counts metrics.
[[inputs.jolokia.metrics]]
name = "class_count"
2016-04-14 21:00:41 +00:00
mbean = "java.lang:type=ClassLoading"
attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount"
2015-10-28 08:13:22 +00:00
`
}
func (j *Jolokia) Description() string {
return "Read JMX metrics through Jolokia"
}
2016-04-14 21:00:41 +00:00
func (j *Jolokia) doRequest(req *http.Request) (map[string]interface{}, error) {
resp, err := j.jClient.MakeRequest(req)
2015-10-28 08:13:22 +00:00
if err != nil {
return nil, err
}
defer resp.Body.Close()
2015-10-29 13:48:39 +00:00
// Process response
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
2016-04-14 21:00:41 +00:00
req.RequestURI,
2015-10-29 13:48:39 +00:00
resp.StatusCode,
http.StatusText(resp.StatusCode),
http.StatusOK,
http.StatusText(http.StatusOK))
return nil, err
}
// read body
body, err := ioutil.ReadAll(resp.Body)
2015-10-28 08:13:22 +00:00
if err != nil {
return nil, err
}
2015-10-29 13:48:39 +00:00
// Unmarshal json
var jsonOut map[string]interface{}
if err = json.Unmarshal([]byte(body), &jsonOut); err != nil {
return nil, errors.New("Error decoding JSON response")
}
2015-10-28 08:13:22 +00:00
2016-04-14 21:00:41 +00:00
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")
}
2015-10-29 13:48:39 +00:00
return jsonOut, nil
2015-10-28 08:13:22 +00:00
}
2016-04-14 21:00:41 +00:00
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 {
2016-04-14 21:00:41 +00:00
context := j.Context // Usually "/jolokia"
2015-10-29 13:48:39 +00:00
servers := j.Servers
metrics := j.Metrics
2015-12-19 20:31:22 +00:00
tags := make(map[string]string)
2016-04-14 21:00:41 +00:00
mode := j.Mode
2015-10-28 08:13:22 +00:00
2016-04-14 21:00:41 +00:00
if( mode == "agent" || mode == ""){
2015-10-28 08:13:22 +00:00
2016-04-14 21:00:41 +00:00
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
}
}
2015-10-28 08:13:22 +00:00
// Prepare URL
2016-04-14 21:00:41 +00:00
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)
}
2016-04-14 21:00:41 +00:00
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)
2016-04-14 21:00:41 +00:00
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,
}
2015-10-29 13:48:39 +00:00
2016-04-14 21:00:41 +00:00
if metric.Attribute != "" {
bodyContent["attribute"] = metric.Attribute
if metric.Path != "" {
bodyContent["path"] = metric.Path
2015-12-19 20:31:22 +00:00
}
2015-10-29 13:48:39 +00:00
}
2016-04-14 21:00:41 +00:00
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)
}
2015-10-29 13:48:39 +00:00
}
2016-04-14 21:00:41 +00:00
acc.AddFields("jolokia", fields, tags)
2015-10-29 13:48:39 +00:00
}
2016-04-14 21:00:41 +00:00
2015-10-29 13:48:39 +00:00
}
return nil
2015-10-28 08:13:22 +00:00
}
func init() {
inputs.Add("jolokia", func() telegraf.Input {
2016-02-29 16:52:58 +00:00
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
client := &http.Client{
Transport: tr,
Timeout: time.Duration(4 * time.Second),
}
return &Jolokia{jClient: &JolokiaClientImpl{client: client}}
2015-10-28 08:13:22 +00:00
})
}