From cd52747cedbdd4fcd07e04694116ac771dfdf87a Mon Sep 17 00:00:00 2001 From: mlindes Date: Wed, 16 Aug 2017 18:33:20 -0400 Subject: [PATCH] Add tomcat input plugin (#3112) --- plugins/inputs/tomcat/README.md | 73 +++++++++++ plugins/inputs/tomcat/tomcat.go | 176 +++++++++++++++++++++++++++ plugins/inputs/tomcat/tomcat_test.go | 95 +++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 plugins/inputs/tomcat/README.md create mode 100644 plugins/inputs/tomcat/tomcat.go create mode 100644 plugins/inputs/tomcat/tomcat_test.go diff --git a/plugins/inputs/tomcat/README.md b/plugins/inputs/tomcat/README.md new file mode 100644 index 000000000..d2fa2e729 --- /dev/null +++ b/plugins/inputs/tomcat/README.md @@ -0,0 +1,73 @@ +# Tomcat Input Plugin + +The Tomcat plugin collects statistics available from the tomcat manager status page from the `http:///manager/status/all?XML=true URL.` +(`XML=true` will return only xml data). See the [Tomcat documentation](https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html#Server_Status) for details of these statistics. + +### Configuration: + +```toml +# A Telegraf plugin to collect tomcat metrics. +[[inputs.tomcat]] + # A Tomcat status URI to gather stats. + # Default is "http://127.0.0.1:8080/manager/status/all?XML=true". + url = "http://127.0.0.1:8080/manager/status/all?XML=true" + # Credentials for status URI. + # Default is tomcat/s3cret. + username = "tomcat" + password = "s3cret" +``` + +### Measurements & Fields: + +- tomcat\_jvm\_memory + - free + - total + - max +- tomcat\_jvm\_memorypool + - max\_threads + - current\_thread\_count + - current\_threads\_busy + - max\_time + - processing\_time + - request\_count + - error\_count + - bytes\_received + - bytes\_sent +- tomcat\_connector + - max\_threads + - current\_thread\_count + - current\_thread\_busy + - max\_time + - processing\_time + - request\_count + - error\_count + - bytes\_received + - bytes\_sent + +### Tags: + +- tomcat\_jvm\_memorypool has the following tags: + - name + - type +- tomcat\_connector + - name + +### Sample Queries: + +TODO + +### Example Output: + +``` +$ ./telegraf -config telegraf.conf -input-filter tomcat -test +* Plugin: tomcat, Collection 1 +> tomcat_jvm_memory,host=N8-MBP free=20014352i,max=127729664i,total=41459712i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Eden\ Space,type=Heap\ memory committed=11534336i,init=2228224i,max=35258368i,used=1941200i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Survivor\ Space,type=Heap\ memory committed=1376256i,init=262144i,max=4390912i,used=1376248i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Tenured\ Gen,type=Heap\ memory committed=28549120i,init=5636096i,max=88080384i,used=18127912i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Code\ Cache,type=Non-heap\ memory committed=6946816i,init=2555904i,max=251658240i,used=6406528i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Compressed\ Class\ Space,type=Non-heap\ memory committed=1966080i,init=0i,max=1073741824i,used=1816120i 1474663361000000000 +> tomcat_jvm_memorypool,host=N8-MBP,name=Metaspace,type=Non-heap\ memory committed=18219008i,init=0i,max=-1i,used=17559376i 1474663361000000000 +> tomcat_connector,host=N8-MBP,name=ajp-bio-8009 bytes_received=0i,bytes_sent=0i,current_thread_count=0i,current_threads_busy=0i,error_count=0i,max_threads=200i,max_time=0i,processing_time=0i,request_count=0i 1474663361000000000 +> tomcat_connector,host=N8-MBP,name=http-bio-8080 bytes_received=0i,bytes_sent=86435i,current_thread_count=10i,current_threads_busy=1i,error_count=2i,max_threads=200i,max_time=167i,processing_time=245i,request_count=15i 1474663361000000000 +``` diff --git a/plugins/inputs/tomcat/tomcat.go b/plugins/inputs/tomcat/tomcat.go new file mode 100644 index 000000000..dfde52ca2 --- /dev/null +++ b/plugins/inputs/tomcat/tomcat.go @@ -0,0 +1,176 @@ +package tomcat + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +type TomcatStatus struct { + TomcatJvm TomcatJvm `xml:"jvm"` + TomcatConnectors []TomcatConnector `xml:"connector"` +} + +type TomcatJvm struct { + JvmMemory JvmMemoryStat `xml:"memory"` + JvmMemoryPools []JvmMemoryPoolStat `xml:"memorypool"` +} + +type JvmMemoryStat struct { + Free int64 `xml:"free,attr"` + Total int64 `xml:"total,attr"` + Max int64 `xml:"max,attr"` +} + +type JvmMemoryPoolStat struct { + Name string `xml:"name,attr"` + Type string `xml:"type,attr"` + UsageInit int64 `xml:"usageInit,attr"` + UsageCommitted int64 `xml:"usageCommitted,attr"` + UsageMax int64 `xml:"usageMax,attr"` + UsageUsed int64 `xml:"usageUsed,attr"` +} + +type TomcatConnector struct { + Name string `xml:"name,attr"` + ThreadInfo ThreadInfo `xml:"threadInfo"` + RequestInfo RequestInfo `xml:"requestInfo"` +} + +type ThreadInfo struct { + MaxThreads int64 `xml:"maxThreads,attr"` + CurrentThreadCount int64 `xml:"currentThreadCount,attr"` + CurrentThreadsBusy int64 `xml:"currentThreadsBusy,attr"` +} +type RequestInfo struct { + MaxTime int `xml:"maxTime,attr"` + ProcessingTime int `xml:"processingTime,attr"` + RequestCount int `xml:"requestCount,attr"` + ErrorCount int `xml:"errorCount,attr"` + BytesReceived int64 `xml:"bytesReceived,attr"` + BytesSent int64 `xml:"bytesSent,attr"` +} + +type Tomcat struct { + URL string + Username string + Password string +} + +var sampleconfig = ` + ## A Tomcat status URI to gather stats. + ## Default is "http://127.0.0.1:8080/manager/status/all?XML=true". + url = "http://127.0.0.1:8080/manager/status/all?XML=true" + ## Credentials for status URI. + ## Default is tomcat/s3cret. + username = "tomcat" + password = "s3cret" +` + +func (s *Tomcat) Description() string { + return "A Telegraf plugin to collect tomcat metrics." +} + +func (s *Tomcat) SampleConfig() string { + return sampleconfig +} + +func (s *Tomcat) Gather(acc telegraf.Accumulator) error { + + if s.URL == "" { + s.URL = "http://127.0.0.1:8080/manager/status/all?XML=true" + } + + if s.Username == "" { + s.Username = "tomcat" + } + + if s.Password == "" { + s.Password = "s3cret" + } + + _, err := url.Parse(s.URL) + if err != nil { + return fmt.Errorf("Unable to parse address '%s': %s", s.URL, err) + } + + req, err := http.NewRequest("GET", s.URL, nil) + req.SetBasicAuth(s.Username, s.Password) + cli := &http.Client{} + resp, err := cli.Do(req) + if err != nil { + return fmt.Errorf("Unable to call URL '%s': %s", s.URL, err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + + var status TomcatStatus + xml.Unmarshal(body, &status) + + // add tomcat_jvm_memory measurements + tcm := map[string]interface{}{ + "free": status.TomcatJvm.JvmMemory.Free, + "total": status.TomcatJvm.JvmMemory.Total, + "max": status.TomcatJvm.JvmMemory.Max, + } + acc.AddFields("tomcat_jvm_memory", tcm, nil) + + // add tomcat_jvm_memorypool measurements + for _, mp := range status.TomcatJvm.JvmMemoryPools { + + tcmpTags := map[string]string{ + "name": mp.Name, + "type": mp.Type, + } + + tcmpFields := map[string]interface{}{ + "init": mp.UsageInit, + "committed": mp.UsageCommitted, + "max": mp.UsageMax, + "used": mp.UsageUsed, + } + + acc.AddFields("tomcat_jvm_memorypool", tcmpFields, tcmpTags) + + } + + // add tomcat_connector measurements + for _, c := range status.TomcatConnectors { + + name, err := strconv.Unquote(c.Name) + if err != nil { + return fmt.Errorf("Unable to unquote name '%s': %s", c.Name, err) + } + + tccTags := map[string]string{ + "name": name, + } + + tccFields := map[string]interface{}{ + "max_threads": c.ThreadInfo.MaxThreads, + "current_thread_count": c.ThreadInfo.CurrentThreadCount, + "current_threads_busy": c.ThreadInfo.CurrentThreadsBusy, + "max_time": c.RequestInfo.MaxTime, + "processing_time": c.RequestInfo.ProcessingTime, + "request_count": c.RequestInfo.RequestCount, + "error_count": c.RequestInfo.ErrorCount, + "bytes_received": c.RequestInfo.BytesReceived, + "bytes_sent": c.RequestInfo.BytesSent, + } + + acc.AddFields("tomcat_connector", tccFields, tccTags) + + } + + return nil +} + +func init() { + inputs.Add("tomcat", func() telegraf.Input { return &Tomcat{} }) +} diff --git a/plugins/inputs/tomcat/tomcat_test.go b/plugins/inputs/tomcat/tomcat_test.go new file mode 100644 index 000000000..8bdddd6d0 --- /dev/null +++ b/plugins/inputs/tomcat/tomcat_test.go @@ -0,0 +1,95 @@ +package tomcat + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/influxdata/telegraf/testutil" + + "github.com/stretchr/testify/require" +) + +var tomcatStatus = ` + + + + + + + + + + + + + + + + + + + + + + + +` + +func TestHTTPTomcat(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, tomcatStatus) + })) + defer ts.Close() + + tc := Tomcat{ + URL: ts.URL, + Username: "tomcat", + Password: "s3cret", + } + + var acc testutil.Accumulator + err := tc.Gather(&acc) + require.NoError(t, err) + + // tomcat_jvm_memory + jvmMemoryFields := map[string]interface{}{ + "free": int64(17909336), + "total": int64(58195968), + "max": int64(620756992), + } + acc.AssertContainsFields(t, "tomcat_jvm_memory", jvmMemoryFields) + + // tomcat_jvm_memorypool + jvmMemoryPoolFields := map[string]interface{}{ + "init": int64(22020096), + "committed": int64(22020096), + "max": int64(174063616), + "used": int64(17533952), + } + jvmMemoryPoolTags := map[string]string{ + "name": "PS Perm Gen", + "type": "Non-heap memory", + } + acc.AssertContainsTaggedFields(t, "tomcat_jvm_memorypool", jvmMemoryPoolFields, jvmMemoryPoolTags) + + // tomcat_connector + connectorFields := map[string]interface{}{ + "max_threads": int64(200), + "current_thread_count": int64(5), + "current_threads_busy": int64(1), + "max_time": int(68), + "processing_time": int(88), + "request_count": int(2), + "error_count": int(1), + "bytes_received": int64(0), + "bytes_sent": int64(9286), + } + connectorTags := map[string]string{ + "name": "http-apr-8080", + } + acc.AssertContainsTaggedFields(t, "tomcat_connector", connectorFields, connectorTags) + +}