ぶらっ記ぃ

日本語の練習をしています

scaladogでScalaからDatadogにメトリクス・イベントを送信する

この記事は Scala Advent Calendar 2019 および Datadog Advent Calendar 2019 の21日目(遅刻)です。

scaladogとは?

github.com

scaladog は拙作のScalaDatadog API のクライアントライブラリです。
HTTPでAPIを直接叩いているため、実行環境にDatadog Agentをインストールする必要はありません。

仕事でもプライベートでもScalaとDatadogにお世話になっているので、いっそのことクライアントライブラリを作ってみようと思ったのが開発を始めたきっかけです。 1

今回はDatadogの主要なユースケースであるメトリクスとイベントの送信を、scaladogで行う方法をお伝えしていきます。

準備

Datadog API を叩くには API Key および、 Application Key が必要になります。
これらのキーを取得する方法はこちらを参照してください。

キーを取得したら、これらを環境変数に設定します。
scaladogは DATADOG_API_KEY , DATADOG_APP_KEY 環境変数に設定されたキーを読み込みます。

export DATADOG_API_KEY=<your-API-key>
export DATADOG_APP_KEY=<your-Application-key>

また、今回のお見せするScalaコードの実行にはAmmoniteを使います。
以下コマンドで v1.8.22 をインストールします。

sudo sh -c '(echo "#!/usr/bin/env sh" && curl -L https://github.com/lihaoyi/Ammonite/releases/download/1.8.2/2.13-1.8.2) > /usr/local/bin/amm && chmod +x /usr/local/bin/amm' && amm

実行すると、Ammoniteの実行バイナリがダウンロードされたのち、REPLが起動します。
import $ivy を使ってscaladogのライブラリをクラスパスに追加します。

import $ivy.`dev.nomadblacky::scaladog:0.4.2`

以下のようにライブラリ依存が追加されればOKです。

Loading...
Welcome to the Ammonite Repl 1.8.2
(Scala 2.13.1 Java 1.8.0_231)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
@ import $ivy.`dev.nomadblacky::scaladog:0.4.2`
https://repo1.maven.org/maven2/dev/nomadblacky/scaladog_2.13/0.4.2/scaladog_2.13-0.4.2.pom
  100.0% [##########] 2.2 KiB (1.3 KiB / s)
https://repo1.maven.org/maven2/dev/nomadblacky/scaladog_2.13/0.4.2/scaladog_2.13-0.4.2-sources.jar
  100.0% [##########] 13.0 KiB (16.9 KiB / s)
https://repo1.maven.org/maven2/dev/nomadblacky/scaladog_2.13/0.4.2/scaladog_2.13-0.4.2.jar
  100.0% [##########] 264.9 KiB (273.4 KiB / s)
import $ivy.$

続けてAPIクライアントとなるクラスのインスタンスを作成します。

@ val datadog = scaladog.Client()
datadog: scaladog.Client = scaladog.ClientImpl@5853495b

これで準備完了です!

カスタムメトリクスを送信する

まずはカスタムメトリクスを送信してみましょう。
以下のコードをREPLに入力します。

import scaladog.api.metrics._
import java.time.Instant
import scala.util.Random

while(true) {
  val series = Series(
    metric = "scaladog.example",
    points = Seq(Point(Instant.now(), Random.nextInt(1000))),
    tags = Seq("project:scaladog")
  )
  datadog.metrics.postMetrics(Seq(series))
  println(series)
  Thread.sleep(5000)
}

5秒感覚でてきとうなメトリクスを送信するコードになっています。3

@ { // ←ブロックで複数行のコードを入力できます
  import scaladog.api.metrics._
  import java.time.Instant
  import scala.util.Random

  while(true) {
    val series = Series(
      metric = "scaladog.example",
      points = Seq(Point(Instant.now(), Random.nextInt(1000))),
      tags = Seq("project:scaladog")
    )
    datadog.metrics.postMetrics(Seq(series))
    println(series)
    Thread.sleep(5000)
  }
  }
Series(scaladog.example,List(Point(2019-12-21T14:59:56.537Z,689)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:02.277Z,484)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:08.033Z,550)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:13.749Z,470)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:19.425Z,682)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:25.129Z,219)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:30.858Z,440)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:36.576Z,269)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:42.258Z,213)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:47.951Z,180)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:53.677Z,615)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:00:59.412Z,377)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:05.119Z,303)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:11.103Z,40)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:16.821Z,147)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:22.506Z,899)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:28.289Z,347)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:33.990Z,241)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:39.657Z,716)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:45.680Z,706)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:51.393Z,191)),,List(project:scaladog),Gauge)
Series(scaladog.example,List(Point(2019-12-21T15:01:57.118Z,696)),,List(project:scaladog),Gauge)
...
// 飽きたら Ctrl+C で抜ける

Datadogの画面からメトリクスを送信できているか確認しましょう。

f:id:Nomad_Blacky:20191222004550p:plain

メトリクスを確認できました!

イベントを送信する

続けてイベントを送信してみましょう。
以下のコードをREPLに入力します。

scaladog.Client().events.postEvent(
  title = "TEST EVENT",
  text = "Hello, scaladog!"
)
@ scaladog.Client().events.postEvent(
    title = "TEST EVENT",
    text = "Hello, scaladog!"
  )
res7: scaladog.api.events.PostEventResponse = PostEventResponse(
  "ok",
  5245888207545475945L,
  "https://app.datadoghq.com/event/event?id=5245888207545475945"
)

イベントのIDとURLが評価結果として返ってきていることがわかります。
Datadogの画面からイベントを確認してみましょう。

f:id:Nomad_Blacky:20191222004703p:plain

イベントが無事送信できていますね!

アラートの種類やタグの付与にも対応していますので、必要があれば以下のように追加できます。

scaladog.Client().events.postEvent(
  title = "TEST EVENT",
  text = "This is a test event.",
  dateHappened = Instant.now(),
  priority = Priority.Low,
  tags = Seq("project:scaladog"),
  alertType = AlertType.Warning
)

活用例

これら以外のAPIも一部提供していますので、詳細はREADMEのコード例をご覧ください。

実際の利用例をご紹介しますと、社のブログ Scala Advent Calendar 6日目での、 esa.io の利用状況をDatadogに送信するScalaスクリプトにも scaladog が使われています。

また、お仕事でお世話になっているワークフローエンジンであるDigdagからDatadogを叩くための digdag-plugin-datadog を鋭意開発中です。

github.com

そのほか、sbtに組み込んでコンパイル時間やカバレッジを記録するのも面白いんじゃないかなぁと考えています。

あとがき

拙作のscaladogの紹介をしていきました。

まだまだサポートしているAPIは少ないのでこれから頑張って増やしていきたいです。
…とはいえ「このAPI使いたいな…じゃあ実装しよう!」というモチベーションから開発が始まるので、もし「このAPIをサポートしてほしい!」等あればIssueを投げてくれるときっと実装する気持ちになれると思います! 4

ぜひ、scaladogを使ってDatadogをカジュアルに使ってくれると嬉しいです!


  1. あと自分でOSSのメンテナンスを経験してみたかったのも理由のひとつです。

  2. 最新版のAmmonite v1.9.2 で動作しないバグをこの記事を書いているときに見つけました…。おそらくはAmmoniteとscaladog両方で依存しているupickleの変更によるものでしょうか。

  3. 今見返すと、メトリクスひとつ送るだけのコードが少し冗長に感じますね…単体のメトリクスを送るためのAPIがあっても良さそうな気がしました…

  4. プルリクもお待ちしてます…!