CLOVER🍀

That was when it all began.

Brave JAXRS2で、JAX-RSでもDistributed Tracing #javaee

この記事は、「Java EE Advent Calendar 2016 - Qiita」の14日目の記事となります。
昨日は、@n_agetsuさんの「Commons Lang 3.5 でJava EEにBreakerを組み込む - 見習いプログラミング日記」でした。
明日は、@khasunumaさんのご担当となります。

JAX-RSでもZipkin

今回のお題には、JAX-RSとZipkinを使います。Zipkinを使うのは初めてなのですが、JAX-RS用の
モジュールも用意されているようなので、これを機に試してみようと思いまして。

なんとなく、このあたりを使うならSpring Cloud〜なイメージもありますが、今回はJAX-RSの素体で押し通します!
※ZipkinはSpring Bootを使って作られていますが

Zipkinって?

最近のMicroservicesの話題でよく出てくる、Distributed Tracingを実現するためのライブラリです。

OpenZipkin · A distributed tracing system

アーキテクチャについてはこちらに図がありますが、リクエストとレスポンス時にZipkin Serverに
メタデータを記録する仕組みみたいです。

Architecture · OpenZipkin

この集められたトレースデータを、Spanと呼ぶそうです。また、Zipkinにデータを送る役割を
受け持つ人を、Reporterと呼ぶと。

このあたりで使うライブラリが、Braveというものにまとめられているようなので、こちらを
使って簡単なDistributed Tracingを行ってみます。
GitHub - openzipkin/brave: Java distributed tracing implementation compatible with Zipkin backend services.

では、試してみましょう。

構成とゴール

今回は、以下の構成でアプリケーションを作成します。

  • Client … JAX-RS Client+Brave JAXRS2
  • Server … JAX-RS Server+Brave JAXRS2 / JPA+Brave MySQL

せっかくなので、JPAを使ってMySQLにアクセスして、ClientからMySQLまでのアクセスを
Zipkinに記録し、Zipkin UIで見るところまでをゴールにしたいと思います。

別にDistributedな感じはしませんけど…。

MySQL側については、単にアクセスできるMySQL Serverがあればよいので、
準備などは割愛します。

Zipkin Server & Zipkin UI

Zipkin ServerとUIについては、Zipkin素体を使ってさっくりと起動します。
※最初、Zipkin ServerのDockerイメージとExperimental new UI for Zipkinを使っていたのですが、他と割と違うことに気づきやめました…

2016/12/22追記)
最初、Spring Cloud Sleuthを使っていたのですが、@makingさんに要らないと突っ込まれたので、外してZipkinのみで
いく感じにしました。

確かに不要でした、ありがとうございましたー。

Maven依存関係を、このように定義。

    <dependencies>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
            <version>1.17.1</version>
        </dependency>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
            <version>1.17.1</version>
        </dependency>
    </dependencies>

Spring Bootのプラグインも入れておきます。

        <plugins>
            <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
              <version>1.4.2.RELEASE</version>
              <executions>
                <execution>
                  <goals>
                    <goal>repackage</goal>
                  </goals>
                </execution>
              </executions>
            </plugin>

アプリケーションは、これだけ。
src/main/java/javaeeadventcalendar/ZipkinServerApp.java

package javaeeadventcalendar;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.EnableZipkinServer;

@SpringBootApplication
@EnableZipkinServer
public class ZipkinServerApp {
    public static void main(String... args) {
        SpringApplication.run(ZipkinServerApp.class, args);
    }
}

Zipkin Serverは、9411ポートでリッスンさせるのが通常のようなのでパッケージング後に「--server.port」指定で起動します。

$ mvn package
$ java -jar zipkin-ui/target/zipkin-ui-0.0.1-SNAPSHOT.jar --server.port=9411

http://localhost:9411/」にアクセスすると、Zipkin UIが見れます。

ここまでで、Zipkin ServerとUIの用意が整いました。

JAX-RS Server & JPA

