我试用了Kamon v1.0系列

这篇文章是 Scala Advent Calendar 第21天的文章。

Kamon 是什么?

Kamon是一个用于运行在JVM上的应用程序的监控工具包。它为您提供了指标、跟踪和上下文传递的API,而不会将您限制在任何特定的供应商上。所有的Kamon API都完全解耦了可以接收数据的服务,无论是StatsD、Prometheus、Kamino、Datadog、Zipkin、Jaeger还是任何其他支持的报告器,使用Kamon,您可以仪表化您的应用程序一次,并在任何您想要的地方报告。

Kamon是一款用于监控在JVM上运行的应用程序的工具包,可以与各种服务进行协作。
它具有类似SystemMetrics的功能,还有用于获取Akka邮箱堆积状况、JDBC请求延迟等指标的插件,输出可以选择Datadog或Prometheus等多种选项。

目前的最新版本是0.6系列,但我們正在另一個分支上開發1.0系列,並且已經進展到了RC7版本,所以我們打算趁這個機會稍微試試看。

源代码在这里。
https://github.com/uryyyyyyy/kamonSample/tree/advent21

这里的环境

    • Kamon: 1.0.0-RC7

 

    Scala: 2.12

要做的事情

为了了解Kamon和插件的工作原理,我们分别创建数据收集器(Collector)和数据报告器(Reporter)进行数据收集和报告。
虽然有各种各样的插件存在,但是我认为如果能理解基本的流程,代码也会更易读。

尝试收集数据

我們試著創建一個定期獲取固定值的Collector。
首先,我們來創建數據。每當調用update方法時,我們會將指標(metric)發送到Kamon。

package com.github.uryyyyyyy.kamon.simple.collector

import kamon.Kamon
import org.slf4j.LoggerFactory

class MyMetrics {

  private val logger = LoggerFactory.getLogger(classOf[MyMetrics])

  // メトリクス名・メトリクスのタイプを決める。
  val hist1 = Kamon.histogram("my-reporter.my-metrics.hist1")
  val counter1 = Kamon.counter("my-reporter.my-metrics.counter1")
  val sampler1 = Kamon.rangeSampler("my-reporter.my-metrics.sampler1")

  def update() = {
    logger.info("MyMetrics update")

    // メトリクスのタイプに応じたAPIでデータが計測される。
    hist1.record(10)
    counter1.increment(1)
    sampler1.increment(2)
  }
}

接下来,我们将创建一个定期执行此测量的调度程序。

package com.github.uryyyyyyy.kamon.simple.collector

import java.time.Duration
import java.util.concurrent.{ScheduledFuture, TimeUnit}

import kamon.Kamon
import org.slf4j.LoggerFactory

object MyCollector {

  private val logger = LoggerFactory.getLogger("com.github.uryyyyyyy.kamon.simple.collector.MyCollector")

  private var scheduledCollection: ScheduledFuture[_] = null

  // 計測開始
  def startCollecting() = {
    val myMetrics = new MyMetrics()
    val updaterSchedule = new Runnable {
      override def run(): Unit = myMetrics.update()
    }
    // 1s毎に、メトリクスを収集するためにmyMetrics.update()を呼ぶ
    scheduledCollection = Kamon.scheduler().scheduleAtFixedRate(
      updaterSchedule,
      Duration.ofSeconds(1).toMillis,
      Duration.ofSeconds(1).toMillis,
      TimeUnit.MILLISECONDS
    )
    logger.info("startCollecting done")
  }

  // Schedulerを止める
  def stopCollecting():Boolean = {
    val b = scheduledCollection.cancel(false)
    scheduledCollection = null
    b
  }
}

通过这个方法,Kamon能够每秒收集到数据。

尝试向所收集的数据报告

本来情况下,我们会将指标转发到外部服务,例如Datadog或Prometheus,但在这里,我们可以通过简单地记录日志来进行确认。

package com.github.uryyyyyyy.kamon.simple.reporter

import com.typesafe.config.Config
import kamon.MetricReporter
import kamon.metric.PeriodSnapshot
import org.slf4j.LoggerFactory

// kamon.MetricReporterを継承
class MyReporter extends MetricReporter {

  private val logger = LoggerFactory.getLogger(classOf[MyReporter])

