CLOVER🍀

That was when it all began.

PrometheusのJMX Exporterを試す

これは、なにをしたくて書いたもの?

  • Prometheus向けのJavaのExporterで、JMXを使ったものがあるというので

こちらを試してみようと、書いたエントリです。

JMX Exporter

JMXの情報から、Prometheus向けの情報を出力することができるExporterです。

GitHub - prometheus/jmx_exporter: A process for exposing JMX Beans via HTTP for Prometheus consumption

こちらを使うことで、MBeanを構成していればPrometheus向けのExporterにすることができます。

現在のバージョンは、0.11.0です。

実行形態は、次の2つがあります。

  • 対象のアプリケーションにJava Agentとして組み込むjmx_prometheus_javaagent
  • 単独のHTTPサーバーとして起動させ、リモートのJMXターゲットにアクセスしてExporterとなるjmx_prometheus_httpserver

推奨は、Java Agentとなり、このエントリでもJava Agentを扱います。

単独のHTTPサーバーとして実行する方法は、以下を参照しましょう。

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/run_sample_httpserver.sh

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/example_configs/httpserver_sample_config.yml

お題

今回は、お題として簡単なJAX-RSアプリケーションにMBeanによるメトリクス取得を有効にしたApache Igniteを載せ、
JAX-RSのリソースクラスからキャッシュにアクセスしたりしたメトリクスをPrometheusで収集してみます。

JAX-RSの実行には、RESTEasy+Undertowを使用します。

環境

今回の環境は、こちらです。

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)


$ mvn -version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-45-generic", arch: "amd64", family: "unix"

Prometheusは、2.7.1を使います。

準備

Maven依存関係は、こちら。

        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-undertow</artifactId>
            <version>3.6.3.Final</version>
        </dependency>

        <dependency>
            <groupId>org.apache.ignite</groupId>
            <artifactId>ignite-core</artifactId>
            <version>2.7.0</version>
        </dependency>

今回はSingle JARにすることにしましょう。Maven Shade Pluginを使用します。

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <transformers>
                        <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>org.littlewings.prometheus.Server</mainClass>
                        </transformer>
                    </transformers>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

サンプルアプリケーション

それでは、サンプルアプリケーションを作成していきます。

キャッシュにアクセスする、JAX-RSリソースクラス。Apache Igniteのキャッシュは、起動側で定義します。
src/main/java/org/littlewings/prometheus/CacheResource.java

package org.littlewings.prometheus;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;

@Path("cache")
public class CacheResource {
    @PUT
    @Path("{key}")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public String put(@PathParam("key") String key, String value) {
        IgniteCache<String, String> cache = Ignition.ignite().getOrCreateCache("myCache");
        cache.put(key, value);

        return String.format("put entry = %s / %s%n", key, value);
    }

    @GET
    @Path("{key}")
    @Produces(MediaType.TEXT_PLAIN)
    public String get(@PathParam("key") String key) {
        IgniteCache<String, String> cache = Ignition.ignite().getOrCreateCache("myCache");
        String value = cache.get(key);

        return String.format("get entry = %s / %s%n", key, value);
    }
}

単純に、キャッシュにput/getするだけのクラスですね。

JAX-RSの有効化。
src/main/java/org/littlewings/prometheus/JaxrsActivator.java

package org.littlewings.prometheus;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("")
public class JaxrsActivator extends Application {
    Set<Object> singletons = new HashSet<>();