JAX-RS Server側とJPAのサンプルプログラムを作成します。Brave MySQLの都合上、
アプリケーションサーバー(特にモジュールシステムのあるWildFly)を持ってくると
手間がかかることがわかったので、今回はRESTEasy+Netty 4で実装します。

JPAの実装は、Hibernateとします。

Maven依存関係

使用したMaven依存関係は、こちら。

        <!-- JAX-RS / RESTEasy and Netty 4 -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-netty4</artifactId>
            <version>3.0.19.Final</version>
            <exclusions>
                <exclusion>
                    <groupId>org.jboss.logging</groupId>
                    <artifactId>jboss-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.0.42.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>3.0.19.Final</version>
        </dependency>

        <!-- JPA -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.2.5.Final</version>
        </dependency>

        <!-- Brave Libraries -->
        <!-- Brave JAXRS2 -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-jaxrs2</artifactId>
            <version>3.16.0</version>
        </dependency>
        <!-- Brave MySQL -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-mysql</artifactId>
            <version>3.16.0</version>
        </dependency>
        <!-- Reporter -->
        <dependency>
            <groupId>io.zipkin.reporter</groupId>
            <artifactId>zipkin-sender-urlconnection</artifactId>
            <version>0.6.9</version>
        </dependency>

        <!-- MySQL JDBC Driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

JAX-RSJPAの部分ははしょりますが、Braveの部分だけ説明します。

Zipkinとの連携用に、JAX-RSMySQL用にそれぞれBraveのライブラリを追加します。

        <!-- Brave Libraries -->
        <!-- Brave JAXRS2 -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-jaxrs2</artifactId>
            <version>3.16.0</version>
        </dependency>
        <!-- Brave MySQL -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-mysql</artifactId>
            <version>3.16.0</version>
        </dependency>

ただ、これだけだとZipkinにデータを送信する人がいないので、Reporterとして今回は
URLConnectionを使う実装を追加します。

        <!-- Reporter -->
        <dependency>
            <groupId>io.zipkin.reporter</groupId>
            <artifactId>zipkin-sender-urlconnection</artifactId>
            <version>0.6.9</version>
        </dependency>

JavaでのRepoterは、こちら。
GitHub - openzipkin/zipkin-reporter-java: Shared library for reporting zipkin spans on transports such as http or kafka

依存関係は以上です。

JPA Entity

とりあえず、話が単純なJPAのEntityから。
src/main/java/javaeeadventcalendar/Book.java

package javaeeadventcalendar;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "book")
public class Book implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private String isbn;
    @Column
    private String title;
    @Column
    private int price;

    // getter/setterは省略
}

テーブル定義のSQLは、テーブルの自動生成が有効な場合にHibernateが出力するものをまんま使用しました。

    create table book (
        isbn varchar(255) not null,
        price integer,
        title varchar(255),
        primary key (isbn)
    ) ENGINE=InnoDB;

persistence.xmlとEntityManager

persistence.xmlはこちら。EE環境ではないので、transaction-typeはRESOURCE_LOCALです。
src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                                 http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
    <persistence-unit name="brave.jaxrs.pu" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:mysql://localhost:3306/practice?statementInterceptors=com.github.kristofa.brave.mysql.MySQLStatementInterceptor&amp;zipkinServiceName=myJaxrsMySqlService&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;characterSetResults=utf-8&amp;useServerPrepStmts=true&amp;useLocalSessionState=true&amp;elideSetAutoCommits=true&amp;alwaysSendSetIsolation=false&amp;useSSL=false"/>
            <property name="javax.persistence.jdbc.user" value="kazuhira"/>
            <property name="javax.persistence.jdbc.password" value="password"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL57InnoDBDialect"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

で、JDBCの接続文字列をずらずらと書いているのですが、今回のテーマで重要なのは次の2つです。

statementInterceptors=com.github.kristofa.brave.mysql.MySQLStatementInterceptor&zipkinServiceName=myJaxrsMySqlService

https://github.com/openzipkin/brave/tree/master/brave-mysql

Brave MySQLは、MySQL JDBC Driver(Connector/J)のStatementInterceptorの仕組みで
トレーシングを実現しているようですね。

