diff options
author | Ivan Topolnjak <ivantopo@gmail.com> | 2014-07-29 02:17:21 -0300 |
---|---|---|
committer | Ivan Topolnjak <ivantopo@gmail.com> | 2014-07-29 02:17:21 -0300 |
commit | 409843750c596d31ce48b606677b1d5224be3880 (patch) | |
tree | b0d9eaec5f62acc223910ad5deafbac23125700a /kamon-system-metrics | |
parent | 4d336a1d2bd42a84a21e8770d8e0c401f8871aa7 (diff) | |
parent | e83babb5f88e91661bec2f1013fcb6b03612bea9 (diff) | |
download | Kamon-409843750c596d31ce48b606677b1d5224be3880.tar.gz Kamon-409843750c596d31ce48b606677b1d5224be3880.tar.bz2 Kamon-409843750c596d31ce48b606677b1d5224be3880.zip |
Merge branch 'master' into release-0.2
Conflicts:
kamon-core/src/main/scala/kamon/metrics/Subscriptions.scala
kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala
kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala
kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala
kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala
kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala
kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
project/Projects.scala
version.sbt
Diffstat (limited to 'kamon-system-metrics')
33 files changed, 1260 insertions, 0 deletions
diff --git a/kamon-system-metrics/src/main/resources/reference.conf b/kamon-system-metrics/src/main/resources/reference.conf new file mode 100644 index 00000000..0600388d --- /dev/null +++ b/kamon-system-metrics/src/main/resources/reference.conf @@ -0,0 +1,76 @@ +# ============================================ # +# Kamon-System-Metrics Reference Configuration # +# ============================================ # + +kamon { + metrics { + precision { + system { + process-cpu { + user = { + refresh-interval = 100 milliseconds + highest-trackable-value = 999999999 + significant-value-digits = 2 + } + system = { + refresh-interval = 100 milliseconds + highest-trackable-value = 999999999 + significant-value-digits = 2 + } + } + + cpu { + user = { + refresh-interval = 100 milliseconds + highest-trackable-value = 999 + significant-value-digits = 2 + } + system = { + refresh-interval = 100 milliseconds + highest-trackable-value = 999 + significant-value-digits = 2 + } + wait = { + refresh-interval = 100 milliseconds + highest-trackable-value = 999 + significant-value-digits = 2 + } + idle ={ + refresh-interval = 100 milliseconds + highest-trackable-value = 999 + significant-value-digits = 2 + } + } + + network { + rx-bytes = ${kamon.metrics.precision.default-histogram-precision} + tx-bytes = ${kamon.metrics.precision.default-histogram-precision} + rx-errors = ${kamon.metrics.precision.default-histogram-precision} + tx-errors = ${kamon.metrics.precision.default-histogram-precision} + } + + memory { + used = ${kamon.metrics.precision.default-histogram-precision} + free = ${kamon.metrics.precision.default-histogram-precision} + buffer = ${kamon.metrics.precision.default-histogram-precision} + cache = ${kamon.metrics.precision.default-histogram-precision} + swap-used = ${kamon.metrics.precision.default-histogram-precision} + swap-free = ${kamon.metrics.precision.default-histogram-precision} + } + } + + jvm { + heap { + used = ${kamon.metrics.precision.default-gauge-precision} + max = ${kamon.metrics.precision.default-gauge-precision} + committed = ${kamon.metrics.precision.default-gauge-precision} + } + + gc { + count = ${kamon.metrics.precision.default-gauge-precision} + time = ${kamon.metrics.precision.default-gauge-precision} + } + } + } + } +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala new file mode 100644 index 00000000..99288f94 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala @@ -0,0 +1,81 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.Histogram + +case class CPUMetrics(name: String) extends MetricGroupIdentity { + val category = CPUMetrics +} + +object CPUMetrics extends MetricGroupCategory { + val name = "cpu" + + case object User extends MetricIdentity { val name = "user" } + case object System extends MetricIdentity { val name = "system" } + case object Wait extends MetricIdentity { val name = "wait" } + case object Idle extends MetricIdentity { val name = "idle" } + + case class CPUMetricRecorder(user: Histogram, system: Histogram, cpuWait: Histogram, idle: Histogram) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + CPUMetricSnapshot(user.collect(context), system.collect(context), cpuWait.collect(context), idle.collect(context)) + } + + def cleanup: Unit = {} + } + + case class CPUMetricSnapshot(user: Histogram.Snapshot, system: Histogram.Snapshot, cpuWait: Histogram.Snapshot, idle: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = CPUMetricSnapshot + + def merge(that: CPUMetricSnapshot, context: CollectionContext): GroupSnapshotType = { + CPUMetricSnapshot(user.merge(that.user, context), system.merge(that.system, context), cpuWait.merge(that.cpuWait, context), idle.merge(that.idle, context)) + } + + lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + User -> user, + System -> system, + Wait -> cpuWait, + Idle -> idle) + } + + val Factory = new MetricGroupFactory { + + type GroupRecorder = CPUMetricRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.system.cpu") + + val userConfig = settings.getConfig("user") + val systemConfig = settings.getConfig("system") + val cpuWaitConfig = settings.getConfig("wait") + val idleConfig = settings.getConfig("idle") + + new CPUMetricRecorder( + Histogram.fromConfig(userConfig), + Histogram.fromConfig(systemConfig), + Histogram.fromConfig(cpuWaitConfig), + Histogram.fromConfig(idleConfig)) + } + } +} + diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala new file mode 100644 index 00000000..b5da600e --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala @@ -0,0 +1,75 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import java.lang.management.GarbageCollectorMXBean + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.{ Gauge, Histogram } + +case class GCMetrics(name: String) extends MetricGroupIdentity { + val category = GCMetrics +} + +object GCMetrics extends MetricGroupCategory { + val name = "gc" + + case object CollectionCount extends MetricIdentity { val name = "collection-count" } + case object CollectionTime extends MetricIdentity { val name = "collection-time" } + + case class GCMetricRecorder(count: Gauge, time: Gauge) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + GCMetricSnapshot(count.collect(context), time.collect(context)) + } + + def cleanup: Unit = {} + } + + case class GCMetricSnapshot(count: Histogram.Snapshot, time: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = GCMetricSnapshot + + def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { + GCMetricSnapshot(count.merge(that.count, context), time.merge(that.time, context)) + } + + lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + CollectionCount -> count, + CollectionTime -> time) + } + + def Factory(gc: GarbageCollectorMXBean) = new MetricGroupFactory { + + type GroupRecorder = GCMetricRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.jvm.gc") + + val countConfig = settings.getConfig("count") + val timeConfig = settings.getConfig("time") + + new GCMetricRecorder( + Gauge.fromConfig(countConfig, system)(() ⇒ gc.getCollectionCount), + Gauge.fromConfig(timeConfig, system, Scale.Milli)(() ⇒ gc.getCollectionTime)) + } + } +} + diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala new file mode 100644 index 00000000..c51b458c --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala @@ -0,0 +1,83 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import java.lang.management.ManagementFactory + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.{ Gauge, Histogram } + +case class HeapMetrics(name: String) extends MetricGroupIdentity { + val category = HeapMetrics +} + +object HeapMetrics extends MetricGroupCategory { + val name = "heap" + + case object Used extends MetricIdentity { val name = "used-heap" } + case object Max extends MetricIdentity { val name = "max-heap" } + case object Committed extends MetricIdentity { val name = "committed-heap" } + + case class HeapMetricRecorder(used: Gauge, max: Gauge, committed: Gauge) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + HeapMetricSnapshot(used.collect(context), max.collect(context), committed.collect(context)) + } + + def cleanup: Unit = {} + } + + case class HeapMetricSnapshot(used: Histogram.Snapshot, max: Histogram.Snapshot, committed: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = HeapMetricSnapshot + + def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { + HeapMetricSnapshot(used.merge(that.used, context), max.merge(that.max, context), committed.merge(that.committed, context)) + } + + lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + Used -> used, + Max -> max, + Committed -> committed) + } + + val Factory = new MetricGroupFactory { + import kamon.system.SystemMetricsExtension._ + + val memory = ManagementFactory.getMemoryMXBean + def heap = memory.getHeapMemoryUsage + + type GroupRecorder = HeapMetricRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.jvm.heap") + + val usedHeapConfig = settings.getConfig("used") + val maxHeapConfig = settings.getConfig("max") + val committedHeapConfig = settings.getConfig("committed") + + new HeapMetricRecorder( + Gauge.fromConfig(usedHeapConfig, system, Scale.Mega)(() ⇒ toMB(heap.getUsed)), + Gauge.fromConfig(maxHeapConfig, system, Scale.Mega)(() ⇒ toMB(heap.getMax)), + Gauge.fromConfig(committedHeapConfig, system, Scale.Mega)(() ⇒ toMB(heap.getCommitted))) + } + } +} + diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala new file mode 100644 index 00000000..6f3eb6df --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala @@ -0,0 +1,88 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.Histogram + +case class MemoryMetrics(name: String) extends MetricGroupIdentity { + val category = MemoryMetrics +} + +object MemoryMetrics extends MetricGroupCategory { + val name = "memory" + + case object Used extends MetricIdentity { val name = "used" } + case object Free extends MetricIdentity { val name = "free" } + case object Buffer extends MetricIdentity { val name = "buffer" } + case object Cache extends MetricIdentity { val name = "cache" } + case object SwapUsed extends MetricIdentity { val name = "swap-used" } + case object SwapFree extends MetricIdentity { val name = "swap-free" } + + case class MemoryMetricRecorder(used: Histogram, free: Histogram, buffer: Histogram, cache: Histogram, swapUsed: Histogram, swapFree: Histogram) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + MemoryMetricSnapshot(used.collect(context), free.collect(context), buffer.collect(context), cache.collect(context), swapUsed.collect(context), swapFree.collect(context)) + } + + def cleanup: Unit = {} + } + + case class MemoryMetricSnapshot(used: Histogram.Snapshot, free: Histogram.Snapshot, buffer: Histogram.Snapshot, cache: Histogram.Snapshot, swapUsed: Histogram.Snapshot, swapFree: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = MemoryMetricSnapshot + + def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { + MemoryMetricSnapshot(used.merge(that.used, context), free.merge(that.free, context), buffer.merge(that.buffer, context), cache.merge(that.cache, context), swapUsed.merge(that.swapUsed, context), swapFree.merge(that.swapFree, context)) + } + + lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + Used -> used, + Free -> free, + Buffer -> buffer, + Cache -> cache, + SwapUsed -> swapUsed, + SwapFree -> swapFree) + } + + val Factory = new MetricGroupFactory { + + type GroupRecorder = MemoryMetricRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.system.memory") + + val usedConfig = settings.getConfig("used") + val freeConfig = settings.getConfig("free") + val bufferConfig = settings.getConfig("buffer") + val cacheConfig = settings.getConfig("cache") + val swapUsedConfig = settings.getConfig("swap-used") + val swapFreeConfig = settings.getConfig("swap-free") + + new MemoryMetricRecorder( + Histogram.fromConfig(usedConfig, Scale.Mega), + Histogram.fromConfig(freeConfig, Scale.Mega), + Histogram.fromConfig(swapUsedConfig, Scale.Mega), + Histogram.fromConfig(swapFreeConfig, Scale.Mega), + Histogram.fromConfig(bufferConfig, Scale.Mega), + Histogram.fromConfig(cacheConfig, Scale.Mega)) + } + } +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala new file mode 100644 index 00000000..831a06e3 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala @@ -0,0 +1,80 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.Histogram + +case class NetworkMetrics(name: String) extends MetricGroupIdentity { + val category = NetworkMetrics +} + +object NetworkMetrics extends MetricGroupCategory { + val name = "network" + + case object RxBytes extends MetricIdentity { val name = "rx-bytes" } + case object TxBytes extends MetricIdentity { val name = "tx-bytes" } + case object RxErrors extends MetricIdentity { val name = "rx-errors" } + case object TxErrors extends MetricIdentity { val name = "tx-errors" } + + case class NetworkMetricRecorder(rxBytes: Histogram, txBytes: Histogram, rxErrors: Histogram, txErrors: Histogram) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + NetworkMetricSnapshot(rxBytes.collect(context), txBytes.collect(context), rxErrors.collect(context), txErrors.collect(context)) + } + + def cleanup: Unit = {} + } + + case class NetworkMetricSnapshot(rxBytes: Histogram.Snapshot, txBytes: Histogram.Snapshot, rxErrors: Histogram.Snapshot, txErrors: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = NetworkMetricSnapshot + + def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { + NetworkMetricSnapshot(rxBytes.merge(that.rxBytes, context), txBytes.merge(that.txBytes, context), rxErrors.merge(that.rxErrors, context), txErrors.merge(that.txErrors, context)) + } + + val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + RxBytes -> rxBytes, + TxBytes -> txBytes, + RxErrors -> rxErrors, + TxErrors -> txErrors) + } + + val Factory = new MetricGroupFactory { + + type GroupRecorder = NetworkMetricRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.system.network") + + val rxBytesConfig = settings.getConfig("rx-bytes") + val txBytesConfig = settings.getConfig("tx-bytes") + val rxErrorsConfig = settings.getConfig("rx-errors") + val txErrorsConfig = settings.getConfig("tx-errors") + + new NetworkMetricRecorder( + Histogram.fromConfig(rxBytesConfig, Scale.Kilo), + Histogram.fromConfig(txBytesConfig, Scale.Kilo), + Histogram.fromConfig(rxErrorsConfig), + Histogram.fromConfig(txErrorsConfig)) + } + } +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala new file mode 100644 index 00000000..61f7ddb2 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala @@ -0,0 +1,73 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.metrics + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import kamon.metric._ +import kamon.metric.instrument.Histogram + +case class ProcessCPUMetrics(name: String) extends MetricGroupIdentity { + val category = ProcessCPUMetrics +} + +object ProcessCPUMetrics extends MetricGroupCategory { + val name = "proc-cpu" + + case object User extends MetricIdentity { val name = "user" } + case object System extends MetricIdentity { val name = "system" } + + case class ProcessCPUMetricsRecorder(user: Histogram, system: Histogram) + extends MetricGroupRecorder { + + def collect(context: CollectionContext): MetricGroupSnapshot = { + ProcessCPUMetricsSnapshot(user.collect(context), system.collect(context)) + } + + def cleanup: Unit = {} + } + + case class ProcessCPUMetricsSnapshot(user: Histogram.Snapshot, system: Histogram.Snapshot) + extends MetricGroupSnapshot { + + type GroupSnapshotType = ProcessCPUMetricsSnapshot + + def merge(that: ProcessCPUMetricsSnapshot, context: CollectionContext): GroupSnapshotType = { + ProcessCPUMetricsSnapshot(user.merge(that.user, context), system.merge(that.system, context)) + } + + lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( + User -> user, + System -> system) + } + + val Factory = new MetricGroupFactory { + + type GroupRecorder = ProcessCPUMetricsRecorder + + def create(config: Config, system: ActorSystem): GroupRecorder = { + val settings = config.getConfig("precision.system.process-cpu") + + val userConfig = settings.getConfig("user") + val systemConfig = settings.getConfig("system") + + new ProcessCPUMetricsRecorder( + Histogram.fromConfig(userConfig), + Histogram.fromConfig(systemConfig)) + } + } +} + diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala new file mode 100644 index 00000000..29048915 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala @@ -0,0 +1,63 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.system + +import java.lang.management.ManagementFactory + +import akka.actor._ +import akka.event.Logging +import kamon.Kamon +import kamon.metric.Metrics +import kamon.metrics._ + +import scala.collection.JavaConverters._ +import scala.concurrent.duration._ + +object SystemMetrics extends ExtensionId[SystemMetricsExtension] with ExtensionIdProvider { + override def lookup(): ExtensionId[_ <: Extension] = SystemMetrics + + override def createExtension(system: ExtendedActorSystem): SystemMetricsExtension = new SystemMetricsExtension(system) +} + +class SystemMetricsExtension(private val system: ExtendedActorSystem) extends Kamon.Extension { + import kamon.system.SystemMetricsExtension._ + + val log = Logging(system, classOf[SystemMetricsExtension]) + log.info(s"Starting the Kamon(SystemMetrics) extension") + + val systemMetricsExtension = Kamon(Metrics)(system) + + //JVM Metrics + systemMetricsExtension.register(HeapMetrics(Heap), HeapMetrics.Factory) + garbageCollectors.map { gc ⇒ systemMetricsExtension.register(GCMetrics(gc.getName), GCMetrics.Factory(gc)) } + + //System Metrics + system.actorOf(SystemMetricsCollector.props(1 second), "system-metrics-collector") +} + +object SystemMetricsExtension { + val CPU = "cpu" + val ProcessCPU = "process-cpu" + val Network = "network" + val Memory = "memory" + val Heap = "heap" + + def toKB(value: Long): Long = (value / 1024) + def toMB(value: Long): Long = (value / 1024 / 1024) + def toLong(value: Double): Long = math round (value * 100L) + + val garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala new file mode 100644 index 00000000..a5a2f411 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala @@ -0,0 +1,115 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.system + +import akka.actor.{ Actor, Props } +import kamon.Kamon +import kamon.metric.Metrics +import kamon.metrics.CPUMetrics.CPUMetricRecorder +import kamon.metrics.MemoryMetrics.MemoryMetricRecorder +import kamon.metrics.NetworkMetrics.NetworkMetricRecorder +import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsRecorder +import kamon.metrics.{ CPUMetrics, MemoryMetrics, NetworkMetrics, ProcessCPUMetrics } +import kamon.system.sigar.SigarHolder +import org.hyperic.sigar.{ Mem, NetInterfaceStat, SigarProxy } + +import scala.concurrent.duration.FiniteDuration + +class SystemMetricsCollector(collectInterval: FiniteDuration) extends Actor with SigarExtensionProvider { + import kamon.system.SystemMetricsCollector._ + import kamon.system.SystemMetricsExtension._ + + val collectSchedule = context.system.scheduler.schedule(collectInterval, collectInterval, self, Collect)(context.dispatcher) + + val systemMetricsExtension = Kamon(Metrics)(context.system) + + val cpuRecorder = systemMetricsExtension.register(CPUMetrics(CPU), CPUMetrics.Factory) + val processCpuRecorder = systemMetricsExtension.register(ProcessCPUMetrics(ProcessCPU), ProcessCPUMetrics.Factory) + val memoryRecorder = systemMetricsExtension.register(MemoryMetrics(Memory), MemoryMetrics.Factory) + val networkRecorder = systemMetricsExtension.register(NetworkMetrics(Network), NetworkMetrics.Factory) + + def receive: Receive = { + case Collect ⇒ collectMetrics() + case anything ⇒ + } + + override def postStop() = collectSchedule.cancel() + + def collectMetrics() = { + cpuRecorder.map(recordCpu) + processCpuRecorder.map(recordProcessCpu) + memoryRecorder.map(recordMemory) + networkRecorder.map(recordNetwork) + } + + private def recordCpu(cpur: CPUMetricRecorder) = { + cpur.user.record(toLong(cpu.getUser)) + cpur.system.record(toLong(cpu.getSys)) + cpur.cpuWait.record(toLong(cpu.getWait())) + cpur.idle.record(toLong(cpu.getIdle)) + } + + private def recordProcessCpu(pcpur: ProcessCPUMetricsRecorder) = { + pcpur.user.record(procCpu.getUser) + pcpur.system.record(procCpu.getSys) + } + + private def recordMemory(mr: MemoryMetricRecorder) = { + mr.used.record(toMB(mem.getUsed)) + mr.free.record(toMB(mem.getFree)) + mr.swapUsed.record(toMB(swap.getUsed)) + mr.swapFree.record(toMB(swap.getFree)) + mr.buffer.record(toMB(collectBuffer(mem))) + mr.cache.record(toMB(collectCache(mem))) + + def collectBuffer(mem: Mem): Long = if (mem.getUsed() != mem.getActualUsed()) mem.getActualUsed() else 0L + def collectCache(mem: Mem): Long = if (mem.getFree() != mem.getActualFree()) mem.getActualFree() else 0L + } + + private def recordNetwork(nr: NetworkMetricRecorder) = { + nr.rxBytes.record(collect(sigar, interfaces)(net ⇒ toKB(net.getRxBytes))) + nr.txBytes.record(collect(sigar, interfaces)(net ⇒ toKB(net.getTxBytes))) + nr.rxErrors.record(collect(sigar, interfaces)(net ⇒ net.getRxErrors)) + nr.txErrors.record(collect(sigar, interfaces)(net ⇒ net.getTxErrors)) + + def collect(sigar: SigarProxy, interfaces: Set[String])(block: NetInterfaceStat ⇒ Long): Long = { + interfaces.foldLeft(0L) { (totalBytes, interface) ⇒ + { + val net = sigar.getNetInterfaceStat(interface) + totalBytes + block(net) + } + } + } + } +} + +object SystemMetricsCollector { + case object Collect + + def props(collectInterval: FiniteDuration): Props = Props[SystemMetricsCollector](new SystemMetricsCollector(collectInterval)) +} + +trait SigarExtensionProvider { + lazy val sigar = SigarHolder.instance() + + def pid = sigar.getPid + def procCpu = sigar.getProcCpu(pid) + def cpu = sigar.getCpuPerc + def mem = sigar.getMem + def swap = sigar.getSwap + + val interfaces: Set[String] = sigar.getNetInterfaceList.toSet +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala new file mode 100644 index 00000000..d138ec8f --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala @@ -0,0 +1,173 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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.system.sigar + +import java.io._ +import java.util +import java.util.logging.Logger +import java.util.{ ArrayList, Date, List } + +import org.hyperic.sigar.{ OperatingSystem, Sigar, SigarProxy } + +import scala.annotation.tailrec +import scala.collection.JavaConversions._ +import scala.io.Source + +object SigarHolder { + private lazy val sigarProxy = SigarLoader.sigarProxy + def instance() = sigarProxy +} + +object SigarLoader { + + val Version = "1.6.4" + val JavaLibraryPath = "java.library.path" + val TmpDir = "java.io.tmpdir" + val IndexFile = "/kamon/system/sigar/index" + val UsrPathField = "usr_paths" + + private val log = Logger.getLogger("SigarLoader") + + def sigarProxy = init(new File(System.getProperty(TmpDir))) + + private[sigar] def init(baseTmp: File): SigarProxy = { + val tmpDir = createTmpDir(baseTmp) + for (lib ← loadIndex) copy(lib, tmpDir) + + attachToLibraryPath(tmpDir) + + try { + val sigar = new Sigar() + printBanner(sigar) + sigar + } catch { + case t: Throwable ⇒ { + log.severe("Failed to load sigar") + throw new RuntimeException(t) + } + } + } + + private[sigar] val usrPathField = { + val usrPathField = classOf[ClassLoader].getDeclaredField(UsrPathField) + usrPathField.setAccessible(true) + usrPathField + } + + private[sigar] def attachToLibraryPath(dir: File): Unit = { + val dirAbsolute = dir.getAbsolutePath + System.setProperty(JavaLibraryPath, newLibraryPath(dirAbsolute)) + var paths = usrPathField.get(null).asInstanceOf[Array[String]] + if (paths == null) paths = new Array[String](0) + for (path ← paths) if (path == dirAbsolute) return + val newPaths = util.Arrays.copyOf(paths, paths.length + 1) + newPaths(newPaths.length - 1) = dirAbsolute + usrPathField.set(null, newPaths) + } + + private[sigar] def newLibraryPath(dirAbsolutePath: String): String = { + Option(System.getProperty(JavaLibraryPath)).fold(dirAbsolutePath)(oldValue ⇒ s"$dirAbsolutePath${File.pathSeparator}$oldValue") + } + + private[sigar] def copy(lib: String, tmpDir: File) { + val target = new File(tmpDir, lib) + if (target.exists()) return + write(classOf[Loader].getResourceAsStream(lib), target) + } + + private[sigar] def createTmpDir(baseTmp: File): File = { + val tmpDir = new File(baseTmp, s"sigar-$Version") + if (!tmpDir.exists()) { + if (!tmpDir.mkdirs()) throw new RuntimeException(s"Could not create temp sigar directory: ${tmpDir.getAbsolutePath}") + } + if (!tmpDir.isDirectory) throw new RuntimeException(s"sigar temp directory path is not a directory: ${tmpDir.getAbsolutePath}") + if (!tmpDir.canWrite()) throw new RuntimeException(s"sigar temp directory not writeable: ${tmpDir.getAbsolutePath}") + tmpDir + } + + private[sigar] def loadIndex(): List[String] = { + val libs = new ArrayList[String]() + val is = classOf[Loader].getResourceAsStream(IndexFile) + + for (line ← Source.fromInputStream(is).getLines()) { + val currentLine = line.trim() + libs add currentLine + } + libs + } + + private[sigar] def write(input: InputStream, to: File) { + val out = new FileOutputStream(to) + try { + transfer(input, out) + } finally { + out.close() + } + } + + private[sigar] def transfer(input: InputStream, out: OutputStream) { + val buffer = new Array[Byte](8192) + + @tailrec def transfer() { + val read = input.read(buffer) + if (read >= 0) { + out.write(buffer, 0, read) + transfer() + } + } + transfer() + } + + private[sigar] def printBanner(sigar: Sigar) = { + def loadAverage(sigar: Sigar) = { + val average = sigar.getLoadAverage + (average(0), average(1), average(2)) + } + + def uptime(sigar: Sigar) = { + val uptime = sigar.getUptime + val now = System.currentTimeMillis() + new Date(now - (uptime.getUptime() * 1000).toLong) + } + + def osInfo() = { + val NewLine = "\n" + val os = OperatingSystem.getInstance + val osInfo = new StringBuilder("------ OS Information ------").append(NewLine) + osInfo.append("Description: ").append(os.getDescription).append(NewLine) + .append("Name: ").append(os.getName).append(NewLine) + .append("Version: ").append(os.getVersion).append(NewLine) + .append("Arch: ").append(os.getArch).append(NewLine) + .toString() + } + + val message = + """ + | + | _____ _ __ __ _ _ _ _ _ + | / ____| | | | \/ | | | (_) | | | | | | + || (___ _ _ ___| |_ ___ _ __ ___ | \ / | ___| |_ _ __ _ ___ ___| | ___ __ _ __| | ___ __| | + | \___ \| | | / __| __/ _ \ '_ ` _ \| |\/| |/ _ \ __| '__| |/ __/ __| | / _ \ / _` |/ _` |/ _ \/ _` | + | ____) | |_| \__ \ || __/ | | | | | | | | __/ |_| | | | (__\__ \ |___| (_) | (_| | (_| | __/ (_| | + ||_____/ \__, |___/\__\___|_| |_| |_|_| |_|\___|\__|_| |_|\___|___/______\___/ \__,_|\__,_|\___|\__,_| + | __/ | + | |___/ + """.stripMargin + s"\nBoot Time: ${uptime(sigar)} \nLoad Average: ${loadAverage(sigar)} \n${osInfo()}" + log.info(message) + } + class Loader private[sigar] +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/index b/kamon-system-metrics/src/main/scala/kamon/system/sigar/index new file mode 100644 index 00000000..cad1f326 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/index @@ -0,0 +1,21 @@ +libsigar-amd64-freebsd-6.so +libsigar-amd64-linux.so +libsigar-amd64-solaris.so +libsigar-ia64-hpux-11.sl +libsigar-ia64-linux.so +libsigar-pa-hpux-11.sl +libsigar-ppc64-aix-5.so +libsigar-ppc64-linux.so +libsigar-ppc-aix-5.so +libsigar-ppc-linux.so +libsigar-s390x-linux.so +libsigar-sparc64-solaris.so +libsigar-sparc-solaris.so +libsigar-universal64-macosx.dylib +libsigar-universal-macosx.dylib +libsigar-x86-freebsd-5.so +libsigar-x86-freebsd-6.so +libsigar-x86-linux.so +libsigar-x86-solaris.so +sigar-amd64-winnt.dll +sigar-x86-winnt.dll
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-freebsd-6.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-freebsd-6.so Binary files differnew file mode 100644 index 00000000..3e94f0d2 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-freebsd-6.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-linux.so Binary files differnew file mode 100644 index 00000000..5a2e4c24 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-solaris.so Binary files differnew file mode 100644 index 00000000..6396482a --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-amd64-solaris.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-hpux-11.sl b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-hpux-11.sl Binary files differnew file mode 100644 index 00000000..d92ea4a9 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-hpux-11.sl diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-linux.so Binary files differnew file mode 100644 index 00000000..2bd2fc8e --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ia64-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-pa-hpux-11.sl b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-pa-hpux-11.sl Binary files differnew file mode 100644 index 00000000..0dfd8a11 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-pa-hpux-11.sl diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-aix-5.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-aix-5.so Binary files differnew file mode 100644 index 00000000..7d4b5199 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-aix-5.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-linux.so Binary files differnew file mode 100644 index 00000000..4394b1b0 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-aix-5.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-aix-5.so Binary files differnew file mode 100644 index 00000000..35fd8288 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-aix-5.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-linux.so Binary files differnew file mode 100644 index 00000000..a1ba2529 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-ppc64-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-s390x-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-s390x-linux.so Binary files differnew file mode 100644 index 00000000..c275f4ac --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-s390x-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc-solaris.so Binary files differnew file mode 100644 index 00000000..aa847d2b --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc-solaris.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc64-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc64-solaris.so Binary files differnew file mode 100644 index 00000000..6c4fe809 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-sparc64-solaris.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal-macosx.dylib b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal-macosx.dylib Binary files differnew file mode 100644 index 00000000..27ab1071 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal-macosx.dylib diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal64-macosx.dylib b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal64-macosx.dylib Binary files differnew file mode 100644 index 00000000..0c721fec --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-universal64-macosx.dylib diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-5.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-5.so Binary files differnew file mode 100644 index 00000000..8c50c611 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-5.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-6.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-6.so Binary files differnew file mode 100644 index 00000000..f0800274 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-freebsd-6.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-linux.so Binary files differnew file mode 100644 index 00000000..a0b64edd --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-linux.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-solaris.so Binary files differnew file mode 100644 index 00000000..c6452e56 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/libsigar-x86-solaris.so diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-amd64-winnt.dll b/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-amd64-winnt.dll Binary files differnew file mode 100644 index 00000000..1ec8a035 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-amd64-winnt.dll diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-x86-winnt.dll b/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-x86-winnt.dll Binary files differnew file mode 100644 index 00000000..6afdc016 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/sigar-x86-winnt.dll diff --git a/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala b/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala new file mode 100644 index 00000000..09904a5f --- /dev/null +++ b/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala @@ -0,0 +1,332 @@ +/* ========================================================================================= + * Copyright © 2013-2014 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.metric + +import akka.actor.ActorSystem +import akka.testkit.{ TestKitBase, TestProbe } +import com.typesafe.config.ConfigFactory +import kamon.Kamon +import kamon.metric.Subscriptions.TickMetricSnapshot +import kamon.metrics.CPUMetrics.CPUMetricSnapshot +import kamon.metrics.GCMetrics.GCMetricSnapshot +import kamon.metrics.HeapMetrics.HeapMetricSnapshot +import kamon.metrics.MemoryMetrics.MemoryMetricSnapshot +import kamon.metrics.NetworkMetrics.NetworkMetricSnapshot +import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsSnapshot +import kamon.metrics._ +import kamon.system.SystemMetricsExtension +import org.scalatest.{ Ignore, Matchers, WordSpecLike } + +import scala.concurrent.duration._ + +@Ignore +class SystemMetricsSpec extends TestKitBase with WordSpecLike with Matchers { + implicit lazy val system: ActorSystem = ActorSystem("system-metrics-spec", ConfigFactory.parseString( + """ + |akka { + | extensions = ["kamon.system.SystemMetrics"] + |} + | + |kamon.metrics { + | + | disable-aspectj-weaver-missing-error = true + | + | tick-interval = 1 second + | + | system { + | cpu { + | user { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | system { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | wait { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | idle { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | } + | process-cpu { + | user { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | system { + | highest-trackable-value = 999999999 + | significant-value-digits = 2 + | } + | } + | memory { + | used { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | free { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | buffer { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | cache { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | swap-used { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | swap-free { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | } + | network { + | rx-bytes { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | tx-bytes { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | rx-errors { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | tx-errors { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | } + | heap { + | used { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | max { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | committed { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | } + | gc { + | count { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | time { + | highest-trackable-value = 3600000000000 + | significant-value-digits = 2 + | } + | } + | } + |} + """.stripMargin)) + + "the Kamon CPU Metrics" should { + "record user, system, wait, idle metrics" in new CPUMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val CPUMetrics = expectCPUMetrics(metricsListener, 3 seconds) + CPUMetrics.user.max should be >= 0L + CPUMetrics.system.max should be >= 0L + CPUMetrics.cpuWait.max should be >= 0L + CPUMetrics.idle.max should be >= 0L + } + } + "the Kamon GC Metrics" should { + "record count, time metrics" in new GCMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val GCMetrics = expectGCMetrics(metricsListener, 3 seconds) + GCMetrics.count.max should be > 0L + GCMetrics.time.max should be > 0L + } + } + + "the Kamon Heap Metrics" should { + "record used, max, commited metrics" in new HeapMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val HeapMetrics = expectHeapMetrics(metricsListener, 3 seconds) + HeapMetrics.used.max should be >= 0L + HeapMetrics.max.max should be >= 0L + HeapMetrics.committed.max should be >= 0L + } + } + + "the Kamon Memory Metrics" should { + "record used, free, buffer, cache, swap used, swap free metrics" in new MemoryMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val MemoryMetrics = expectMemoryMetrics(metricsListener, 3 seconds) + MemoryMetrics.used.max should be >= 0L + MemoryMetrics.free.max should be >= 0L + MemoryMetrics.buffer.max should be >= 0L + MemoryMetrics.cache.max should be >= 0L + MemoryMetrics.swapUsed.max should be >= 0L + MemoryMetrics.swapFree.max should be >= 0L + } + } + + "the Kamon Network Metrics" should { + "record rxBytes, txBytes, rxErrors, txErrors metrics" in new NetworkMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val NetworkMetrics = expectNetworkMetrics(metricsListener, 3 seconds) + NetworkMetrics.rxBytes.max should be >= 0L + NetworkMetrics.txBytes.max should be >= 0L + NetworkMetrics.rxErrors.max should be >= 0L + NetworkMetrics.txErrors.max should be >= 0L + } + } + + "the Kamon Process CPU Metrics" should { + "record user, system metrics" in new ProcessCPUMetricsListenerFixture { + val metricsListener = subscribeToMetrics() + + val ProcessCPUMetrics = expectProcessCPUMetrics(metricsListener, 3 seconds) + ProcessCPUMetrics.user.max should be > 0L + ProcessCPUMetrics.system.max should be > 0L + } + } + + def expectCPUMetrics(listener: TestProbe, waitTime: FiniteDuration): CPUMetricSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + val cpuMetricsOption = tickSnapshot.metrics.get(CPUMetrics(SystemMetricsExtension.CPU)) + cpuMetricsOption should not be empty + cpuMetricsOption.get.asInstanceOf[CPUMetricSnapshot] + } + + trait CPUMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(CPUMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } + + def expectGCMetrics(listener: TestProbe, waitTime: FiniteDuration): GCMetricSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + + val gcMetricsOption = tickSnapshot.metrics.get(GCMetrics(SystemMetricsExtension.garbageCollectors(0).getName)) + gcMetricsOption should not be empty + gcMetricsOption.get.asInstanceOf[GCMetricSnapshot] + } + + trait GCMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(GCMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } + + def expectHeapMetrics(listener: TestProbe, waitTime: FiniteDuration): HeapMetricSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + val heapMetricsOption = tickSnapshot.metrics.get(HeapMetrics(SystemMetricsExtension.Heap)) + heapMetricsOption should not be empty + heapMetricsOption.get.asInstanceOf[HeapMetricSnapshot] + } + + trait HeapMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(HeapMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } + + def expectMemoryMetrics(listener: TestProbe, waitTime: FiniteDuration): MemoryMetricSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + val memoryMetricsOption = tickSnapshot.metrics.get(MemoryMetrics(SystemMetricsExtension.Memory)) + memoryMetricsOption should not be empty + memoryMetricsOption.get.asInstanceOf[MemoryMetricSnapshot] + } + + trait MemoryMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(MemoryMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } + + def expectNetworkMetrics(listener: TestProbe, waitTime: FiniteDuration): NetworkMetricSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + val networkMetricsOption = tickSnapshot.metrics.get(NetworkMetrics(SystemMetricsExtension.Network)) + networkMetricsOption should not be empty + networkMetricsOption.get.asInstanceOf[NetworkMetricSnapshot] + } + + trait NetworkMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(NetworkMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } + + def expectProcessCPUMetrics(listener: TestProbe, waitTime: FiniteDuration): ProcessCPUMetricsSnapshot = { + val tickSnapshot = within(waitTime) { + listener.expectMsgType[TickMetricSnapshot] + } + val processCPUMetricsOption = tickSnapshot.metrics.get(ProcessCPUMetrics(SystemMetricsExtension.ProcessCPU)) + processCPUMetricsOption should not be empty + processCPUMetricsOption.get.asInstanceOf[ProcessCPUMetricsSnapshot] + } + + trait ProcessCPUMetricsListenerFixture { + def subscribeToMetrics(): TestProbe = { + val metricsListener = TestProbe() + Kamon(Metrics).subscribe(ProcessCPUMetrics, "*", metricsListener.ref, permanently = true) + // Wait for one empty snapshot before proceeding to the test. + metricsListener.expectMsgType[TickMetricSnapshot] + metricsListener + } + } +} |