  // メトリクスが収集されるとこのメソッドが呼ばれる
  override def reportPeriodSnapshot(snapshot: PeriodSnapshot): Unit = {
    logger.info("reportTickSnapshot")
    snapshot.metrics.counters.foreach(metric => {
      logger.info(s"name: ${metric.name}, tags: ${metric.tags}, unit: ${metric.unit}, value: ${metric.value}")
    })
    snapshot.metrics.histograms.foreach(metric => {
      logger.info(s"name: ${metric.name}, tags: ${metric.tags}, unit: ${metric.unit}, value-sum: ${metric.distribution.sum}")
    })
    snapshot.metrics.rangeSamplers.foreach(metric => {
      logger.info(s"name: ${metric.name}, tags: ${metric.tags}, unit: ${metric.unit}, value-max: ${metric.distribution.max}")
    })
  }

  // 前処理
  override def start(): Unit = {
    logger.info("MyReporter start")
  }

  // 後処理
  override def stop(): Unit = {
    logger.info("MyReporter stop")
  }

  // 再設定
  override def reconfigure(config: Config): Unit = {
    logger.info("MyReporter reconfigure")
  }
}

基本上是最小配置的形式,在获取指标时会调用reportPeriodSnapshot函数,因此根据指标类型实现相应的操作。在这里,我们编写了将日志属性和获取的数据输出到日志中的处理方法,但是当编写发送到外部服务的插件时,可能需要在start()等函数中进行初始化设置。

试一试

如果要调用上述的Collector和Reporter,可以这样做。

package com.github.uryyyyyyy.kamon.simple

import com.github.uryyyyyyy.kamon.simple.collector.MyCollector
import kamon.Kamon

object Main {

  def main(args: Array[String]): Unit = {
    // reporterの作成
    Kamon.loadReportersFromConfig()
    // collectorの実行
    MyCollector.startCollecting()
    Thread.sleep(10000)
    // collectorの終了
    MyCollector.stopCollecting()
    // reporterの終了
    Kamon.stopAllReporters()
    Kamon.scheduler().shutdown()
    System.exit(0) // 何かkamonでスレッドを使っていて止まらないので終了させる
  }

}

日志大致如下

$ ./bin/simple 
2017-12-21 23:13:46,667 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - MyReporter start
2017-12-21 23:13:46,668 [INFO] from kamon.ReporterRegistry in main - Loaded metric reporter [com.github.uryyyyyyy.kamon.simple.reporter.MyReporter]
2017-12-21 23:13:46,672 [INFO] from com.github.uryyyyyyy.kamon.simple.collector.MyCollector in main - startCollecting done
2017-12-21 23:13:47,009 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - reportTickSnapshot
2017-12-21 23:13:47,673 [INFO] from com.github.uryyyyyyy.kamon.simple.collector.MyMetrics in kamon-scheduler-1 - MyMetrics update
2017-12-21 23:13:48,013 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - reportTickSnapshot
2017-12-21 23:13:48,038 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.counter1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value: 1
2017-12-21 23:13:48,044 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.hist1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value-sum: 10
2017-12-21 23:13:48,045 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.sampler1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value-max: 2
2017-12-21 23:13:48,672 [INFO] from com.github.uryyyyyyy.kamon.simple.collector.MyMetrics in kamon-scheduler-2 - MyMetrics update
2017-12-21 23:13:49,002 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - reportTickSnapshot
2017-12-21 23:13:49,002 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.counter1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value: 1
2017-12-21 23:13:49,002 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.hist1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value-sum: 10
2017-12-21 23:13:49,003 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - name: my-reporter.my-metrics.sampler1, tags: Map(), unit: MeasurementUnit(Dimension(none),Magnitude(none,1.0)), value-max: 4
2017-12-21 23:13:49,672 [INFO] from com.github.uryyyyyyy.kamon.simple.collector.MyMetrics in kamon-scheduler-1 - MyMetrics update
2017-12-21 23:13:50,004 [INFO] from com.github.uryyyyyyy.kamon.simple.reporter.MyReporter in loaded-from-config: com.github.uryyyyyyy.kamon.simple.reporter.MyReporter - reportTickSnapshot
...

总结

暫時能夠運作了。
但是,我們可以看出當指示器類型為Counter時,數字每次都會被重置。這在0.6版本中沒有出現過,所以有些莫名其妙。
此外,從RC版本開始,API經常變動,如果查看提交記錄會不安地擔心是否經過了審查,真的是RC嗎?但我們將繼續觀察情況。

顺便一提,由于找不到适用于Stackdriver Monitoring的插件,笔者便自己制作了一个。现在也差不多要开始为其进行v1.0的兼容了。

广告
将在 10 秒后关闭
bannerAds