2015-10-28 08:13:22 +00:00
|
|
|
|
package jolokia
|
|
|
|
|
|
|
|
|
|
import (
|
2016-04-14 22:38:46 +00:00
|
|
|
|
"bytes"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
2015-10-29 12:25:16 +00:00
|
|
|
|
"net/url"
|
2016-02-29 16:52:58 +00:00
|
|
|
|
"time"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
|
"github.com/influxdata/telegraf"
|
2016-12-21 12:41:58 +00:00
|
|
|
|
"github.com/influxdata/telegraf/internal"
|
2016-01-20 18:57:35 +00:00
|
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
)
|
|
|
|
|
|
2016-12-21 12:41:58 +00:00
|
|
|
|
// Default http timeouts
|
|
|
|
|
var DefaultResponseHeaderTimeout = internal.Duration{Duration: 3 * time.Second}
|
|
|
|
|
var DefaultClientTimeout = internal.Duration{Duration: 4 * time.Second}
|
|
|
|
|
|
2015-10-28 08:13:22 +00:00
|
|
|
|
type Server struct {
|
2015-12-03 18:46:06 +00:00
|
|
|
|
Name string
|
|
|
|
|
Host string
|
|
|
|
|
Username string
|
|
|
|
|
Password string
|
|
|
|
|
Port string
|
2015-10-28 08:13:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Metric struct {
|
2016-04-14 22:38:46 +00:00
|
|
|
|
Name string
|
|
|
|
|
Mbean string
|
2016-04-14 21:00:41 +00:00
|
|
|
|
Attribute string
|
2016-04-14 22:38:46 +00:00
|
|
|
|
Path string
|
2015-10-28 08:13:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 16:00:26 +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 {
|
2017-01-12 11:08:22 +00:00
|
|
|
|
jClient JolokiaClient
|
|
|
|
|
Context string
|
|
|
|
|
Mode string
|
|
|
|
|
Servers []Server
|
|
|
|
|
Metrics []Metric
|
|
|
|
|
Proxy Server
|
|
|
|
|
Delimiter string
|
2016-12-21 12:41:58 +00:00
|
|
|
|
|
|
|
|
|
ResponseHeaderTimeout internal.Duration `toml:"response_header_timeout"`
|
|
|
|
|
ClientTimeout internal.Duration `toml:"client_timeout"`
|
2015-10-28 08:13:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
const sampleConfig = `
|
|
|
|
|
## This is the context root used to compose the jolokia url
|
2016-12-17 12:51:46 +00:00
|
|
|
|
## NOTE that Jolokia requires a trailing slash at the end of the context root
|
2016-09-06 10:11:27 +00:00
|
|
|
|
## NOTE that your jolokia security policy must allow for POST requests.
|
2016-12-17 12:51:46 +00:00
|
|
|
|
context = "/jolokia/"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
## This specifies the mode used
|
2016-04-14 21:00:41 +00:00
|
|
|
|
# mode = "proxy"
|
|
|
|
|
#
|
2016-04-14 22:38:46 +00:00
|
|
|
|
## When in proxy mode this section is used to specify further
|
|
|
|
|
## proxy address configurations.
|
|
|
|
|
## Remember to change host address to fit your environment.
|
2016-04-14 21:00:41 +00:00
|
|
|
|
# [inputs.jolokia.proxy]
|
2016-04-14 22:38:46 +00:00
|
|
|
|
# host = "127.0.0.1"
|
|
|
|
|
# port = "8080"
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-12-21 12:41:58 +00:00
|
|
|
|
## Optional http timeouts
|
|
|
|
|
##
|
|
|
|
|
## response_header_timeout, if non-zero, specifies the amount of time to wait
|
|
|
|
|
## for a server's response headers after fully writing the request.
|
|
|
|
|
# response_header_timeout = "3s"
|
|
|
|
|
##
|
|
|
|
|
## client_timeout specifies a time limit for requests made by this client.
|
|
|
|
|
## Includes connection time, any redirects, and reading the response body.
|
|
|
|
|
# client_timeout = "4s"
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2017-01-12 11:08:22 +00:00
|
|
|
|
## Attribute delimiter
|
|
|
|
|
##
|
|
|
|
|
## When multiple attributes are returned for a single
|
|
|
|
|
## [inputs.jolokia.metrics], the field name is a concatenation of the metric
|
|
|
|
|
## name, and the attribute name, separated by the given delimiter.
|
|
|
|
|
# delimiter = "_"
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
## 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"
|
2015-12-03 18:46:06 +00:00
|
|
|
|
# username = "myuser"
|
|
|
|
|
# password = "mypassword"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
|
2016-02-18 21:26:51 +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"
|
|
|
|
|
|
2016-04-08 15:54:32 +00:00
|
|
|
|
## This collect thread counts metrics.
|
|
|
|
|
[[inputs.jolokia.metrics]]
|
|
|
|
|
name = "thread_count"
|
2016-04-14 21:00:41 +00:00
|
|
|
|
mbean = "java.lang:type=Threading"
|
2016-04-14 22:38:46 +00:00
|
|
|
|
attribute = "TotalStartedThreadCount,ThreadCount,DaemonThreadCount,PeakThreadCount"
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-08 15:54:32 +00:00
|
|
|
|
## 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"
|
2016-04-14 22:38:46 +00:00
|
|
|
|
attribute = "LoadedClassCount,UnloadedClassCount,TotalLoadedClassCount"
|
2015-10-28 08:13:22 +00:00
|
|
|
|
`
|
2016-04-14 22:38:46 +00:00
|
|
|
|
|
|
|
|
|
func (j *Jolokia) SampleConfig() string {
|
|
|
|
|
return sampleConfig
|
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) {
|
2015-10-29 16:00:26 +00:00
|
|
|
|
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) {
|
2016-04-14 22:38:46 +00:00
|
|
|
|
return nil, fmt.Errorf("Not expected status value in response body: %3.f",
|
|
|
|
|
status)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
} 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 22:38:46 +00:00
|
|
|
|
func (j *Jolokia) prepareRequest(server Server, metric Metric) (*http.Request, error) {
|
|
|
|
|
var jolokiaUrl *url.URL
|
2016-12-17 12:51:46 +00:00
|
|
|
|
context := j.Context // Usually "/jolokia/"
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
// Create bodyContent
|
|
|
|
|
bodyContent := map[string]interface{}{
|
|
|
|
|
"type": "read",
|
|
|
|
|
"mbean": metric.Mbean,
|
|
|
|
|
}
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
if metric.Attribute != "" {
|
|
|
|
|
bodyContent["attribute"] = metric.Attribute
|
|
|
|
|
if metric.Path != "" {
|
|
|
|
|
bodyContent["path"] = metric.Path
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
// 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)
|
2015-10-28 08:13:22 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
target := map[string]string{
|
|
|
|
|
"url": serviceUrl,
|
|
|
|
|
}
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
if server.Username != "" {
|
|
|
|
|
target["user"] = server.Username
|
|
|
|
|
}
|
2015-10-28 08:13:22 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
if server.Password != "" {
|
|
|
|
|
target["password"] = server.Password
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
bodyContent["target"] = target
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
|
|
|
|
proxy := j.Proxy
|
|
|
|
|
|
|
|
|
|
// Prepare ProxyURL
|
2016-04-14 22:38:46 +00:00
|
|
|
|
proxyUrl, err := url.Parse("http://" + proxy.Host + ":" + proxy.Port + context)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
if err != nil {
|
2016-04-14 22:38:46 +00:00
|
|
|
|
return nil, err
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
if proxy.Username != "" || proxy.Password != "" {
|
2016-04-14 22:38:46 +00:00
|
|
|
|
proxyUrl.User = url.UserPassword(proxy.Username, proxy.Password)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
jolokiaUrl = proxyUrl
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
} 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)
|
|
|
|
|
}
|
2015-10-29 12:25:16 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
jolokiaUrl = serverUrl
|
|
|
|
|
}
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
requestBody, err := json.Marshal(bodyContent)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
req, err := http.NewRequest("POST", jolokiaUrl.String(), bytes.NewBuffer(requestBody))
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-10-29 13:48:39 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
req.Header.Add("Content-type", "application/json")
|
|
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-12 11:08:22 +00:00
|
|
|
|
func (j *Jolokia) extractValues(measurement string, value interface{}, fields map[string]interface{}) {
|
2016-11-04 11:32:20 +00:00
|
|
|
|
if mapValues, ok := value.(map[string]interface{}); ok {
|
|
|
|
|
for k2, v2 := range mapValues {
|
2017-01-12 11:08:22 +00:00
|
|
|
|
j.extractValues(measurement+j.Delimiter+k2, v2, fields)
|
2016-11-04 11:32:20 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fields[measurement] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
func (j *Jolokia) Gather(acc telegraf.Accumulator) error {
|
2016-12-21 12:41:58 +00:00
|
|
|
|
|
|
|
|
|
if j.jClient == nil {
|
|
|
|
|
tr := &http.Transport{ResponseHeaderTimeout: j.ResponseHeaderTimeout.Duration}
|
|
|
|
|
j.jClient = &JolokiaClientImpl{&http.Client{
|
|
|
|
|
Transport: tr,
|
|
|
|
|
Timeout: j.ClientTimeout.Duration,
|
|
|
|
|
}}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
servers := j.Servers
|
|
|
|
|
metrics := j.Metrics
|
|
|
|
|
tags := make(map[string]string)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
for _, server := range servers {
|
2016-04-27 21:50:55 +00:00
|
|
|
|
tags["jolokia_name"] = server.Name
|
|
|
|
|
tags["jolokia_port"] = server.Port
|
|
|
|
|
tags["jolokia_host"] = server.Host
|
2016-04-14 22:38:46 +00:00
|
|
|
|
fields := make(map[string]interface{})
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
for _, metric := range metrics {
|
|
|
|
|
measurement := metric.Name
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
req, err := j.prepareRequest(server, metric)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
out, err := j.doRequest(req)
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Error handling response: %s\n", err)
|
|
|
|
|
} else {
|
|
|
|
|
if values, ok := out["value"]; ok {
|
2017-01-12 11:08:22 +00:00
|
|
|
|
j.extractValues(measurement, values, fields)
|
2016-04-14 22:38:46 +00:00
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf("Missing key 'value' in output response\n")
|
2016-04-14 21:00:41 +00:00
|
|
|
|
}
|
2016-04-14 22:38:46 +00:00
|
|
|
|
|
2015-10-29 13:48:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-14 21:00:41 +00:00
|
|
|
|
|
2016-04-14 22:38:46 +00:00
|
|
|
|
acc.AddFields("jolokia", fields, tags)
|
2015-10-29 13:48:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2015-10-28 08:13:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
2016-01-27 21:21:36 +00:00
|
|
|
|
inputs.Add("jolokia", func() telegraf.Input {
|
2016-12-21 12:41:58 +00:00
|
|
|
|
return &Jolokia{
|
|
|
|
|
ResponseHeaderTimeout: DefaultResponseHeaderTimeout,
|
|
|
|
|
ClientTimeout: DefaultClientTimeout,
|
2017-01-12 11:08:22 +00:00
|
|
|
|
Delimiter: "_",
|
2016-02-29 16:52:58 +00:00
|
|
|
|
}
|
2015-10-28 08:13:22 +00:00
|
|
|
|
})
|
|
|
|
|
}
|