我试用了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的兼容了。