Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding tomcat plugin #3112

Merged
merged 1 commit into from
Aug 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions plugins/inputs/tomcat/README.md
Original file line number Diff line number Diff line change
@@ -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
```
176 changes: 176 additions & 0 deletions plugins/inputs/tomcat/tomcat.go
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After checking for errors, we should make sure the response code was a 200 status and if not return an error.

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{} })
}
95 changes: 95 additions & 0 deletions plugins/inputs/tomcat/tomcat_test.go
Original file line number Diff line number Diff line change
@@ -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)

}