aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego <diegolparra@gmail.com>2015-08-20 00:03:16 -0300
committerDiego <diegolparra@gmail.com>2015-08-25 10:13:22 -0300
commit569f1997c70f54b1b9f08b2f255e6bb46212ae77 (patch)
tree4d723d2ba2bc0f62b21865a21341795295c181a1
parent1efb03c6c6c8afc528a103eb78c3cfc96e12bd8a (diff)
downloadKamon-569f1997c70f54b1b9f08b2f255e6bb46212ae77.tar.gz
Kamon-569f1997c70f54b1b9f08b2f255e6bb46212ae77.tar.bz2
Kamon-569f1997c70f54b1b9f08b2f255e6bb46212ae77.zip
! core: generalize ThreadPoolExecutors metrics
-rw-r--r--kamon-core/src/main/scala/kamon/util/executors/ExecutorMetricRecorder.scala94
-rw-r--r--kamon-core/src/main/scala/kamon/util/executors/ExecutorServiceMetrics.scala165
-rw-r--r--kamon-core/src/test/scala/kamon/util/executors/ExecutorServiceMetricsSpec.scala77
3 files changed, 278 insertions, 58 deletions
diff --git a/kamon-core/src/main/scala/kamon/util/executors/ExecutorMetricRecorder.scala b/kamon-core/src/main/scala/kamon/util/executors/ExecutorMetricRecorder.scala
new file mode 100644
index 00000000..465205b2
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/executors/ExecutorMetricRecorder.scala
@@ -0,0 +1,94 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.util.executors
+
+import kamon.metric.{ EntityRecorderFactory, GenericEntityRecorder }
+import kamon.metric.instrument.{ Gauge, MinMaxCounter, DifferentialValueCollector, InstrumentFactory }
+import java.util.concurrent.{ ForkJoinPool ⇒ JavaForkJoinPool, ThreadPoolExecutor }
+import kamon.util.executors.ForkJoinPools.ForkJoinMetrics
+
+import scala.concurrent.forkjoin.ForkJoinPool
+
+object ForkJoinPools {
+ trait ForkJoinMetrics[T] {
+ def getParallelism(fjp: T): Long
+ def getPoolSize(fjp: T): Long
+ def getActiveThreadCount(fjp: T): Long
+ def getRunningThreadCount(fjp: T): Long
+ def getQueuedTaskCount(fjp: T): Long
+ }
+
+ implicit object ScalaForkJoin extends ForkJoinMetrics[ForkJoinPool] {
+ def getParallelism(fjp: ForkJoinPool) = fjp.getParallelism
+ def getPoolSize(fjp: ForkJoinPool) = fjp.getPoolSize.toLong
+ def getRunningThreadCount(fjp: ForkJoinPool) = fjp.getActiveThreadCount.toLong
+ def getActiveThreadCount(fjp: ForkJoinPool) = fjp.getRunningThreadCount.toLong
+ def getQueuedTaskCount(fjp: ForkJoinPool) = fjp.getQueuedTaskCount
+
+ }
+
+ implicit object JavaForkJoin extends ForkJoinMetrics[JavaForkJoinPool] {
+ def getParallelism(fjp: JavaForkJoinPool) = fjp.getParallelism
+ def getPoolSize(fjp: JavaForkJoinPool) = fjp.getPoolSize.toLong
+ def getRunningThreadCount(fjp: JavaForkJoinPool) = fjp.getActiveThreadCount.toLong
+ def getActiveThreadCount(fjp: JavaForkJoinPool) = fjp.getRunningThreadCount.toLong
+ def getQueuedTaskCount(fjp: JavaForkJoinPool) = fjp.getQueuedTaskCount
+
+ }
+}
+
+abstract class ForkJoinPoolMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
+ def paralellism: MinMaxCounter
+ def poolSize: Gauge
+ def activeThreads: Gauge
+ def runningThreads: Gauge
+ def queuedTaskCount: Gauge
+}
+
+object ForkJoinPoolMetrics {
+ def factory[T: ForkJoinMetrics](fjp: T, categoryName: String) = new EntityRecorderFactory[ForkJoinPoolMetrics] {
+ val forkJoinMetrics = implicitly[ForkJoinMetrics[T]]
+
+ def category: String = categoryName
+ def createRecorder(instrumentFactory: InstrumentFactory) = new ForkJoinPoolMetrics(instrumentFactory) {
+ val paralellism = minMaxCounter("parallelism")
+ paralellism.increment(forkJoinMetrics.getParallelism(fjp)) // Steady value.
+
+ val poolSize = gauge("pool-size", forkJoinMetrics.getPoolSize(fjp))
+ val activeThreads = gauge("active-threads", forkJoinMetrics.getActiveThreadCount(fjp))
+ val runningThreads = gauge("running-threads", forkJoinMetrics.getRunningThreadCount(fjp))
+ val queuedTaskCount = gauge("queued-task-count", forkJoinMetrics.getQueuedTaskCount(fjp))
+ }
+ }
+}
+
+class ThreadPoolExecutorMetrics(tpe: ThreadPoolExecutor, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
+ val corePoolSize = gauge("core-pool-size", tpe.getCorePoolSize.toLong)
+ val maxPoolSize = gauge("max-pool-size", tpe.getMaximumPoolSize.toLong)
+ val poolSize = gauge("pool-size", tpe.getPoolSize.toLong)
+ val activeThreads = gauge("active-threads", tpe.getActiveCount.toLong)
+ val processedTasks = gauge("processed-tasks", DifferentialValueCollector(() ⇒ {
+ tpe.getTaskCount
+ }))
+}
+
+object ThreadPoolExecutorMetrics {
+ def factory(tpe: ThreadPoolExecutor, cat: String) = new EntityRecorderFactory[ThreadPoolExecutorMetrics] {
+ def category: String = cat
+ def createRecorder(instrumentFactory: InstrumentFactory) = new ThreadPoolExecutorMetrics(tpe, instrumentFactory)
+ }
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/util/executors/ExecutorServiceMetrics.scala b/kamon-core/src/main/scala/kamon/util/executors/ExecutorServiceMetrics.scala
index e1d84df3..7a87163f 100644
--- a/kamon-core/src/main/scala/kamon/util/executors/ExecutorServiceMetrics.scala
+++ b/kamon-core/src/main/scala/kamon/util/executors/ExecutorServiceMetrics.scala
@@ -17,75 +17,124 @@
package kamon.util.executors
import kamon.Kamon
-import kamon.metric.{ EntityRecorderFactory, Entity, GenericEntityRecorder }
-import kamon.metric.instrument.{ DifferentialValueCollector, InstrumentFactory }
-import java.util.concurrent.ThreadPoolExecutor
-import scala.concurrent.forkjoin.ForkJoinPool
+import kamon.metric.Entity
import java.util.concurrent.{ ForkJoinPool ⇒ JavaForkJoinPool }
+import java.util.concurrent.{ ExecutorService, ThreadPoolExecutor }
-class ForkJoinPoolMetrics(fjp: ForkJoinPool, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
- val paralellism = minMaxCounter("parallelism")
- paralellism.increment(fjp.getParallelism) // Steady value.
+import scala.concurrent.forkjoin.ForkJoinPool
+import scala.util.control.NoStackTrace
- val poolSize = gauge("pool-size", fjp.getPoolSize.toLong)
- val activeThreads = gauge("active-threads", fjp.getActiveThreadCount.toLong)
- val runningThreads = gauge("running-threads", fjp.getRunningThreadCount.toLong)
- val queuedTaskCount = gauge("queued-task-count", fjp.getQueuedTaskCount)
-}
+object ExecutorServiceMetrics {
+ val Category = "executor-service"
-object ForkJoinPoolMetrics {
- def factory(fjp: ForkJoinPool) = new EntityRecorderFactory[ForkJoinPoolMetrics] {
- def category: String = ExecutorServiceMetrics.Category
- def createRecorder(instrumentFactory: InstrumentFactory) = new ForkJoinPoolMetrics(fjp, instrumentFactory)
+ private val DelegatedExecutor = Class.forName("java.util.concurrent.Executors$DelegatedExecutorService")
+ private val FinalizableDelegated = Class.forName("java.util.concurrent.Executors$FinalizableDelegatedExecutorService")
+ private val DelegateScheduled = Class.forName("java.util.concurrent.Executors$DelegatedScheduledExecutorService")
+ private val JavaForkJoinPool = classOf[JavaForkJoinPool]
+ private val ScalaForkJoinPool = classOf[ForkJoinPool]
+
+ private val executorField = {
+ // executorService is private :(
+ val field = DelegatedExecutor.getDeclaredField("e")
+ field.setAccessible(true)
+ field
}
-}
-
-class JavaForkJoinPoolMetrics(fjp: JavaForkJoinPool, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
- val paralellism = minMaxCounter("parallelism")
- paralellism.increment(fjp.getParallelism) // Steady value.
-
- val poolSize = gauge("pool-size", fjp.getPoolSize.toLong)
- val activeThreads = gauge("active-threads", fjp.getActiveThreadCount.toLong)
- val runningThreads = gauge("running-threads", fjp.getRunningThreadCount.toLong)
- val queuedTaskCount = gauge("queued-task-count", fjp.getQueuedTaskCount)
-}
-
-object JavaForkJoinPoolMetrics {
- def factory(fjp: JavaForkJoinPool) = new EntityRecorderFactory[JavaForkJoinPoolMetrics] {
- def category: String = ExecutorServiceMetrics.Category
- def createRecorder(instrumentFactory: InstrumentFactory) = new JavaForkJoinPoolMetrics(fjp, instrumentFactory)
+
+ /**
+ *
+ * Register the [[http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html ThreadPoolExecutor]] to Monitor.
+ *
+ * @param name The name of the [[ThreadPoolExecutor]]
+ * @param threadPool The intance of the [[ThreadPoolExecutor]]
+ * @param tags The tags associated to the [[ThreadPoolExecutor]]
+ */
+ private def registerThreadPool(name: String, threadPool: ThreadPoolExecutor, tags: Map[String, String]): Unit = {
+ Kamon.metrics.entity(ThreadPoolExecutorMetrics.factory(threadPool, Category), Entity(name, Category, tags))
}
-}
-
-class ThreadPoolExecutorMetrics(tpe: ThreadPoolExecutor, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
- val corePoolSize = gauge("core-pool-size", tpe.getCorePoolSize.toLong)
- val maxPoolSize = gauge("max-pool-size", tpe.getMaximumPoolSize.toLong)
- val poolSize = gauge("pool-size", tpe.getPoolSize.toLong)
- val activeThreads = gauge("active-threads", tpe.getActiveCount.toLong)
- val processedTasks = gauge("processed-tasks", DifferentialValueCollector(() ⇒ {
- tpe.getTaskCount
- }))
-}
-
-object ThreadPoolExecutorMetrics {
- def factory(tpe: ThreadPoolExecutor) = new EntityRecorderFactory[ThreadPoolExecutorMetrics] {
- def category: String = ExecutorServiceMetrics.Category
- def createRecorder(instrumentFactory: InstrumentFactory) = new ThreadPoolExecutorMetrics(tpe, instrumentFactory)
+
+ /**
+ *
+ * Register the [[http://www.scala-lang.org/api/current/index.html#scala.collection.parallel.TaskSupport ForkJoinPool]] to Monitor.
+ *
+ * @param name The name of the [[ForkJoinPool]]
+ * @param forkJoinPool The instance of the [[ForkJoinPool]]
+ * @param tags The tags associated to the [[ForkJoinPool]]
+ */
+ private def registerScalaForkJoin(name: String, forkJoinPool: ForkJoinPool, tags: Map[String, String] = Map.empty): Unit = {
+ Kamon.metrics.entity(ForkJoinPoolMetrics.factory(forkJoinPool, Category), Entity(name, Category, tags))
}
-}
-object ExecutorServiceMetrics {
- val Category = "thread-pool-executors"
+ /**
+ *
+ * Register the [[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html JavaForkJoinPool]] to Monitor.
+ *
+ * @param name The name of the [[JavaForkJoinPool]]
+ * @param forkJoinPool The instance of the [[JavaForkJoinPool]]
+ * @param tags The tags associated to the [[JavaForkJoinPool]]
+ */
+ private def registerJavaForkJoin(name: String, forkJoinPool: JavaForkJoinPool, tags: Map[String, String] = Map.empty): Unit = {
+ Kamon.metrics.entity(ForkJoinPoolMetrics.factory(forkJoinPool, Category), Entity(name, Category, tags))
+ }
- def register(name: String, tpe: ThreadPoolExecutor): Unit = {
- Kamon.metrics.entity(ThreadPoolExecutorMetrics.factory(tpe), Entity(name, Category))
+ /**
+ *
+ * Register the [[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html ExecutorService]] to Monitor.
+ *
+ * @param name The name of the [[ExecutorService]]
+ * @param executorService The instance of the [[ExecutorService]]
+ * @param tags The tags associated to the [[ExecutorService]]
+ */
+ def register(name: String, executorService: ExecutorService, tags: Map[String, String]): Unit = executorService match {
+ case threadPoolExecutor: ThreadPoolExecutor ⇒ registerThreadPool(name, threadPoolExecutor, tags)
+ case scalaForkJoinPool: ForkJoinPool if scalaForkJoinPool.getClass.isAssignableFrom(ScalaForkJoinPool) ⇒ registerScalaForkJoin(name, scalaForkJoinPool, tags)
+ case javaForkJoinPool: JavaForkJoinPool if javaForkJoinPool.getClass.isAssignableFrom(JavaForkJoinPool) ⇒ registerJavaForkJoin(name, javaForkJoinPool, tags)
+ case delegatedExecutor: ExecutorService if delegatedExecutor.getClass.isAssignableFrom(DelegatedExecutor) ⇒ registerDelegatedExecutor(name, delegatedExecutor, tags)
+ case delegatedScheduledExecutor: ExecutorService if delegatedScheduledExecutor.getClass.isAssignableFrom(DelegateScheduled) ⇒ registerDelegatedExecutor(name, delegatedScheduledExecutor, tags)
+ case finalizableDelegatedExecutor: ExecutorService if finalizableDelegatedExecutor.getClass.isAssignableFrom(FinalizableDelegated) ⇒ registerDelegatedExecutor(name, finalizableDelegatedExecutor, tags)
+ case other ⇒ throw new NotSupportedException(s"The ExecutorService $name is not supported.")
}
- def register(name: String, fjp: ForkJoinPool): Unit = {
- Kamon.metrics.entity(ForkJoinPoolMetrics.factory(fjp), Entity(name, Category))
+ //Java variants
+ def register(name: String, executorService: ExecutorService): Unit = {
+ register(name, executorService, Map.empty[String, String])
}
- def register(name: String, jfjp: JavaForkJoinPool): Unit = {
- Kamon.metrics.entity(JavaForkJoinPoolMetrics.factory(jfjp), Entity(name, Category))
+ def register(name: String, executorService: ExecutorService, tags: java.util.Map[String, String]): Unit = {
+ import scala.collection.JavaConverters._
+ register(name, executorService, tags.asScala.toMap)
}
-}
+
+ /**
+ *
+ * Remove the [[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html ExecutorService]] to Monitor.
+ *
+ * @param name The name of the [[ExecutorService]]
+ * @param tags The tags associated to the [[ExecutorService]]
+ */
+ def remove(name: String, tags: Map[String, String]): Unit = {
+ Kamon.metrics.removeEntity(name, Category, tags)
+ }
+
+ //Java variants
+ def remove(name: String): Unit = {
+ remove(name, Map.empty[String, String])
+ }
+
+ def remove(name: String, tags: java.util.Map[String, String]): Unit = {
+ import scala.collection.JavaConverters._
+ remove(name, tags.asScala.toMap)
+ }
+
+ /**
+ * INTERNAL USAGE ONLY
+ */
+ private def registerDelegatedExecutor(name: String, executor: ExecutorService, tags: Map[String, String]) = {
+ val underlyingExecutor = executorField.get(executor) match {
+ case executorService: ExecutorService ⇒ executorService
+ case other ⇒ other
+ }
+ register(name, underlyingExecutor.asInstanceOf[ExecutorService], tags)
+ }
+
+ case class NotSupportedException(message: String) extends RuntimeException with NoStackTrace
+} \ No newline at end of file
diff --git a/kamon-core/src/test/scala/kamon/util/executors/ExecutorServiceMetricsSpec.scala b/kamon-core/src/test/scala/kamon/util/executors/ExecutorServiceMetricsSpec.scala
new file mode 100644
index 00000000..91f3cb71
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/util/executors/ExecutorServiceMetricsSpec.scala
@@ -0,0 +1,77 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.util.executors
+
+import java.util.concurrent.Executors
+
+import kamon.Kamon
+import kamon.metric.EntityRecorder
+import kamon.testkit.BaseKamonSpec
+
+class ExecutorServiceMetricsSpec extends BaseKamonSpec("executor-service-metrics-spec") {
+
+ "the ExecutorServiceMetrics" should {
+ "register a SingleThreadPool, collect their metrics and remove it" in {
+ val singleThreadPoolExecutor = Executors.newSingleThreadExecutor()
+ ExecutorServiceMetrics.register("single-thread-pool", singleThreadPoolExecutor)
+ findExecutorRecorder("single-thread-pool") should not be empty
+
+ ExecutorServiceMetrics.remove("single-thread-pool")
+ findExecutorRecorder("single-thread-pool") should be (empty)
+ }
+
+ "register a ThreadPoolExecutor, collect their metrics and remove it" in {
+ val threadPoolExecutor = Executors.newCachedThreadPool()
+ ExecutorServiceMetrics.register("thread-pool-executor", threadPoolExecutor)
+ findExecutorRecorder("thread-pool-executor") should not be empty
+
+ ExecutorServiceMetrics.remove("thread-pool-executor")
+ findExecutorRecorder("thread-pool-executor") should be (empty)
+ }
+
+ "register a ScheduledThreadPoolExecutor, collect their metrics and remove it" in {
+ val scheduledThreadPoolExecutor = Executors.newSingleThreadScheduledExecutor()
+ ExecutorServiceMetrics.register("scheduled-thread-pool-executor", scheduledThreadPoolExecutor)
+ findExecutorRecorder("scheduled-thread-pool-executor") should not be empty
+
+ ExecutorServiceMetrics.remove("scheduled-thread-pool-executor")
+ findExecutorRecorder("scheduled-thread-pool-executor") should be (empty)
+ }
+
+ "register a Java ForkJoinPool, collect their metrics and remove it" in {
+ val javaForkJoinPool = Executors.newWorkStealingPool()
+ ExecutorServiceMetrics.register("java-fork-join-pool", javaForkJoinPool)
+ findExecutorRecorder("java-fork-join-pool") should not be empty
+
+ ExecutorServiceMetrics.remove("java-fork-join-pool")
+ findExecutorRecorder("java-fork-join-pool") should be (empty)
+ }
+
+ "register a Scala ForkJoinPool, collect their metrics and remove it" in {
+ val scalaForkJoinPool = new scala.concurrent.forkjoin.ForkJoinPool()
+ ExecutorServiceMetrics.register("scala-fork-join-pool", scalaForkJoinPool)
+ findExecutorRecorder("scala-fork-join-pool") should not be empty
+
+ ExecutorServiceMetrics.remove("scala-fork-join-pool")
+ findExecutorRecorder("scala-fork-join-pool") should be (empty)
+ }
+
+ def findExecutorRecorder(name: String): Option[EntityRecorder] =
+ Kamon.metrics.find(name, ExecutorServiceMetrics.Category, Map.empty)
+ }
+
+}