あと、Java EE環境ではないのでEntityManagerを使うための簡易クラスを用意しておきました。
src/main/java/javaeeadventcalendar/EntityManagerProvider.java

package javaeeadventcalendar;

import java.util.function.Consumer;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class EntityManagerProvider {
    private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("brave.jaxrs.pu");

    public static <T> T call(Function<EntityManager, T> func) {
        EntityManager entityManager = emf.createEntityManager();
        try {
            return func.apply(entityManager);
        } finally {
            entityManager.close();
        }
    }

    public static void run(Consumer<EntityManager> func) {
        EntityManager entityManager = emf.createEntityManager();
        try {
            func.accept(entityManager);
        } finally {
            entityManager.close();
        }
    }
}

JAX-RS側のクラス

JAX-RSリソースクラスは、単純なものにしています。
src/main/java/javaeeadventcalendar/BookResource.java

package javaeeadventcalendar;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

@Path("book")
public class BookResource {
    @GET
    @Path("{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    public Book find(@PathParam("isbn") String isbn) {
        return EntityManagerProvider.call(em -> em.find(Book.class, isbn));
    }

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response register(Book book, @Context UriInfo uriInfo) {
        EntityManagerProvider.run(em -> {
            EntityTransaction tx = em.getTransaction();
            try {
                tx.begin();
                em.persist(book);
                tx.commit();
            } catch (Exception e) {
                tx.rollback();

                throw new RuntimeException(e);
            }
        });

        return Response.created(uriInfo.getRequestUriBuilder().path(book.getIsbn()).build()).build();
    }
}

ちょっと変わるのがApplicationのサブクラスで、こちらのgetSingletonsメソッドでBraveに関する
設定をしたインスタンスを返却します。
src/main/java/javaeeadventcalendar/JaxrsActivator.java

package javaeeadventcalendar;

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

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.jaxrs2.BraveTracingFeature;
import zipkin.Span;
import zipkin.reporter.AsyncReporter;
import zipkin.reporter.urlconnection.URLConnectionSender;

@ApplicationPath("")
public class JaxrsActivator extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        return new HashSet<>(Arrays.asList(BookResource.class));
    }

    @Override
    public Set<Object> getSingletons() {
        // Setup Zipkin / JAX-RS Server
        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

        Brave brave = new Brave.Builder("myJaxrsServerService").reporter(asyncReporter).build();
        BraveTracingFeature tracingFeature = BraveTracingFeature.create(brave);
        return new HashSet<>(Arrays.asList(tracingFeature));
    }
}

他でもこういうコードは登場するので、ここで1度説明しておきます。

Reporterを作成しますが、SenderとしてURLConnectionSenderを指定します。Zipkinの
URLは「http://localhost:9411/api/v1/spans」を指定します。

        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

作成したReporterをBrave.Builderに設定してBraveのインスタンスを作成し、BraveTracingFeatureを
作成、getSingletonsの結果に含めるようにします。

        Brave brave = new Brave.Builder("myJaxrsServerService").reporter(asyncReporter).build();
        BraveTracingFeature tracingFeature = BraveTracingFeature.create(brave);

なお、Brave.Builderの引数に与える名前がZipkin UI上で見る時の名前として重要に
なるので、意味のわかる名前を設定しましょう。

起動クラス

JAX-RS Server側の起動クラスは、以下のように用意。
src/main/java/javaeeadventcalendar/ServerBootstrap.java

package javaeeadventcalendar;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.mysql.MySQLStatementInterceptorManagementBean;
import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import zipkin.Span;
import zipkin.reporter.AsyncReporter;
import zipkin.reporter.urlconnection.URLConnectionSender;

