これは、なにをしたくて書いたもの?
以前、WildFlyのMicroProfile Telemetryサブシステムを使って、トレースシグナルをJaegerに送信してみました。
WildFlyのMicroProfile Telemetryサブシステムを使って、トレースを試す - CLOVER🍀
今回はもっと範囲を広げて、メトリクスも含めてやってみようと思います。WildFly 37を使います。
MicroProfile Telemetry
少しおさらい的にMicroProfile Telemetryの情報を見返してみます。
WildFly 37で実装しているMicroProfileの仕様は7.0です。
MicroProfile 7.0に含まれるMicroProfile Telemetryは2.0です。
MicroProfile Telemetry 2.0は、OpenTelemetry Java実装の1.39をベースにしています。
This specification is based on the Java implementation v1.39.0 of OpenTelemetry.
MicroProfile Telemetry / Architecture
MicroProfile Telemetryには固有のAPIはありません。
ソースコードを見るとわかりますが、pom.xmlがあるだけです。
microprofile-telemetry/api at 2.0 · microprofile/microprofile-telemetry · GitHub
<dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-annotations</artifactId> </dependency> <dependency> <groupId>io.opentelemetry.semconv</groupId> <artifactId>opentelemetry-semconv</artifactId> </dependency> </dependencies>
https://github.com/microprofile/microprofile-telemetry/blob/2.0/api/pom.xml#L28-L41
つまり、すごくざっくりと言うとMicroProfile TelemetryというのはOpenTelemetry SDKとインテグレーションする仕様です。
MicroProfile Telemetry / SDK integration
設定は、MicroProfile Configによって行います。
- MicroProfile Telemetry / SDK integration / Enabling OpenTelemetry support
- MicroProfile Telemetry / Configuration
またOpenTelemetry APIが提供するクラスに対して、Jakarta Contexts and Dependency Injection(CDI)によるBeanを提供します。
- 全体
- Tracing
- Metrics
ちなみにログシグナルに対してはロギングフレームワークによるブリッジに任せる方針としているので、MicroProfile Telemetryではほとんど
仕様がありません。
ただ、MicroProfile Telemetry(OpenTelemetryのサポート)を有効化するとログのサポートも有効になります。
WildFlyのMicroProfile Telemetryサブシステム
WildFly 37には、テレメトリーデータに関するいくつかのサブシステムがあります。
- メトリクス
- トレース
いろいろあってどうなっているのかよくわからないのですが、こういう使い分けでよさそうです。
- メトリクス
- WildFlyとJavaVMのMBeanからの基本メトリクスをHTTPエンドポイントで公開する場合は、Metricsサブシステムを使う
- メトリクスをMicrometerで扱いたい場合は、Micrometerサブシステムを使う
- OpenTelemetryを使いたい場合は、OpenTelemetryサブシステムを使う
- MicroProfile Telemetryを使いたい場合は、MicroProfile Telemetryサブシステムを使う
- トレース
- OpenTelemetryを使いたい場合は、OpenTelemetryサブシステムを使う
- MicroProfile Telemetryを使いたい場合は、MicroProfile Telemetryサブシステムを使う
Micrometerサブシステムの立ち位置はわかりやすいのですが、OpenTelemetryサブシステムとMicroProfile Telemetryサブシステムの関係はちょっと
よくわからない感じになります。
OpenTelemetryサブシステムは、WildFlyでOpenTelemetry SDKの設定を行うサブシステムです。
MicroProfile Telemetryサブシステムは、MicroProfile Telemetryをアプリケーションで利用できるようにするサブシステムです。
よって両者には依存関係があり、MicroProfile TelemetryサブシステムはOpenTelemetryサブシステムに依存しています。
レイヤー定義はこちら。
OpenTelemetryサブシステムのレイヤー定義はこちら。
ソースコードはこのあたりですね。
- https://github.com/wildfly/wildfly/tree/37.0.1.Final/microprofile/telemetry-smallrye
- https://github.com/wildfly/wildfly/tree/37.0.1.Final/observability/opentelemetry
OpenTelemetryサブシステムの設定はこちら。
このあたりの設定を見ていると、テレメトリーデータの送信先など全体の共通設定はWildFly(アプリケーションサーバー)側で管理して、
アプリケーション固有の設定はMicroProfile Telemetry+MicroProfile Config経由で設定、という考え方に見えます。
ちょっと気をつけておいた方がよさそうなのは、プロトコルはgRPCで固定されているみたいです。
Exporterの実装に使われているSmallRye OpenTelemetryおよびVert.xではgRPCもhttp/protobufも両方扱えそうなので、プロトコルを指定できても
いいように思うのですが。
またシグナルごとにエンドポイントを変えるといった設定はできなさそうですね。つまり、OpenTelemetry Collector相当のものが必要になります。
とはいえ、いざとなればアプリケーション側で設定すればよい気はしますが。
MicroProfile Telemetry / Configuration
情報を見るのはこれくらいにして、試していってみましょう。
お題
今回のお題は、以下のようにします。
flowchart LR
クライアント --> |curl/HTTP| A
subgraph WildFly
A[JAX-RS Server]
end
subgraph Telemetry
A -- OTLP over gRPC --> B["OpenTelemetry Collector Contrib"]
B -- OTLP over http/protobuf --> C["Grafana Tempo"]
C -- read/write --> D[("MinIO")]
B -- Remote Write --> E["Prometheus"]
F["Grafana"] --> C
F --> E
end
ふだんなら複数のアプリケーションやデータベースを用意してトレーシングデータを収集するのですが、ちょっと登場要素が多すぎるので
今回はアプリケーション側は簡易的な構成にしました。
WildFlyとOpenTelemetry Collector Contribの間がgRPCなのに、その後のGrafana Tempoとの間がhttp/protobufになっているのは、個人的な好みです。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.8 2025-07-15 OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1) OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.9.11 (3e54c93a704957b63ee3494413a2b544fd3d825b) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.8, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-79-generic", arch: "amd64", family: "unix"
OpenTelemetry Collector Contrib。172.19.0.2で動作しているものとします。
$ otelcol-contrib --version otelcol-contrib version 0.135.0
Grafana Tempo。172.19.0.3で動作しているものとします。
$ tempo --version tempo, version 2.8.2 (branch: HEAD, revision: 6b9261586) build user: build date: go version: go1.24.5 platform: linux/amd64 tags: unknown
MinIO。172.19.0.4で動作しているものとします。
$ minio --version minio version RELEASE.2025-09-07T16-13-09Z (commit-id=07c3a429bfed433e49018cb0f78a52145d4bedeb) Runtime: go1.24.6 linux/amd64 License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html Copyright: 2015-2025 MinIO, Inc. $ mcli --version mcli version RELEASE.2025-08-13T08-35-41Z (commit-id=7394ce0dd2a80935aded936b09fa12cbb3cb8096) Runtime: go1.24.6 linux/amd64 Copyright (c) 2015-2025 MinIO, Inc. License GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
Prometheus。172.19.0.5で動作しているものとします。
$ ./prometheus --version prometheus, version 3.5.0 (branch: HEAD, revision: 8be3a9560fbdd18a94dedec4b747c35178177202) build user: root@4451b64cb451 build date: 20250714-16:15:23 go version: go1.24.5 platform: linux/amd64 tags: netgo,builtinassets
Grafana。172.19.0.6で動作しているものとします。
$ grafana-server --version Version 12.1.1 (commit: df5de8219b41d1e639e003bf5f3a85913761d167, branch: release-12.1.1)
Terraformも使います。
$ terraform version Terraform v1.13.2 on linux_amd64
準備(ミドルウェア)
各種ミドルウェアの準備をしていきます。
Prometheus。
設定はこのようにしました。
prometheus.yml
global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: storage: tsdb: out_of_order_time_window: 30m
起動オプションはこんな感じで、リモート書き込みとネイティブヒストグラムを有効にします。
$ ./prometheus --web.enable-remote-write-receiver --enable-feature=native-histograms
--enable-feature=native-histogramsがないと、この構成だとリモート書き込み時にこんなエラーを見ることになります。
time=2025-09-14T14:44:42.719Z level=ERROR source=write_handler.go:192 msg="Error while remote writing the v1 request" component=web err="native histograms are disabled"
今回は主旨で追いませんが、たぶんこれはGrafana TempoのMetrics generatorですね。
Metrics-generator | Grafana Tempo documentation
MinIO。
$ export MINIO_ROOT_USER=minioadmin $ export MINIO_ROOT_PASSWORD=minioadmin $ minio server /var/lib/minio --console-address :9001
バケットの作成。
$ mcli alias set myminio http://minio:9000 minioadmin minioadmin $ mcli mb myminio/tempo-blocks
Grafana Tempo。
/etc/tempo/config.yml
stream_over_http_enabled: true server: http_listen_port: 3200 log_level: info query_frontend: search: duration_slo: 5s throughput_bytes_slo: 1.073741824e+09 metadata_slo: duration_slo: 5s throughput_bytes_slo: 1.073741824e+09 trace_by_id: duration_slo: 5s distributor: receivers: otlp: protocols: grpc: endpoint: "0.0.0.0:4317" http: endpoint: "0.0.0.0:4318" metrics_generator: registry: external_labels: source: tempo cluster: docker-compose storage: path: /var/tempo/generator/wal remote_write: - url: http://172.19.0.5:9090/api/v1/write send_exemplars: true traces_storage: path: /var/tempo/generator/traces storage: trace: backend: s3 s3: bucket: "tempo-blocks" endpoint: "172.19.0.4:9000" insecure: true forcepathstyle: true access_key: "minioadmin" secret_key: "minioadmin" wal: path: /var/tempo/wal # where to store the wal locally overrides: defaults: metrics_generator: processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator generate_native_histograms: both
起動。
$ tempo -config.file /etc/tempo/config.yml
OpenTelemetry Collector Contrib。
/etc/otelcol-contrib/config.yaml
extensions: health_check: pprof: endpoint: 0.0.0.0:1777 zpages: endpoint: 0.0.0.0:55679 receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: exporters: otlphttp: endpoint: http://172.19.0.3:4318 prometheusremotewrite: endpoint: "http://172.19.0.5:9090/api/v1/write" resource_to_telemetry_conversion: enabled: true debug: verbosity: detailed service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlphttp, debug] metrics: receivers: [otlp] processors: [batch] exporters: [prometheusremotewrite, debug] extensions: [health_check, pprof, zpages]
起動。
$ otelcol-contrib --config /etc/otelcol-contrib/config.yaml
Grafanaのデータソース設定。
terraform.tf
terraform { required_version = "1.13.2" required_providers { grafana = { source = "grafana/grafana" version = "4.7.1" } } }
main.tf
provider "grafana" { url = "http://172.19.0.6:3000" auth = "admin:admin" } resource "grafana_data_source" "tempo" { name = "tempo" type = "tempo" url = "http://172.19.0.3:3200" } resource "grafana_data_source" "prometheus" { name = "prometheus" type = "prometheus" url = "http://172.19.0.5:9090" is_default = true json_data_encoded = jsonencode({ httpMethod = "POST" prometheusType = "Prometheus" }) }
作成。
$ terraform init $ terraform apply
準備(アプリケーション)
次はアプリケーションの準備です。簡単なJakarta RESTful Web Services(JAX-RS)を使ったアプリケーションを作成します。
Maven依存関係など。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>wildfly-ee-with-tools</artifactId> <version>37.0.1.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>wildfly-expansion</artifactId> <version>37.0.1.Final</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.enterprise</groupId> <artifactId>jakarta.enterprise.cdi-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.inject</groupId> <artifactId>jakarta.inject-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.microprofile.config</groupId> <artifactId>microprofile-config-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-context</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>ROOT</finalName> </plugins> </build>
JAX-RSの有効化。
src/main/java/org/littlewings/wildfly/telemetry/RestApplication.java
package org.littlewings.wildfly.telemetry; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("/") public class RestApplication extends Application { }
src/main/java/org/littlewings/wildfly/telemetry/HelloResource.java
package org.littlewings.wildfly.telemetry; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import java.util.Map; @ApplicationScoped @Path("/hello") public class HelloResource { @GET @Produces(MediaType.APPLICATION_JSON) public Map<String, String> message() { return Map.of("message", "Hello World!!"); } }
CDI管理Beanにしていますが、特に意味はありません…。
MicroProfile Configを使った、OpenTelemetry SDKの設定。
src/main/resources/META-INF/microprofile-config.properties
otel.sdk.disabled=false otel.service.name=api otel.logs.exporter=none
動作確認はこの時点では省略します。
パッケージングまでは行っておきます。
$ mvn package
WildFlyでMicroProfile Telemetryサブシステムを有効にする
準備ができたので、本題に入っていきましょう。
デフォルト設定のWildFlyではMicroProfile Telemetryサブシステムは有効になっていないので、こちらを有効にするところからですね。
もっとも、先にOpenTelemetryサブシステムを有効にする必要がありますが。
The MicroProfile Telemetry subsystem depends on the opentelemetry subsystem, so it must be added prior to adding MicroProfile Telemetry.
WildFly Admin Guide / Subsystem configuration / OpenTelemetry Subsystem
WildFlyをダウンロードして起動。
$ curl -LO https://github.com/wildfly/wildfly/releases/download/37.0.1.Final/wildfly-37.0.1.Final.zip
$ unzip wildfly-37.0.1.Final.zip
$ cd wildfly-37.0.1.Final
$ bin/standalone.sh \
-Djboss.bind.address=0.0.0.0 \
-Djboss.bind.address.management=0.0.0.0
$ bin/jboss-cli.sh -c
OpenTelemetryサブシステムを追加。
[standalone@localhost:9990 /] /extension=org.wildfly.extension.opentelemetry:add() [standalone@localhost:9990 /] /subsystem=opentelemetry:add()
設定。プロトコルはOpenTelemetryプロトコル(gRPC)、エンドポイントはOpenTelemetry Collector Contribに向けます。
[standalone@localhost:9990 /] /subsystem=opentelemetry:write-attribute(name=exporter-type,value=otlp) [standalone@localhost:9990 /] /subsystem=opentelemetry:write-attribute(name=endpoint,value=http://172.19.0.2:4317)
MicroProfile Telemetryサブシステムを追加。
[standalone@localhost:9990 /] /extension=org.wildfly.extension.microprofile.telemetry:add() [standalone@localhost:9990 /] /subsystem=microprofile-telemetry:add()
ここまで設定したら、WildFlyを再起動。
[standalone@localhost:9990 /] reload
動作確認
では、動作確認していきます。
アプリケーションをデプロイ。
$ cp /path/to/target/ROOT.war standalone/deployments
curlで何回かアクセスしてみます。
$ curl localhost:8080/hello
{"message":"Hello World!!"}
$ curl localhost:8080/hello
{"message":"Hello World!!"}
$ curl localhost:8080/hello
{"message":"Hello World!!"}
$ curl localhost:8080/hello
{"message":"Hello World!!"}
$ curl localhost:8080/hello
{"message":"Hello World!!"}
Grafanaで確認。
メトリクス。


トレース。


OKですね。
オマケ: WildFly Maven Pluginを使って設定する
WildFly Maven Pluginを使って設定する場合は、こんな感じでしょうか。
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>5.1.4.Final</version> <executions> <execution> <id>package</id> <goals> <goal>package</goal> </goals> </execution> </executions> <configuration> <overwrite-provisioned-server>true</overwrite-provisioned-server> <discover-provisioning-info> <version>37.0.1.Final</version> <add-ons> <add-on>wildfly-cli</add-on> </add-ons> <layers-for-jndi> <layer>microprofile-telemetry</layer> </layers-for-jndi> </discover-provisioning-info> <scripts> <script>src/main/jboss-cli-scripts/configure-server.cli</script> </scripts> <packaging-scripts> <packaging-script> <scripts> <script>src/main/jboss-cli-scripts/configure-server.cli</script> </scripts> </packaging-script> </packaging-scripts> </configuration> </plugin>
設定はスクリプトで。
src/main/jboss-cli-scripts/configure-server.cli
/subsystem=opentelemetry:write-attribute(name=exporter-type,value=otlp) /subsystem=opentelemetry:write-attribute(name=endpoint,value=http://172.19.0.2:4317)
おわりに
WildFlyのMicroProfile Telemetryサブシステムを使って、トレースとメトリクスを送信してみました。
久しぶりに扱ったのでMicroProfile TelemetryサブシステムとOpenTelemetryサブシステムの関係を忘れていたり、WildFly以外の準備がかなり大変
だったりしたのですが、ひとまず通せてよかったです。
OpentTelemetryまわりはとにかく準備が大変なのですが、このあたりもうちょっと簡単になるようにしたいですね…。