kamon-mackerelでJVMで動くアプリケーションを監視しよう

[Mackerel Advent Calendar 2018](https://qiita.com/advent-calendar/2018/mackerel) の2日目の記事っす。よろしくお願いいたします。

[Kamon](https://kamon.io/documentation/get-started/) は、JVM上で動作するアプリケーションの監視ツールキットです。

特定のベンダーに決められることなく、Metricsや、Tracing、Context Propagation APIs を提供してくれるツールキットです。

公式では、 StatsD, Prometheus, Kamino, Datadog, Zipkin, Jaeger などをサポートしておりますが、Mackerelがそこにありませんでしたので、ちょっと作ってみるかってことで、作りましたので、ご紹介させていただきます。

[yoshiyoshifujii/kamon-mackerel](https://github.com/yoshiyoshifujii/kamon-mackerel)

# How to use

JVMで動くアプリケーションといっておりますが、基本、Scalaで書いたコードをJVMで実行するのを想定してlibraryを作っておりますので、ここでは、Scalaでどうやって組んでいけばメトリクスをMackerelで確認できるのか、手順を記載していきます。

## sbt new

まずは、サンプルとして、プロジェクトを作成します。

今回は、簡単なWeb APIの雛形として、 [akka/akka-http-quickstart-scala.g8](https://github.com/akka/akka-http-quickstart-scala.g8) を作って、組んでみたいと思います。

なお、事前に、sbt、JVMがインストールされている前提となります。

私の環境では、

- AdoptOpenJDK v1.8.0_192
- sbt 1.2.6

がインストールされています。

それでは、以下のコマンドを実行します。

```sh
$ sbt new akka/akka-http-quickstart-scala.g8
```

すると、以下のようにだーっと出力されて、

```sh
Getting org.scala-sbt sbt 1.2.6  (this may take some time)...
downloading file:////Users/yoshiyoshifujii/.sbt/preloaded/org.scala-sbt/sbt/1.2.6/jars/sbt.jar ...
        [SUCCESSFUL ] org.scala-sbt#sbt;1.2.6!sbt.jar (11ms)
...
```

で、以下の入力待ち状態になりますので、それぞれ、適切に入力したりEnterします。

```sh
This is a seed project which creates a basic build for an Akka HTTP
application using Scala.

name [My Akka HTTP Project]: example-akka-http
scala_version [2.12.7]:
akka_http_version [10.1.5]:
akka_version [2.5.18]:
organization [com.example]:
package [com.example]:
```

すると、直下に、 `example-akka-http` というディレクトリが出来ていますので、これでベースとなるプロジェクトができます。

## IntelliJ IDEAで開く

作ったプロジェクトをIDEAで開きます。

<img width="666" alt="Welcome_to_IntelliJ_IDEA.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/7823aa9d-078b-e602-f877-3f4842e1e90d.png">

`Import Project` をクリックして、

<img width="741" alt="Import_Project.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/6cdae487-8c89-b1cf-77c9-aeb0e873ae76.png">

sbtを選んで、Nextを押します。

<img width="741" alt="Import_Project2.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/4af5216f-8251-9c00-f89e-eaa1563f4203.png">

JDKは、1.8とか適切なのを選んで、Finishしますと、プロジェクトが開きます。

最初は、IntelliJ IDEAが色々と依存関係を成立させるためにダウンロードだなんだと時間がかかりますが、それを待ちます。

## kamon-mackerelを依存関係に追加する

IntelliJ IDEAの初回ロードが終わりましたら、次に、build.sbtを開いて、kamon-mackerelを依存関係に追加します。

IDEAで、build.sbtを開きます。

```scala:build.sbt
lazy val akkaHttpVersion = "10.1.5"
lazy val akkaVersion    = "2.5.18"

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization    := "com.example",
      scalaVersion    := "2.12.7"
    )),
    name := "example-akka-http",
    libraryDependencies ++= Seq(
      "com.typesafe.akka" %% "akka-http"            % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-http-xml"        % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-stream"          % akkaVersion,

      "com.typesafe.akka" %% "akka-http-testkit"    % akkaHttpVersion % Test,
      "com.typesafe.akka" %% "akka-testkit"         % akkaVersion     % Test,
      "com.typesafe.akka" %% "akka-stream-testkit"  % akkaVersion     % Test,
      "org.scalatest"     %% "scalatest"            % "3.0.5"         % Test
    )
  )
```

このあたりに加えましょう。

```scala
    libraryDependencies ++= Seq(
      "com.typesafe.akka" %% "akka-http"            % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-http-xml"        % akkaHttpVersion,
      "com.typesafe.akka" %% "akka-stream"          % akkaVersion,
      "io.kamon" %% "kamon-core" % "1.1.0", // <= ここ!!
      "io.kamon" %% "kamon-akka-http-2.5" % "1.0.1", // <= ここ!!
      "com.github.yoshiyoshifujii" %% "kamon-mackerel" % "0.1.0", // <= ここ!!

      "com.typesafe.akka" %% "akka-http-testkit"    % akkaHttpVersion % Test,
      "com.typesafe.akka" %% "akka-testkit"         % akkaVersion     % Test,
      "com.typesafe.akka" %% "akka-stream-testkit"  % akkaVersion     % Test,
      "org.scalatest"     %% "scalatest"            % "3.0.5"         % Test
    )
```

すると、 `Import Changes` って出てきますので、それを押します。

<img width="1680" alt="example-akka-http____workspace_git_yoshiyoshifujii_example-akka-http__-_____build_sbt__example-akka-http_.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/c0eef69d-cfd7-d06b-5a27-d584b2828d52.png">

## sbt-aspectj-runnerをpluginに追加

https://github.com/kamon-io/sbt-aspectj-runner のREADMEを参照し、 `project/plugins.sbt` に以下を追記する。

```scala:project/plugins.sbt
resolvers += Resolver.bintrayRepo("kamon-io", "sbt-plugins")
addSbtPlugin("io.kamon" % "sbt-aspectj-runner" % "1.1.0")
```

## KamonにReporterを追加

Mainとなる `com.example.QuickstartServer` を開きます。

以下のようにimport文を追記して、Reporterを追加します。

```scala
...
import kamon.Kamon
import kamon.mackerel.MackerelAPIReporter

//#main-class
object QuickstartServer extends App with UserRoutes {
  Kamon.addReporter(new MackerelAPIReporter())
...
```

さらに、 `application.conf` を `src/main/resources` 直下に作成します。

```conf:application.conf
kamon {
  mackerel {

    #
    # Settings relevant to the MackerelAPIReporter
    #
    http {

      # Mackerel API key to use to send metrics to mackerel directly over HTTPS.
      api-key = ""
    }

    host {
      id = ""
    }

  }
}
```

上記のように、api-keyとhostのidをconfigに設定しておく必要がありますので、Mackerelを開いて、それぞれ取得してきます。

## Mackerelから設定値を取得

Mackerelを開いて、api-keyを取得します。

<img width="1680" alt="Kamon_·_Kamon.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/e5868528-926a-0345-7ab5-74732cf5d105.png">

## Hostを登録

Mackerelでは、エージェントをインストールすることでHostを登録するかと思いますが、今回は、APIを使ってHostを登録したいと思います。

ドキュメントは、こちら。 https://mackerel.io/ja/api-docs/entry/hosts#create

以下のcurlコマンドを実行します。

```sh
$ curl -i -X POST https://api.mackerelio.com/api/v0/hosts \
  -H X-Api-Key:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
  -H Content-Type:application/json \
  -d '{"name":"host_name_1","meta":{}}'
```

すると、 `200 OK` で返ってきたら登録OKです。

```sh
HTTP/1.1 200 OK
...
{"id":"YYYYYYYYYYY"}% 
```

この表示されているIDが、HostIDですので、こちらも使います。

これで、 `api-key` と、 `host-id` を取得できました。

## Akka HTTPを起動

sbtを起動して、 `run` を実行します。

```sh
$ cd example-akka-http/
$ sbt run
...
[info] Running com.example.QuickstartServer
23:37:29.340 [kamon.mackerel.MackerelAPIReporter] INFO kamon.mackerel.MackerelAPIReporter - Started the Mackerel API reporter.
Server online at http://127.0.0.1:8080/
```

以下のcurlを実行すると、いろいろとアクセスできます。

```sh
$ curl -i -X GET http://localhost:8080/users                                                                                                                                                      [~]
HTTP/1.1 200 OK
X-B3-TraceId: db805a66b23110ec
X-B3-Sampled: 0
X-B3-SpanId: 5388c20a0e8292b3
Server: akka-http/10.1.5
Date: Thu, 22 Nov 2018 14:42:14 GMT
Content-Type: application/json
Content-Length: 12

{"users":[]}% 

$ curl -i -X POST http://localhost:8080/users -H Content-Type:application/json -d '{"name":"hoge","age":10,"countryOfResidence":"ooo"}'                                                           [~]
HTTP/1.1 201 Created
X-B3-TraceId: 4eb323d6196145e8
X-B3-Sampled: 0
X-B3-SpanId: dbfc829b7f2499b0
Server: akka-http/10.1.5
Date: Thu, 22 Nov 2018 14:45:45 GMT
Content-Type: application/json
Content-Length: 36

{"description":"User hoge created."}% 
```

## MackerelでMetricsを確認

Mackerelを開いて、HostのMetricsを確認してみます。

<img width="1680" alt="スクリーンショット_2018-11-22_23_47_44.png" src="https://qiita-image-store.s3.amazonaws.com/0/24050/13f9ca73-7992-cc37-d085-a0c1bd1fa0d1.png">

うん。なんか出てます。

# まとめ

各メトリクスの内容については、Actorの数とかが可視化されたりしてます。

他にも、JVMを可視化する [kamon-jmx](https://github.com/kamon-io/kamon-jmx) を入れたりすることも可能ですので、色々とKamonのMetrics収集を組み込んだうえで、Mackerelで可視化するライフが送れると思います。

当記事を作成するにあたり作ったサンプルのコードは、 https://github.com/yoshiyoshifujii/example-akka-http です。

以上です。