public class ServerBootstrap {
    public static void main(String... arsg) throws Exception {
        // Setup Zipkin / MySql
        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

        Brave brave = new Brave.Builder("myJaxrsMySqlService").reporter(asyncReporter).build();
        new MySQLStatementInterceptorManagementBean(brave.clientTracer());


        // Setup JAX-RS / RESTEasy & Netty 4
        NettyJaxrsServer jaxrsServer = new NettyJaxrsServer();

        ResteasyDeployment deployment = jaxrsServer.getDeployment();
        deployment.setApplicationClass(JaxrsActivator.class.getName());

        jaxrsServer.setRootResourcePath("");
        jaxrsServer.setPort(8080);
        jaxrsServer.setDeployment(deployment);

        jaxrsServer.start();
    }
}

後半はRESTEasy+Netty 4を使ったJAX-RS Server起動のコードですが、前半はBrave MySQLのためのコードになっています。

        // Setup Zipkin / MySql
        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

        Brave brave = new Brave.Builder("myJaxrsMySqlService").reporter(asyncReporter).build();
        new MySQLStatementInterceptorManagementBean(brave.clientTracer());

内容自体は、JAX-RSのApplicationクラスを紹介した時と似た感じです。ただ、最後に次の1行が入っている
ところがポイントです。

        new MySQLStatementInterceptorManagementBean(brave.clientTracer());

これがないと、JDBC接続文字列にStatementInterceptorを設定しても、Zipkinにトレースデータが
送信されません。

JAX-RS Client

続いては、JAX-RS Client。こちらにも、Brave JAXRS2を加えます。JAX-RS Clientの
実装には、やっぱりRESTEasyを利用。

Maven依存関係

使用したMaven依存関係は、こちら。Server側を見たあとだと、JAX-RSがClient用になっていること以外は、
そう迷うものはないかなと思います。

        <!-- JAX-RS / RESTEasy -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>3.0.19.Final</version>
        </dependency>

        <!-- Brave Libraries -->
        <!-- Brave JAXRS2 -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-jaxrs2</artifactId>
            <version>3.16.0</version>
        </dependency>
        <!-- Reporter -->
        <dependency>
            <groupId>io.zipkin.reporter</groupId>
            <artifactId>zipkin-sender-urlconnection</artifactId>
            <version>0.6.9</version>
        </dependency>
Entity

Server側とは別コードで作成したので、JSONのやり取りに使用するEntityクラスを作成。
src/main/java/javaeeadventcalendar/Book.java

package javaeeadventcalendar;

public class Book {
    private String isbn;
    private String title;
    private int price;

    public static Book create(String isbn, String title, int price) {
        Book book = new Book();
        book.setIsbn(isbn);
        book.setTitle(title);
        book.setPrice(price);
        return book;
    }

    public Book() {
    }

    // getter/setterは省略
}
JAX-RS Clientと起動クラス

JAX-RS Clientの実装は、こんな感じで用意。
src/main/java/javaeeadventcalendar/ClientApp.java

package javaeeadventcalendar;

import java.net.URI;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.jaxrs2.BraveTracingFeature;
import zipkin.Span;
import zipkin.reporter.AsyncReporter;
import zipkin.reporter.urlconnection.URLConnectionSender;

public class ClientApp {
    public static void main(String... args) throws InterruptedException {
        // Setup Zipkin / JAX-RS Client
        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

        Brave brave = new Brave.Builder("myJaxrsClientService").reporter(asyncReporter).build();

        Client client =
                ClientBuilder
                        .newBuilder()
                        .register(BraveTracingFeature.create(brave))
                        .build();

        Response registerResponse =
                client
                        .target("http://localhost:8080/book")
                        .request()
                        .put(Entity.entity(Book.create("978-4774183169", "パーフェクト Java EE", 3456),
                                MediaType.APPLICATION_JSON_TYPE));

        URI location = registerResponse.getLocation();
        System.out.println("Location = " + location);

        registerResponse.close();

        System.out.println("=====");

        Response findResponse =
                client
                        .target(location)
                        .request()
                        .get();

        Book responseBook = findResponse.readEntity(Book.class);
        System.out.println("isbn = " + responseBook.getIsbn());
        System.out.println("title = " + responseBook.getTitle());
        System.out.println("price = " + responseBook.getPrice());

        findResponse.close();

        client.close();

        // wait Zipkin request...
        System.out.println("wait Zipkin request...");
        TimeUnit.SECONDS.sleep(2L);
    }
}

