JavaアプリケーションをJMXで覗いてみるのに、JConsoleもいいんですけど、ちょっとGroovyを使ってみたくなりまして。
こちらのページを参考に、さらっと書いてみました。
Groovy and JMX
http://groovy.codehaus.org/Groovy+and+JMX
とりあえず、監視する何かのプログラムがないと始まらないので、簡単なダミーを。
dummy-script.groovy
Thread.start('dummy-thread') { while (true) { println("waiting...") Thread.sleep(3 * 1000L) } }
JMX関連の起動パラメータの設定をして
$ export JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9012 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
動かしておきます。
$ groovy dummy-script.groovy waiting... waiting...
では、このGroovyスクリプトを除いてみるプログラムを。
まずはimport文。
import javax.management.ObjectName import javax.management.remote.JMXConnectorFactory import javax.management.remote.JMXServiceURL
まあ、普通です。
今回のJMX接続URLと合わせて、MBeanServerConnectionを作成します。
def serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:9012/jmxrmi' def serviceUrl = new JMXServiceURL(serverUrl) def connector = JMXConnectorFactory.connect(serviceUrl)
で、MBeanServerConnectionを取得して、closeするまでの間にいろいろやります。
try { def server = connector.MBeanServerConnection // serverを使って、いろいろやる } finally { connector.close() }
JMXにクエリがあるなんて知らなかったのですが、Groovyのページを見て知ったので、せっかくなので使ってみました。
def querys = [new ObjectName('java.lang:*'), new ObjectName('java.nio:*')] def allModules = querys.collect { objectName -> server .queryNames(objectName, null) .collect { resultName -> new GroovyMBean(server, resultName) } }
ObjectNameを作るときに、「*」とその前の「java.lang」とかのJMXドメインで絞り込めるみたいです。絞り込むときは、MBeanServerConnection#queryNamesとかqueryMBeansで。
今回は、queryNamesを使用しています。検索したJMXドメインは、「java.lang」と「java.nio」です。メソッドの戻り値は、ObjectNameのSetとなります。そこから、ObjectNameを使ってGroovyMBeanを作成しています。
GroovyMBean
http://groovy.codehaus.org/api/groovy/util/GroovyMBean.html
GroovyMBeanを使用すると、MBeanの属性や操作の一覧が簡単に取得できたりします。
では、取得したGroovyMBeanに対して、属性や操作の一覧を出力するようにしてみます。
allModules.each { modules -> modules.each { mbean -> def format = { collection -> collection .collect { " Name: [$it]${System.lineSeparator()}" } .join('') } def safeFormat = { cls -> try { cls.call() } catch (Exception e) { "Got Exception: $e" } } def formatAttributes = { names -> names .collect { name -> " Name: [$name], " + "Value: [${safeFormat { mbean.getProperty(name) }}]" + "${System.lineSeparator()}" } .join('') } def attributes = formatAttributes(mbean.listAttributeNames()) def operations = format(mbean.listOperationNames()) def operationDescriptions = format(mbean.listOperationDescriptions()) def mbeanInfo = """|[MBean] | Name: ${mbean.name()} | Attributes: |${attributes} | Operations: |${operations} | OperationDescriptions: |${operationDescriptions}""".stripMargin() println(mbeanInfo) } }
属性の値を抜く時、今回みたいに総当たりで取得しようとすると、一部の属性は取得できず例外が飛んでくやつがいたので、ちょっとごまかしています。
def safeFormat = { cls -> try { cls.call() } catch (Exception e) { "Got Exception: $e" } } def formatAttributes = { names -> names .collect { name -> " Name: [$name], " + "Value: [${safeFormat { mbean.getProperty(name) }}]" + "${System.lineSeparator()}" } .join('') }
ホントは、取得する項目はきっちり定義するのでしょうが。
これを動作させると、こんな結果が出力されます。
*それなりに出力されるので、けっこう端折っています
[MBean] Name: java.lang:type=Memory Attributes: Name: [Verbose], Value: [false] Name: [HeapMemoryUsage], Value: [javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=90243072, init=74642048, max=1062600704, used=35327544})] Name: [NonHeapMemoryUsage], Value: [javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=24313856, init=24313856, max=136314880, used=17065640})] Name: [ObjectPendingFinalizationCount], Value: [0] Name: [ObjectName], Value: [java.lang:type=Memory] Operations: Name: [gc] OperationDescriptions: Name: [void gc()] [MBean] Name: java.lang:type=GarbageCollector,name=PS MarkSweep Attributes: Name: [LastGcInfo], Value: [null] Name: [CollectionCount], Value: [0] Name: [CollectionTime], Value: [0] Name: [Name], Value: [PS MarkSweep] Name: [Valid], Value: [true] Name: [MemoryPoolNames], Value: [[PS Eden Space, PS Survivor Space, PS Old Gen, PS Perm Gen]] Name: [ObjectName], Value: [java.lang:type=GarbageCollector,name=PS MarkSweep] Operations: OperationDescriptions: [MBean] Name: java.lang:type=Threading Attributes: Name: [ThreadAllocatedMemoryEnabled], Value: [true] Name: [ThreadAllocatedMemorySupported], Value: [true] Name: [ThreadContentionMonitoringEnabled], Value: [false] Name: [DaemonThreadCount], Value: [14] Name: [PeakThreadCount], Value: [16] Name: [CurrentThreadCpuTimeSupported], Value: [true] Name: [ObjectMonitorUsageSupported], Value: [true] Name: [SynchronizerUsageSupported], Value: [true] Name: [ThreadContentionMonitoringSupported], Value: [true] Name: [ThreadCpuTimeEnabled], Value: [true] Name: [AllThreadIds], Value: [[28, 27, 26, 24, 23, 22, 21, 15, 13, 12, 11, 10, 9, 4, 3, 2]] Name: [CurrentThreadCpuTime], Value: [94469784] Name: [CurrentThreadUserTime], Value: [50000000] Name: [ThreadCount], Value: [16] Name: [TotalStartedThreadCount], Value: [24] Name: [ThreadCpuTimeSupported], Value: [true] Name: [ObjectName], Value: [java.lang:type=Threading] Operations: Name: [getThreadCpuTime] Name: [getThreadCpuTime] Name: [getThreadUserTime] Name: [getThreadUserTime] Name: [getThreadAllocatedBytes] Name: [getThreadAllocatedBytes] Name: [dumpAllThreads] Name: [findDeadlockedThreads] Name: [findMonitorDeadlockedThreads] Name: [getThreadInfo] Name: [getThreadInfo] Name: [getThreadInfo] Name: [getThreadInfo] Name: [getThreadInfo] Name: [resetPeakThreadCount] OperationDescriptions: Name: [[J getThreadCpuTime([J p0)] Name: [long getThreadCpuTime(long p0)] Name: [[J getThreadUserTime([J p0)] Name: [long getThreadUserTime(long p0)] Name: [[J getThreadAllocatedBytes([J p0)] Name: [long getThreadAllocatedBytes(long p0)] Name: [[Ljavax.management.openmbean.CompositeData; dumpAllThreads(boolean p0, boolean p1)] Name: [[J findDeadlockedThreads()] Name: [[J findMonitorDeadlockedThreads()] Name: [[Ljavax.management.openmbean.CompositeData; getThreadInfo([J p0, boolean p1, boolean p2)] Name: [javax.management.openmbean.CompositeData getThreadInfo(long p0)] Name: [javax.management.openmbean.CompositeData getThreadInfo(long p0, int p1)] Name: [[Ljavax.management.openmbean.CompositeData; getThreadInfo([J p0, int p1)] Name: [[Ljavax.management.openmbean.CompositeData; getThreadInfo([J p0)] Name: [void resetPeakThreadCount()]
一部の属性とか、かなり大きな出力になります。
JMXに慣れていれば、けっこう簡単に使える?スクリプトで簡単に書くなら、やっぱりGroovyですよね。
では、今回作成したコードを貼っておきます。
jmx-monitor.groovy
import javax.management.ObjectName import javax.management.remote.JMXConnectorFactory import javax.management.remote.JMXServiceURL def serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:9012/jmxrmi' def serviceUrl = new JMXServiceURL(serverUrl) def connector = JMXConnectorFactory.connect(serviceUrl) try { def server = connector.MBeanServerConnection def querys = [new ObjectName('java.lang:*'), new ObjectName('java.nio:*')] def allModules = querys.collect { objectName -> server .queryNames(objectName, null) .collect { resultName -> new GroovyMBean(server, resultName) } } allModules.each { modules -> modules.each { mbean -> def format = { collection -> collection .collect { " Name: [$it]${System.lineSeparator()}" } .join('') } def safeFormat = { cls -> try { cls.call() } catch (Exception e) { "Got Exception: $e" } } def formatAttributes = { names -> names .collect { name -> " Name: [$name], " + "Value: [${safeFormat { mbean.getProperty(name) }}]" + "${System.lineSeparator()}" } .join('') } def attributes = formatAttributes(mbean.listAttributeNames()) def operations = format(mbean.listOperationNames()) def operationDescriptions = format(mbean.listOperationDescriptions()) def mbeanInfo = """|[MBean] | Name: ${mbean.name()} | Attributes: |${attributes} | Operations: |${operations} | OperationDescriptions: |${operationDescriptions}""".stripMargin() println(mbeanInfo) } } } finally { connector.close() }