From a91457e001f38ca8b5c4b8435c8eae2987eb8e05 Mon Sep 17 00:00:00 2001
From: mlindes <mlindes@hushmail.com>
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 00000000..d2fa2e72
--- /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://<host>/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 00000000..dfde52ca
--- /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 00000000..8bdddd6d
--- /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 = `<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="/manager/xform.xsl" ?>
+<status>
+  <jvm>
+    <memory free='17909336' total='58195968' max='620756992'/>
+    <memorypool name='PS Eden Space' type='Heap memory' usageInit='8912896' usageCommitted='35651584' usageMax='230686720' usageUsed='25591384'/>
+    <memorypool name='PS Old Gen' type='Heap memory' usageInit='21495808' usageCommitted='21495808' usageMax='465567744' usageUsed='13663040'/>
+    <memorypool name='PS Survivor Space' type='Heap memory' usageInit='1048576' usageCommitted='1048576' usageMax='1048576' usageUsed='1032208'/>
+    <memorypool name='Code Cache' type='Non-heap memory' usageInit='2555904' usageCommitted='2555904' usageMax='50331648' usageUsed='1220096'/>
+    <memorypool name='PS Perm Gen' type='Non-heap memory' usageInit='22020096' usageCommitted='22020096' usageMax='174063616' usageUsed='17533952'/>
+  </jvm>
+  <connector name='"ajp-apr-8009"'>
+    <threadInfo maxThreads="200" currentThreadCount="0" currentThreadsBusy="0"/>
+    <requestInfo maxTime="0" processingTime="0" requestCount="0" errorCount="0" bytesReceived="0" bytesSent="0"/>
+    <workers>
+    </workers>
+  </connector>
+  <connector name='"http-apr-8080"'>
+    <threadInfo maxThreads="200" currentThreadCount="5" currentThreadsBusy="1"/>
+    <requestInfo maxTime="68" processingTime="88" requestCount="2" errorCount="1" bytesReceived="0" bytesSent="9286"/>
+    <workers>
+      <worker stage="S" requestProcessingTime="4" requestBytesSent="0" requestBytesReceived="0" remoteAddr="127.0.0.1" virtualHost="127.0.0.1" method="GET" currentUri="/manager/status/all" currentQueryString="XML=true" protocol="HTTP/1.1"/>
+    </workers>
+  </connector>
+</status>`
+
+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)
+
+}
-- 
GitLab