通常のJAX-RS Clientを使う時と違うところは、ClientBuilder#registerでBraveTracingFeatureを追加しているところくらいです。

        // Setup Zipkin / JAX-RS Client
        AsyncReporter<Span> asyncReporter =
                AsyncReporter
                        .builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans")).build();

        Brave brave = new Brave.Builder("myJaxrsClientService").reporter(asyncReporter).build();

        Client client =
                ClientBuilder
                        .newBuilder()
                        .register(BraveTracingFeature.create(brave))
                        .build();

BraveおよびReporterを作成しているところは、今までのパターンとそう変わりません。

あと、Zipkinに非同期にデータを送信している都合上、プログラムを即終了してしまうと
Zipkinにデータが渡りきらないため、ちょっとsleepを入れています。

        // wait Zipkin request...
        System.out.println("wait Zipkin request...");
        TimeUnit.SECONDS.sleep(2L);

最初、これでClient側のトレースデータが入らずに「なんでだろう?」とか思っていました…。

ここまでで、サンプルコードはできあがりです。

動作確認

それでは、作成したコードをパッケージングします。

今回作成したコードは、Mavenのマルチプロジェクトになっていますので、トップレベルのディレクトリでパッケージングします。

$ find brave-jaxrs-* pom.xml -type f
brave-jaxrs-client/pom.xml
brave-jaxrs-client/src/main/java/javaeeadventcalendar/ClientApp.java
brave-jaxrs-client/src/main/java/javaeeadventcalendar/Book.java
brave-jaxrs-server/pom.xml
brave-jaxrs-server/src/main/java/javaeeadventcalendar/EntityManagerProvider.java
brave-jaxrs-server/src/main/java/javaeeadventcalendar/Book.java
brave-jaxrs-server/src/main/java/javaeeadventcalendar/BookResource.java
brave-jaxrs-server/src/main/java/javaeeadventcalendar/JaxrsActivator.java
brave-jaxrs-server/src/main/java/javaeeadventcalendar/ServerBootstrap.java
brave-jaxrs-server/src/main/resources/META-INF/persistence.xml
pom.xml

パッケージング。

$ mvn package

さらに言うと、両方ともSpring BootのMaven Pluginを使っているので、Uber JARになります。

            <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
              <version>1.4.2.RELEASE</version>
              <executions>
                <execution>
                  <goals>
                    <goal>repackage</goal>
                  </goals>
                </execution>
              </executions>
            </plugin>

では、実行。まずはServer側を起動します。めちゃくちゃ高速に起動します(笑)。

$ java -jar brave-jaxrs-server/target/brave-jaxrs-server-0.0.1-SNAPSHOT.jar

そして、Client側を実行。

$ java -jar brave-jaxrs-client/target/brave-jaxrs-client-0.0.1-SNAPSHOT.jar 
Location = http://localhost:8080/book/978-4774183169
isbn = 978-4774183169
title = パーフェクト Java EE
price = 3456
wait Zipkin request...

プログラムの実行が終わったら、Zipkin UIをブラウザで参照します。すると、左のプルダウンで
Zipkinに送信したサービスが選べるようになっています。

今回は「myjaxrsclientservice」を選び、「Start time」に適当な値を設定して「Find Traces」を実行。
トレースの結果が2つ確認できます。

示されたトレース情報は、選択できるのでクリックします。すると、さらに内訳を見ることができます。

展開された後に表示される、棒グラフの部分をクリックすると、もっと細かい情報が。

なんとなく、可視化された感じになりますね?(笑)

まとめ

Brave JAXRS2とBrave MySQLを使って、Distributed Tracing(分散してないけど)を試してみました。

組み込みには慣れが要りそうな感じ?ではありますが、まず使ってみる分にはそう困らないのではないでしょうか?
少しでも興味を持っていただけると、幸いです。

今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/javaee-advent-calendar/tree/master/2016

明日は、@khasunumaさんのご担当です。よろしくお願いします!