mackerel plugin 作ってみた Tomcat requestCount

[技術書典4でmackerel本を買った](https://qiita.com/masudakz/items/34bc3f7ad671a330605e) ことだし、今年度も性能検証仕事が増えそうだし、今のうちに欲しかったmetricをmackerelでとれるようにしようと。


## 欲しかったもの

Munin にはある tomcat request count と tomcat trafficです。特殊なものじゃないのだけど、これがなぜか、 mackerel になくて。
mackerel-plugin-jvm でとれるのは、GC関係、Heap関係なのです。

![jmx_tomcat_request_jk_8009-day.png](https://qiita-image-store.s3.amazonaws.com/0/47549/d458d237-6e97-941c-4a35-4386c1c34aff.png)
![jmx_tomcat_traffic_jk_8009-day.png](https://qiita-image-store.s3.amazonaws.com/0/47549/8c4c7f2a-b684-d84b-1898-9c881d42c50e.png)

## 自力実装の判断

第3者公開版ならどこかにありそうな気もするけれど、ちょっと探しても見つからないので、作ったほうが早そうだと。
すぐに見つからない＝メジャーどころじゃない plugin だと見つかったあとの、動作検証・ライセンス確認・安全性確認まで時間とられちゃいますから。

Munin で取れているので、munin plugin を移植すればできるはず。

そこだけMuninで見ればいい？　
mackerel の「グラフをチャネル(Slackなど)に投稿」「グラフを共有」機能を一度使っちゃうと、それなしには耐えられない体にされてしまうのです。

## Munin tomcat plugin

`/usr/share/munin/plugins/` を見渡せば `jmx_tomcat_request_` がいました。
シェルスクリプトだったのでこのまま移植しちゃいましょう。
autoconf, config 用のパートもありますが、中核部分だけ

```shell
...
cat <<@EOF > ${tmpfile}
open ${PidNum}
get -s -d Catalina -b name="${objname}",type=GlobalRequestProcessor requestCount
close
@EOF

${JAVA_HOME}/bin/java -jar ${jmxterm} -i ${tmpfile} 2>/dev/null | awk \
'BEGIN {
  FS="\n"
  RS=""
}
{
  print "requests.value " $1;
}'
...
```

概要

- Tomcat起動時に記録している pidファイルを参照
- それを引数に、Jmxterm で対象JVMから情報取得

ちょろい。

※この記事書くときに [Munin の GitHub](https://github.com/munin-monitoring) を探したのですが、この plugin ソースは現役じゃなさそうです。古い版を引き継ぎ引き継ぎ使っているみたい。


## コマンドラインで動作確認

```shell-session

$ cat /service/tomcat/pid
xxx
$ java -jar /usr/share/munin/plugins/jmxterm.jar
Welcome to JMX terminal. Type "help" for available commands.
$>open xxx
#Connection to xxx is opened
$>get -s -d Catalina -b name="jk_8009",type=GlobalRequestProcessor requestCount
#mbean = Catalina:name="jk_8009",type=GlobalRequestProcessor:
requestCount = 16;

$>get -s -d Catalina -b name="jk_8009",type=GlobalRequestProcessor *
#mbean = Catalina:name="jk_8009",type=GlobalRequestProcessor:
requestCount = 16;

maxTime = 1408;

bytesReceived = 81;

modelerType = org.apache.coyote.RequestGroupInfo;

bytesSent = 57435;

processingTime = 3235;

errorCount = 0;
$>close
#disconnected
$>quit
#bye
```

ちょろい。traffic用の bytesSent, bytesReceivedもとれそう。

## トラブルシュート

### jmxtermでopenできない

Tomcat起動時のjmxremoteオプションを確認します。port番号は一例。

```
-Dcom.sun.management.jmxremote.port=10080  
-Dcom.sun.management.jmxremote.ssl=false  
-Dcom.sun.management.jmxremote.authenticate=false
```

### InstanceNotFoundException

getしても `#InstanceNotFoundException: Catalina:name="jk_8009",type=GlobalRequestProcessor` でとれなかったら、そのTomcatでのBean名が違うのでしょう。
jmxterm で `help` して `beans -d Catalina` して `type=GlobalRequestProcessor` を探してください。
`http-8080` だったり `"ajp-nio-8009"` だったりかもしれません。


## pidファイル・コマンドtmpfileはトロい

古式蒼然としてますよね。 [jmxtermのマニュアル](https://github.com/jiaqi/jmxterm/wiki/Scripting)見てたら、対話形式だけでなく自動実行用のオプションもいろいろあるじゃないですか。

- tmpfile にjmxtermのコマンドをつくるのはやめて、--noninteract を使おう
- 動的に取得するtomcat pidではなく、固定の com.sun.management.jmxremote.port=10080 を使おう

## 初版

```shell
#!/bin/bash
DefaultJmxterm="/usr/share/munin/plugins/jmxterm.jar"
jmxterm=${jmxterm:-${DefaultJmxterm}}

EPOCH=$(date +'%s')

requestCount=`echo "get -d Catalina -b name=jk_8009,type=GlobalRequestProcessor requestCount" | \
java -jar ${jmxterm} -l localhost:10080 -v silent -n | \
sed -e 's/[^0-9]//g'`

echo -e "tomcat.requestCount\t${requestCount}\t${EPOCH}"
```

短っ！
これを実行許可した `tomcat.access.sh` にして、mackerel-agent.conf に指定します。

```shell-session
# tail -n 2 /etc/mackerel-agent/mackerel-agent.conf
[plugin.metrics.tomcat]
command = "/home/work/tomcat.access.sh"
```

agentを再起動すれば、1-2分で新metricのグラフが出てくるはずです

```shell-session
/etc/init.d/mackerel-agent restart
```

これでとれる requestCount はTomcat起動で0リセットのあとは、単調増加の累積値です。
mackerel は metric が累積値の場合に、グラフを per minute にしてくれる「差分値」グラフの設定があるので、グラフが出てきたら設定変更してください。

やったね。My first mackerel plugin 成功。

## plugin改

改造プラン

- bean名の"jk_8009", jmxportの10080 は環境次第なので環境変数対応する
- requestCount のついでに他の関連metricも取る

jmxterm で `get *` で見えた、errorCount, bytesSent, bytesReceived もmetric として取りましょう。
pluginを４つでグラフを4つにするのはイケてない。
pluginが4行違うmetricを返せば、mackerel は '.'(dot)で区切ったnamespaceに合わせてグラフを統合してくれます。
(requestCount, errorCount) と (bytesSent, byetesreceived) は単位が違うので、namespace を２分割して2枚のグラフにします。

plugin改の出力ゴールイメージ

```
tomcat.access.requestCount      5434    1526870547
tomcat.traffic.bytesReceived    81      1526870547
tomcat.traffic.bytesSent        2105    1526870547
tomcat.access.errorCount        416     1526870547
```

4つの値をとるのに、javaを4回起動するのも、昭和の第5世代脳には耐えがたいので、1回で`get *` して、結果を `sed` で加工調整します。

ほい。

```shell
#!/bin/bash
DefaultJmxterm="/usr/share/munin/plugins/jmxterm.jar"
jmxterm=${jmxterm:-${DefaultJmxterm}}
DefaultJmxport="localhost:10080"
jmxport=${jmxport:-${DefaultJmxport}}
DefaultBeanName='"ajp-nio-8009"'
beanName=${beanName:-${DefaultBeanName}}
EPOCH=$(date +'%s')

echo "get -d Catalina -b name=${beanName},type=GlobalRequestProcessor *" | \
java -jar ${jmxterm} -l ${jmxport} -v silent -n | \
#requestCount = 5434;
#bytesReceived = 81;
#bytesSent = 2105;
#errorCount = 416;
sed -r -n "
s/^(requestCount|errorCount)/tomcat.access.\1/
s/^(bytesReceived|bytesSent)/tomcat.traffic.\1/
s/ = /\t/
s/;/\t${EPOCH}/
/(requestCount|bytesReceived|bytesSent|errorCount)/p
"
#tomcat.access.requestCount      5434    1526870547
#tomcat.traffic.bytesReceived    81      1526870547
#tomcat.traffic.bytesSent        2105    1526870547
#tomcat.access.errorCount        416     1526870547
```

環境変数は、mackerel-agent.conf に env 行を追加してください。

```
[plugin.metrics.tomcat]
command = "/home/work/tomcat.access.sh"
env = { beanName="jk-8009" }
```

## 公開場所

https://github.com/masudakz/mackerel-plugin-tomcat-access