Stackdriver Traceを使ってGAE/FEで動くSpringbootアプリをトレースする

## はじめに

[Stackdriver Trace](https://cloud.google.com/trace/) を使ってGAE/FEで動くSpringbootのトレースを試してみたのでまとめます。

## ところでStackdriver Traceってなんだっけ？

Stackdriver Trace は、主に Google Cloud Platform 向けの [分散トレース システム](https://qiita.com/miya10kei/items/2532b80fcd8d19eb2e75) です。
アプリからレイテンシデータを収集し、[図表にしてくれます](https://cloud.google.com/trace/docs/trace-overview)。

図表の一例をあげると次のような感じ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/467ea04b-3249-c4c6-5c40-b6befa37ce17.png)

これを使うことでパフォーマンスのボトルネックになっている箇所の確認や、
アプリ更新前後のパフォーマンス変化の確認などが容易になります。

導入する意義や用語についてもう少し詳しくは [Stackdriver Traceの公式](https://cloud.google.com/trace/docs/overview) に書いてあります。

## Stackdriver Traceを使う

Stackdriver Traceを使うためには、計測対象がレイテンシを送信する必要があります。
逆に言うと取り合えずこれだけやってあげればOKです。

タイトルに記載の構成の場合、[Spring Cloud GCP](https://cloud.spring.io/spring-cloud-gcp/) の [Spring Cloud Sleuth](https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-docs/src/main/asciidoc/trace.adoc) ほかをpomに追記すれば
自動でStackdriver Trace宛にレイテンシを送信してくれます。 (これになかなか気付かなかった・・)

この具体的なセットアップ手順について参考になるのは [こちらのチュートリアル](https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-trace/index.html
) です。

[Spring Cloud Sleuth](https://cloud.spring.io/spring-cloud-sleuth/single/spring-cloud-sleuth.html) のリファレンスはこちら。

## Spring Cloud Sleuthのオプション

[オプションはこちら](https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-docs/src/main/asciidoc/trace.adoc#spring-boot-starter-for-stackdriver-trace) に記載されています。

主なところを抜粋すると次の通り。

```
spring.cloud.gcp.trace.enabled=true                     # トレースするか否か
spring.sleuth.sampler.probability=1                     # リクエストの何割をトレースするか (0.0～1.0)
spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*)  # トレースから除外するURL
```

## 試してみる

そんなわけでチュートリアルをお手本に試してみました。
前提は次のような感じ。

#### 試してみる前提

- レイテンシ収集対象はGAE/FE上のSpringbootのWebアプリ。
- レイテンシ収集は本番稼働している時のみ行います。
- 収集するか否かはactive profileで切り替えます。 本番profileを **prod** にしました。


### 事前準備

まず下記が無ければ用意します。

- JDK
- Maven
- 適当なGCPプロジェクト
- [Cloud SDK](https://cloud.google.com/sdk/docs/overview?hl=ja#installing_cloud_sdk)
- [適当なSpringbootサンプルソース](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver)

Cloud SDKは [使用するアカウントとプロジェクトを指定してログイン](https://cloud.google.com/sdk/docs/initializing?hl=ja) しておきます。
また適当なSpringbootサンプルソースは上記リンク先にあげました。

### Stackdriver Trace APIを有効にする

ウェブUIの **APIとサービス** でStackdriver Trace APIを探し、無効であれば有効にします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/c36ec72e-a69b-f9b0-20de-331d049c5b8e.png)

### 適当なSpringbootサンプルソースを用意する

適当なサンプルを [GitHubにあげました](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver)。
以下サンプルの補足です。

#### サンプル画面

ソースは [こちらです](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/java/com/example/demo/HelloSpringbootController.java) 。
ダミーの待ち時間を入れている他は特に何もしていません。

#### pom.xml

ソースは [こちらです](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/pom.xml) 。
下記コマンドで雛形を作り、`spring-cloud-gcp-starter-trace` を追加しています。

```bash
> curl https://start.spring.io/starter.tgz -d packaging=jar -d dependencies=web,cloud-gcp --output springboot-trace.tgz
```
またGAEにデプロイするため [`appengine-maven-plugin`](https://cloud.google.com/appengine/docs/flexible/java/using-maven) も追加しています。

#### application.yml
ローカルはトレースせず、本番はすべてトレースするよう指定しました。

ソースは次の通り。
[application.yml](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/resources/application.yml)
[application-prod.yml](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/resources/application-prod.yml)

#### app.yaml
[app.yaml](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/appengine/app.yaml) では特別な対応はしていません。

#### 素のアプリと比較して修正した箇所まとめ
トレース対応で手を入れた箇所は次の二箇所です。

| ファイル | 補足 |
|:-----------------|:------------------|
| pom.xml | 依存関係追記 |
| application(-prod).yml | トレース向けオプションを指定|


### デプロイ
appengine-maven-plugin を入れたので次のコマンドでデプロイします。

```bash
> mvn appengine:deploy
```

### 動作テスト

デプロイが終わったら該当のURLを適当に叩き、少し待ってから 
Stackdriverトレースのトレースタブを見てみると次のような感じに。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/ce322f5b-717d-653b-ca54-fd2cf9d77a73.png)

またレイテンシのサマリーも出てきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/341811b3-92f2-a16f-2b2f-f9e4b4bfba0f.png)

### APIのレイテンシはわかった。 内訳をもっと細かく見たい。

最初のテストではAPI全体のレイテンシが見れました。
今度はAPI内の任意の範囲のレイテンシを確認してみます。

これは [brave.Span](https://static.javadoc.io/io.zipkin.brave/brave/5.3.3/brave/Span.html) で任意の範囲を指定することで出来ます。
サンプルソースは [こちら](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/java/com/example/demo/HelloSpringbootController2.java) 通り。

トレース結果は次の通り。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/7c710f17-313c-a9b7-d80e-330184f6fef4.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/d8a27f73-e9f8-2d5f-c752-17e4d90d31af.png)

上記のほか [アノテーションでも指定](https://cloud.spring.io/spring-cloud-sleuth/single/spring-cloud-sleuth.html#_managing_spans_with_annotations) 出来るっぽいけど、まだいまいちわかってない・・

### 複数サービスを串刺しでトレースしたい

分散トレーシングが本来の力を発揮するのはこの用途です。
[リファレンスのここ](https://cloud.spring.io/spring-cloud-sleuth/single/spring-cloud-sleuth.html#_synchronous_rest_template) に従い `RestTemplate` のbeanを作成して他サービスの呼出に使用します。
サンプルソースは [こちら](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/java/com/example/demo/HelloSpringbootApplication.java#L16) と [こちら](https://github.com/tnagao3000/hello-springboot-on-gae-traced-by-stackdriver/blob/master/src/main/java/com/example/demo/HelloSpringbootController3.java) 。

本サンプルでは **/c** から **/c2** を呼び出しています。
トレース結果は次の通り。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/26808/8c03cb33-0330-5518-33df-1509ef732e41.png)

以上です。