    public JaxrsActivator() {
        singletons.add(new CacheResource());
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

起動クラス。
src/main/java/org/littlewings/prometheus/Server.java

package org.littlewings.prometheus;

import java.time.LocalDateTime;

import io.undertow.Undertow;
import io.undertow.servlet.api.DeploymentInfo;
import org.apache.ignite.Ignite;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.CacheConfiguration;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;

public class Server {
    public static void main(String... args) {
        Ignite ignite = Ignition.start();

        CacheConfiguration<String, String> cacheConfiguration =
                new CacheConfiguration<String, String>()
                        .setName("myCache")
                        .setStatisticsEnabled(true);
        ignite.addCacheConfiguration(cacheConfiguration);
        ignite.getOrCreateCache("myCache");

        UndertowJaxrsServer server = new UndertowJaxrsServer();

        DeploymentInfo deploymentInfo = server.undertowDeployment(JaxrsActivator.class);
        deploymentInfo.setDeploymentName("prometheus-jmx-exporter-example");
        deploymentInfo.setContextPath("");

        server.deploy(deploymentInfo);

        server.start(Undertow.builder().addHttpListener(8080, "localhost"));

        System.out.printf("[%s] Server startup.%n", LocalDateTime.now());
        System.console().readLine("> Enter stop.");

        server.stop();

        ignite.close();
        Ignition.stopAll(true);
    }
}

Apache Igniteの起動とキャッシュ定義は、こちら。

        Ignite ignite = Ignition.start();

        CacheConfiguration<String, String> cacheConfiguration =
                new CacheConfiguration<String, String>()
                        .setName("myCache")
                        .setStatisticsEnabled(true);
        ignite.addCacheConfiguration(cacheConfiguration);
        ignite.getOrCreateCache("myCache");

メトリクスを有効にしてあります。

Cache Metrics

また、Enterを打ったら停止するようにはしています(Apache Igniteがなかなか止まらないので、Ctrl-cを実行した方が早いですが…)。

こちらをパッケージングします。

$ mvn package

単純に実行する場合は、こちら。

$ java -jar target/jmx-exporter-example-0.0.1-SNAPSHOT.jar

JConsoleなどを使用すると、JMXでMBeanの情報を見ることができます。

確認。

$ curl -i -XPUT -H 'Content-Type: text/plain' localhost:8080/cache/key1 -d 'value1'
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain;charset=UTF-8
Content-Length: 26
Date: Tue, 26 Feb 2019 12:41:11 GMT

put entry = key1 / value1


$ curl -i localhost:8080/cache/key1
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain;charset=UTF-8
Content-Length: 26
Date: Tue, 26 Feb 2019 12:41:37 GMT

get entry = key1 / value1


$ curl -i localhost:8080/cache/key2
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain;charset=UTF-8
Content-Length: 24
Date: Tue, 26 Feb 2019 12:41:42 GMT

get entry = key2 / null

これで、準備は完了です。

JMX Exporterを使う

それでは、JMX Exporterを使います。

まずは、JMX Exporterをダウンロード。Java Agent版をダウンロードします。

$ wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.11.0/jmx_prometheus_javaagent-0.11.0.jar

JMX ExporterのJava Agentを使ってアプリケーションを起動するには、「-javaagent」の後に以下のパラメータを入力します。

 -javaagent:jmx_prometheus_javaagent-0.11.0.jar=[host:]<port>:<yaml configuration file>

Java Agent側で、アプリケーションとは別のHTTPサーバーを起動します。この時にバインドするアドレス、ポートと設定ファイルを
指定することになります。

hostはオプションです。この内容は、Java AgentのJARを指定して、「=」だけつければ確認することができます。

$ java -javaagent:jmx_prometheus_javaagent-0.11.0.jar= -jar target/jmx-exporter-example-0.0.1-SNAPSHOT.jar
Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration file>

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java#L33

では、設定ファイルを用意します。中身は、最低限だと中身は実は空で良かったりします。

$ touch config.yml

起動してみましょう。Java Agentで起動するExporterは、9080ポートでリッスンすることにします。

$ java -javaagent:jmx_prometheus_javaagent-0.11.0.jar=9080:config.yml -jar target/jmx-exporter-example-0.0.1-SNAPSHOT.jar

確認してみましょう。

$ curl localhost:9080
# HELP jvm_info JVM version info
# TYPE jvm_info gauge
jvm_info{version="1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12",vendor="Oracle Corporation",runtime="OpenJDK Runtime Environment",} 1.0
# HELP jmx_exporter_build_info A metric with a constant '1' value labeled with the version of the JMX exporter.
# TYPE jmx_exporter_build_info gauge
jmx_exporter_build_info{version="0.11.0",name="jmx_prometheus_javaagent",} 1.0
# HELP jvm_memory_bytes_used Used bytes of a given JVM memory area.
# TYPE jvm_memory_bytes_used gauge
jvm_memory_bytes_used{area="heap",} 7.374024E7
jvm_memory_bytes_used{area="nonheap",} 4.6803216E7

〜省略〜

Apache Igniteから取得できるMBeanの情報も、出力されています。

# HELP org_apache_18b4aac2_WriteBehindFlushFrequency Flush frequency interval in milliseconds. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.pr
ocessors.cache.CacheClusterMetricsMXBeanImpl"><>WriteBehindFlushFrequency)
# TYPE org_apache_18b4aac2_WriteBehindFlushFrequency untyped
org_apache_18b4aac2_WriteBehindFlushFrequency{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} -1.0
org_apache_18b4aac2_WriteBehindFlushFrequency{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} -1.0
# HELP org_apache_18b4aac2_NodesFailed Nodes failed count. (org.apache<clsLdr=18b4aac2, group=SPIs, name=TcpDiscoverySpi><>NodesFailed)
# TYPE org_apache_18b4aac2_NodesFailed untyped
org_apache_18b4aac2_NodesFailed{group="SPIs",name="TcpDiscoverySpi",} 0.0
# HELP org_apache_18b4aac2_CacheMisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_CacheMisses untyped
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 0.0
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 0.0

ちょっと試してみます。

$ curl -i -XPUT -H 'Content-Type: text/plain' localhost:8080/cache/key1 -d 'value1'
$ curl -i -XPUT -H 'Content-Type: text/plain' localhost:8080/cache/key2 -d 'value2'

$ curl -i localhost:8080/cache/key1
$ curl -i localhost:8080/cache/key1
$ curl -i localhost:8080/cache/key1

$ curl -i localhost:8080/cache/key3

データを2つ登録、キャッシュにヒットするキーを3回指定、キャッシュミスするキーを1回指定しました。

確認。

$ curl -s localhost:9080 | grep CachePut
# HELP org_apache_18b4aac2_CachePuts Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)
# TYPE org_apache_18b4aac2_CachePuts untyped
org_apache_18b4aac2_CachePuts{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 2.0
org_apache_18b4aac2_CachePuts{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 2.0


$ curl -s localhost:9080 | grep CacheHits
# HELP org_apache_18b4aac2_CacheHits Number of hits. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheHits)
# TYPE org_apache_18b4aac2_CacheHits untyped
org_apache_18b4aac2_CacheHits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 3.0
org_apache_18b4aac2_CacheHits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 3.0


$ curl -s localhost:9080 | grep CacheMisses
# HELP org_apache_18b4aac2_CacheMisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_CacheMisses untyped
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 1.0
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 1.0

OKそうですね。

なお、最初にサンプリングで表示した時に、JavaVMの情報が出力されていましたが、これはJVM ClientのHot Spot向けのものが
組み込まれているからですね。

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java#L53

https://github.com/prometheus/client_java/blob/parent-0.3.0/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java

Prometheusでも確認してみましょう。

接続設定。
※「192.168.0.2」はアプリケーションが動作しているIPアドレスです

scrape_configs:
  - job_name: 'jmx'
    static_configs:
    - targets: ['192.168.0.2:9080']

ちゃんと認識できていることが確認できます。

f:id:Kazuhira:20190226221620p:plain

f:id:Kazuhira:20190226221633p:plain

ただ、ちょっとメトリクスの名前が…。

JMX Exporterの設定を変えてみる

先ほどは設定ファイルを空っぽで用意したので、設定を書いていってみましょう。

設定項目とサンプルは、こちらです。

Configuration

https://github.com/prometheus/jmx_exporter/tree/parent-0.11.0/example_configs

設定項目のデフォルト値は説明が書いてあるもの、ないものがありますが、ないものについてはこちらを見るとよいでしょうね。

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxCollector.java#L40-L64

設定のうち、hostPort、username、password、jmxUrlは単独でHTTPサーバーを実行し、他のアプリケーショに対して
JMXでリモート接続する場合に使います。

今回は、それ以外を使用しましょう。

ちなみにですね、アプリケーションを起動したまま設定ファイルを変更すると、JMX Exporter側で変更を検知してリロード
してくれます。

設定内容は、出力内容を変換したり、絞り込んだりするものが多いです。今回は、こちらを題材にして見ていきましょう。

$ curl -s localhost:9080 | grep -iE 'CachePuts|CacheGets|CacheHits|CacheMisses'
# HELP org_apache_18b4aac2_CacheGets Number of gets. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheGets)
# TYPE org_apache_18b4aac2_CacheGets untyped
org_apache_18b4aac2_CacheGets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 4.0
org_apache_18b4aac2_CacheGets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 4.0
# HELP org_apache_18b4aac2_CacheHits Number of hits. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheHits)
# TYPE org_apache_18b4aac2_CacheHits untyped
org_apache_18b4aac2_CacheHits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 3.0
org_apache_18b4aac2_CacheHits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 3.0
# HELP org_apache_18b4aac2_CachePuts Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)
# TYPE org_apache_18b4aac2_CachePuts untyped
org_apache_18b4aac2_CachePuts{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 2.0
org_apache_18b4aac2_CachePuts{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 2.0
# HELP org_apache_18b4aac2_CacheMisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_CacheMisses untyped
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 1.0
org_apache_18b4aac2_CacheMisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 1.0

「lowercaseOutputName」をtrueにしてみます。

lowercaseOutputName: true

出力されるメトリクスが、小文字になりました。

$ curl -s localhost:9080 | grep -iE 'CachePuts|CacheGets|CacheHits|CacheMisses'
# HELP org_apache_18b4aac2_cachehits Number of hits. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheHits)
# TYPE org_apache_18b4aac2_cachehits untyped
org_apache_18b4aac2_cachehits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 3.0
org_apache_18b4aac2_cachehits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 3.0
# HELP org_apache_18b4aac2_cacheputs Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)
# TYPE org_apache_18b4aac2_cacheputs untyped
org_apache_18b4aac2_cacheputs{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 2.0
org_apache_18b4aac2_cacheputs{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 2.0
# HELP org_apache_18b4aac2_cachemisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_cachemisses untyped
org_apache_18b4aac2_cachemisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 1.0
org_apache_18b4aac2_cachemisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 1.0
# HELP org_apache_18b4aac2_cachegets Number of gets. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheGets)
# TYPE org_apache_18b4aac2_cachegets untyped
org_apache_18b4aac2_cachegets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 4.0
org_apache_18b4aac2_cachegets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheLocalMetricsMXBeanImpl\"",} 4.0

今回のケースでは効果がありませんが、「lowercaseOutputLabelNames」をtrueにすると、ラベル名も小文字にしてくれます。

lowercaseOutputName: true
lowercaseOutputLabelNames: true

ここに、「whitelistObjectNames」を追加して、出力対象をフィルタリングしてみます。

lowercaseOutputName: true
lowercaseOutputLabelNames: true
whitelistObjectNames:
  - "org.apache:name=\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\",*"
  - "org.xnio:provider=\"nio\",*"

ちょっとXNIOのものも入れてしまいましたが、ObjectNameを指定することで出力対象を絞り込めます。配列での複数指定が
可能です。「*」を使っているのは、ObjectNameのクエリですね。

ObjectName (Java Platform SE 8)

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxCollector.java#L150

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxScraper.java#L100-L104

例えば、Apache Igniteのものは指定のものだけが残りました。

$ curl -s localhost:9080 | grep -iE apache | grep -v '#' | perl -wp -e 's!.+name="\\"(.+)\\"".+!$1!' | sort -u
org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl

項目別に見ても、CacheLocalMetricsMXBeanImplの分がなくなりましたね。

$ curl -s localhost:9080 | grep -iE 'CachePuts|CacheGets|CacheHits|CacheMisses'
# HELP org_apache_18b4aac2_cachehits Number of hits. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheHits)
# TYPE org_apache_18b4aac2_cachehits untyped
org_apache_18b4aac2_cachehits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 3.0
# HELP org_apache_18b4aac2_cacheputs Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)
# TYPE org_apache_18b4aac2_cacheputs untyped
org_apache_18b4aac2_cacheputs{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 2.0
# HELP org_apache_18b4aac2_cachemisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_cachemisses untyped
org_apache_18b4aac2_cachemisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 1.0
# HELP org_apache_18b4aac2_cachegets Number of gets. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheGets)
# TYPE org_apache_18b4aac2_cachegets untyped
org_apache_18b4aac2_cachegets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 4.0

あくまでObjectNameで絞り込んでいるので、属性などは考慮されません。

whitelistObjectNamesを指定しない場合は、全MBeanの情報が出力されます。

A list of ObjectNames to query. Defaults to all mBeans.

whitelistObjectNamesに対して「blacklistObjectNames」という項目もあり、blacklistObjectNamesに指定した項目も出力対象外に
なりますが、これはwhitelistObjectNamesよりも優先されます。

A list of ObjectNames to not query. Takes precedence over whitelistObjectNames. Defaults to none.

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxScraper.java#L106-L110

最後はrulesです。rulesを使うことで、この長いメトリクスの名前をもっと短くしたり、出力対象を正規表現で絞ったりできます。

$ curl -s localhost:9080 | grep -iE 'CachePuts|CacheGets|CacheHits|CacheMisses'
# HELP org_apache_18b4aac2_cachehits Number of hits. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheHits)
# TYPE org_apache_18b4aac2_cachehits untyped
org_apache_18b4aac2_cachehits{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 3.0
# HELP org_apache_18b4aac2_cacheputs Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)
# TYPE org_apache_18b4aac2_cacheputs untyped
org_apache_18b4aac2_cacheputs{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 2.0
# HELP org_apache_18b4aac2_cachemisses Number of misses. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheMisses)
# TYPE org_apache_18b4aac2_cachemisses untyped
org_apache_18b4aac2_cachemisses{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 1.0
# HELP org_apache_18b4aac2_cachegets Number of gets. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CacheGets)
# TYPE org_apache_18b4aac2_cachegets untyped
org_apache_18b4aac2_cachegets{group="myCache",name="\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\"",} 4.0

rulesの説明。

A list of rules to apply in order, processing stops at the first matching rule. Attributes that aren't matched aren't collected. If not specified, defaults to collecting everything in the default format.

最初に一致した定義が使用され、一致しなかった場合は収集されません、と。なお、指定しなかった場合は全部が収集対象になります。

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxCollector.java#L362

https://github.com/prometheus/jmx_exporter/blob/parent-0.11.0/collector/src/main/java/io/prometheus/jmx/JmxCollector.java#L214-L215

例えば、次のように設定してみます。

lowercaseOutputName: true
lowercaseOutputLabelNames: true
whitelistObjectNames:
  - "org.apache:name=\"org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl\",*"
  - "org.xnio:provider=\"nio\",*"
rules:
  - pattern: "^org.apache<clsLdr=.+, group=(.+), name=\".+\"><>(.+): .+"
    name: $2
    attrNameSnakeCase: true
    labels:
      cache_name: $1

patternでは、収集対象を絞り、かつキャプチャするための正規表現を書きます。patternは、以下のフォーマットで書きます。

domain<beanpropertyName1=beanPropertyValue1, beanpropertyName2=beanPropertyValue2, ...><key1, key2, ...>attrName: value

Pattern input

とはいえ、こう書かれてもどういう文字列に対して正規表現を書けばいいのかわからないのでは?と思いますが、メトリクス取得時に
出力されているHELPコメントをよくよく見ると、各メトリクスに対するフォーマットが出力されています。

# HELP cache_puts Number of puts. (org.apache<clsLdr=18b4aac2, group=myCache, name="org.apache.ignite.internal.processors.cache.CacheClusterMetricsMXBeanImpl"><>CachePuts)

これを使うと、正規表現が書けますね。

今回は、メトリクス名には属性名を用い、ラベルにはキャッシュ名(group)を使いました。また「attrNameSnakeCase」をtrueに
することで属性名をSnakeCaseに変換します。

つまり、こうなります。

$ curl -s localhost:9080 | grep -iE 'cache_puts|cache_gets|cache_hits|cache_misses' | grep -v '#'
cache_gets{cache_name="myCache",} 4.0
cache_misses{cache_name="myCache",} 1.0
cache_puts{cache_name="myCache",} 2.0
cache_hits{cache_name="myCache",} 3.0

だいぶスッキリ。

valueそのものも、rulesで設定できたりします。

その他の例は、サンプルとして用意されている設定ファイルを見てみるとよいでしょう。

https://github.com/prometheus/jmx_exporter/tree/parent-0.11.0/example_configs

まとめ

PrometheusのJMX Exporter(Java Agent)を使用してみました。

最初、とっつきにくかったですがサンプルを眺めたり、ソースコードを眺めたりしていたらなんとなく使い方がわかってきました。
とりあえず、これくらいでいいでしょうかね。