JavaアプリケーションをOpenTelemetry（+Zipkin）で計測してみる

# はじめに

JavaアプリケーションでOpenTelemetryを使って可視化してみたメモ。
APMとOpenTelemetryに関する説明は他の方の投稿を参考にしてください。

以下の投稿のOpenTelemetry（+Zipkin）版です。

- [JavaアプリケーションをElastic APMで計測してみる](https://qiita.com/mkyz08/items/dfcb4785f9444b04a574)


# 環境

使用した環境は以下のとおり。

- CentOS 7.5
- OpenTelemetry(agent) 0.6.0
- Zipkin Server 2.21.5

アプリケーションは測定対象のアプリケーションはWindows上で動かします。


# OpenJDKのインストール

まず、OpenJDK 8をインストールします。

```
# yum install java-1.8.0-openjdk-devel

# java -version
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (build 1.8.0_252-b09)
OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
```

JAVA_HOMEを設定し、PATHにJavaのパスを追加しています。

```
# echo "export JAVA_HOME=$(readlink -e $(which java)|sed 's:/bin/java::')" > /etc/profile.d/java.sh
# echo "PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java.sh
# source /etc/profile
```

# Zipkinのインストール

可視化するためのZipkinをインストールします。
Zipkinに送信された計測データはElasticsearch, cassandra等のデータストアに保管できますが、今回はお試しなのでインメモリに格納します。

インストール自体は以下のようにjarファイルをダウンロードするだけです。

```
# mkdir /opt/zipkin
# curl -L "https://search.maven.org/remote_content?g=io.zipkin&a=zipkin-server&v=LATEST&c=exec" -o /opt/zipkin/zipkin-server.jar
```

以下のコマンドでZipkinサーバーを起動します。
起動時に「--logging.level.zipkin2=DEBUG」をつけるとログレベルをDEBUGに変更できます。

```
STORAGE_TYPE="mem" MEM_MAX_SPANS=1000000 java -Xmx1G -jar /opt/zipkin/zipkin-server.jar
```

最後に動作確認。

```
# curl -L http://[サーバーのホスト名]:9411/health
{
  "status" : "UP",
  "zipkin" : {
    "status" : "UP",
    "details" : {
      "InMemoryStorage{}" : {
        "status" : "UP"
      }
    }
  }
```


# Java agentの設定

Java agentの設定方法は以下のサイトを参考にして実施します。

- https://github.com/open-telemetry/opentelemetry-java-instrumentation

Java agentのモジュールは以下からダウンロードします。

- https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent-all.jar



# javaagentフラグを使用した手動セットアップ

以下のようにjavaコマンド実行時に「-javaagent」フラグを指定します。

```
-javaagent:opentelemetry-javaagent-all.jar -Dota.exporter=zipkin  -Dotel.zipkin.endpoint=http://[Zipkinサーバーのホスト名]:9411/api/v2/spans -Dotel.zipkin.service.name=test_service
```

「-Dio.opentelemetry.auto.slf4j.simpleLogger.defaultLogLevel=debug」を追加するとDEBUGログを出力できるようになります。

Zipkinエクスポーターを利用するためのVMオプションは以下に説明があります。

- [Zipkin exporter](https://github.com/open-telemetry/opentelemetry-java-instrumentation#zipkin-exporter)

以下のサポートしているライブラリ・フレームワークのデータは自動でデータを収集（計測）されます。

- https://github.com/open-telemetry/opentelemetry-java-instrumentation#supported-java-libraries-and-frameworks


# Zipkin UI

Java agentがアプリケーションから収集した情報はZipkinサーバーへ送信されます。
Zipkinサーバーはそれを今回の設定ではインメモリに格納し、Zipkin UIで可視化することができます。

UIのURLは以下のようになります。

- http://[Zipkinサーバーのホスト名]:9411/zipkin/

UIではサービスマップとトレースの情報を表示することができます。

以下はサービスマップです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/1189d9d8-71dd-db5d-f29d-bc6825b77de2.png)

作成したアプリケーションは、以下の流れになっています。
そのため、[test_service]から[test_service2]への流れでサービスマップが表示されています。

- [test_service]GETリクエストを受信。
- 内部の別のGETを受け付けるRESTサービス[test_service2]をリクエスト。レスポンスを受信。
- PostgreSQLへJDBCでSELECTを実行。

次にトレースの情報では、各トレースの情報を検索して以下のように表示することができます。

検索結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/e9d7223a-c9a0-9594-95c7-9b9693441c81.png)

トレース情報

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/74fdd9bc-b4ae-e0cb-1ce0-008b8c81f3da.png)


# エクスポーター

OpenTelemetryのエージェントは、サーバーへ対してデータを送信するエクスポーターを選択することができます。
エクスポーターはデフォルトで以下の4つが利用できるようです。

- In-Memory Exporter(メモリにデータを保持する。デバッグ用。)
- Jaeger Exporter
- Zipkin Exporter
- Logging Exporter

利用できる他のエクスポーターは以下に一覧があり、Elastic APMやNew Relic等があります。

- https://opentelemetry.io/registry/?s=exporter


# （追記）OpenTelemetry APIを使用したSpan作成

前述で自動にてトレースを取得する方法を試してみましたが、今度はOpenTelemetry APIを使用して手動でトレース中のSpanを作成してみます。
以下を参考にしてコーディングしています。

- [OpenTelemetry QuickStart](https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md#tracing)

まずはpom.xmlに以下を追加します。

```xml:pom.xml
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    <version>0.6.0</version>
</dependency>
```

次にコード中に以下のようにOpenTelemetry APIを使用してSpanを作成します。
Span名は"my span"にしています。

```java
    		Tracer tracer =
    			    OpenTelemetry.getTracer("instrumentation-library-name","semver:1.0.0");
    		Span parentSpan = tracer.getCurrentSpan();
    		Span childSpan = tracer.spanBuilder("my span")
    		        .setParent(parentSpan)
    		        .startSpan();
    		childSpan.setAttribute("hoge", "hoge2");

    		try (Scope scope = tracer.withSpan(childSpan)) {
    			// ここに計測対象のロジックが入る。
    		} finally {
    			childSpan.end(); // closing the scope does not end the span, this has to be done manually
    		}
```

Zipkin UIでトレースデータを確認すると以下のように表示されます。
チャートの一番下に"my span"というSpanが追加されているのを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/08cea45d-d9c2-26ec-7f10-38b38c099dbc.png)


# おわりに

[前回](https://qiita.com/mkyz08/items/dfcb4785f9444b04a574)はElastic APMを使ってみたのですが、使い勝手はElastic APMが上かなと感じました。Kibana UIでの検索の機能性やAll in oneでログ・メトリクスも扱えるのは魅力的です。
OpenTelemetryは標準化されているので後からバックエンド（Zipkin等）を変えることもできる柔軟性がポイントかと思います。各バックエンドのUIはトレースの情報を表示させることはほぼ似たような感じで、Elastic APMと比較するとプラスαがない分がマイナス要因。ただし、Elastic APMも有料じゃないと使えない機能もあります（サービスマップとか）。
クラウド対応とか他にも色々検討すべき要素はあるので、どちらを選択するかは各機能の重要度を鑑みて決定すべきことでプロジェクト（組織）によるかなと思います。
個人的には楽で情報量も多いElastic APMを推したい。


# 参考

- [OpenTelemetry公式](https://opentelemetry.io/)
- [OpenTelemetry公式ドキュメント](https://opentelemetry.io/docs/)
- [OpenTelemetry Instrumentation for Java](https://github.com/open-telemetry/opentelemetry-java-instrumentation#opentelemetry-instrumentation-for-java)