aboutsummaryrefslogtreecommitdiff
path: root/kamon-system-metrics
diff options
context:
space:
mode:
authorDiego <diegolparra@gmail.com>2014-06-24 23:35:14 -0300
committerDiego <diegolparra@gmail.com>2014-07-21 17:13:28 -0300
commit5c141733ad39cf2730cf34dc8f3f4a82f0c1b516 (patch)
treeab2830795cacae80aa232e41ed224534572ba678 /kamon-system-metrics
parenta96f6dadd5f77271672215e731b842ce785954f4 (diff)
downloadKamon-5c141733ad39cf2730cf34dc8f3f4a82f0c1b516.tar.gz
Kamon-5c141733ad39cf2730cf34dc8f3f4a82f0c1b516.tar.bz2
Kamon-5c141733ad39cf2730cf34dc8f3f4a82f0c1b516.zip
! kamon-system-metrics: introducing System and JVM metrics module
Diffstat (limited to 'kamon-system-metrics')
-rw-r--r--kamon-system-metrics/src/main/resources/reference.conf76
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala84
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala75
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala82
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala96
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala94
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala76
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala64
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/SigarLoader.scala131
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/index22
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-freebsd-6.sobin0 -> 210641 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-linux.sobin0 -> 246605 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-solaris.sobin0 -> 251360 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-hpux-11.slbin0 -> 577452 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-linux.sobin0 -> 494929 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-pa-hpux-11.slbin0 -> 516096 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-aix-5.sobin0 -> 400925 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-linux.sobin0 -> 258547 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-aix-5.sobin0 -> 425077 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-linux.sobin0 -> 330767 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-s390x-linux.sobin0 -> 269932 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc-solaris.sobin0 -> 285004 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc64-solaris.sobin0 -> 261896 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal-macosx.dylibbin0 -> 377668 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal64-macosx.dylibbin0 -> 397440 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-5.sobin0 -> 179751 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-6.sobin0 -> 179379 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-linux.sobin0 -> 233385 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-solaris.sobin0 -> 242880 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/sigar-amd64-winnt.dllbin0 -> 402432 bytes
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/native/sigar-x86-winnt.dllbin0 -> 266240 bytes
-rw-r--r--kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala331
32 files changed, 1131 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..d6eb0576
--- /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 = 999999999
+ significant-value-digits = 2
+ }
+ system = {
+ refresh-interval = 100 milliseconds
+ highest-trackable-value = 999999999
+ significant-value-digits = 2
+ }
+ wait = {
+ refresh-interval = 100 milliseconds
+ highest-trackable-value = 999999999
+ significant-value-digits = 2
+ }
+ idle ={
+ refresh-interval = 100 milliseconds
+ highest-trackable-value = 999999999
+ significant-value-digits = 2
+ }
+ }
+
+ network {
+ rx-bytes = ${kamon.metrics.precision.default-gauge-precision}
+ tx-bytes = ${kamon.metrics.precision.default-gauge-precision}
+ rx-errors = ${kamon.metrics.precision.default-gauge-precision}
+ tx-errors = ${kamon.metrics.precision.default-gauge-precision}
+ }
+
+ memory {
+ used = ${kamon.metrics.precision.default-gauge-precision}
+ free = ${kamon.metrics.precision.default-gauge-precision}
+ buffer = ${kamon.metrics.precision.default-gauge-precision}
+ cache = ${kamon.metrics.precision.default-gauge-precision}
+ swap-used = ${kamon.metrics.precision.default-gauge-precision}
+ swap-free = ${kamon.metrics.precision.default-gauge-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..c098d1e5
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala
@@ -0,0 +1,84 @@
+/*
+ * =========================================================================================
+ * 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.instrument.{ Gauge, Histogram }
+import kamon.metric._
+import kamon.system.SigarExtensionProvider
+import org.hyperic.sigar.SigarProxy
+
+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: Gauge, system: Gauge, cpuWait: Gauge, idle: Gauge)
+ 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 with SigarExtensionProvider {
+ def cpu = sigar.getCpu
+
+ 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(
+ Gauge.fromConfig(userConfig, system)(() ⇒ cpu.getUser),
+ Gauge.fromConfig(systemConfig, system)(() ⇒ cpu.getSys),
+ Gauge.fromConfig(cpuWaitConfig, system)(() ⇒ cpu.getWait),
+ Gauge.fromConfig(idleConfig, system)(() ⇒ cpu.getIdle))
+ }
+ }
+}
+
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..09174f47
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala
@@ -0,0 +1,82 @@
+/*
+ * =========================================================================================
+ * 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 {
+
+ 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)(() ⇒ heap.getUsed),
+ Gauge.fromConfig(maxHeapConfig, system)(() ⇒ heap.getMax),
+ Gauge.fromConfig(committedHeapConfig, system)(() ⇒ 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..4f6cb1cd
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala
@@ -0,0 +1,96 @@
+/*
+ * =========================================================================================
+ * 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.Gauge.CurrentValueCollector
+import kamon.metric.instrument.{ Gauge, Histogram }
+import kamon.system.SigarExtensionProvider
+import org.hyperic.sigar.Mem
+
+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: Gauge, free: Gauge, buffer: Gauge, cache: Gauge, swapUsed: Gauge, swapFree: Gauge)
+ 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 with SigarExtensionProvider {
+ def mem = sigar.getMem
+ def swap = sigar.getSwap
+
+ 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(
+ Gauge.fromConfig(usedConfig, system)(() ⇒ mem.getUsed),
+ Gauge.fromConfig(freeConfig, system)(() ⇒ mem.getFree),
+ Gauge.fromConfig(bufferConfig, system)(() ⇒ swap.getUsed),
+ Gauge.fromConfig(cacheConfig, system)(() ⇒ swap.getFree),
+ Gauge.fromConfig(swapUsedConfig, system)(collectBuffer(mem)),
+ Gauge.fromConfig(swapFreeConfig, system)(collectCache(mem)))
+ }
+
+ private def collectBuffer(mem: Mem) = () ⇒ if (mem.getUsed() != mem.getActualUsed()) mem.getActualUsed() else 0L
+ private def collectCache(mem: Mem) = () ⇒ if (mem.getFree() != mem.getActualFree()) mem.getActualFree() else 0L
+ }
+} \ 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..62fc3fcd
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala
@@ -0,0 +1,94 @@
+/*
+ * =========================================================================================
+ * 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.Gauge.CurrentValueCollector
+import kamon.metric.instrument.{ Gauge, Histogram }
+import kamon.system.SigarExtensionProvider
+import org.hyperic.sigar.{ NetInterfaceStat, SigarProxy }
+
+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: Gauge, txBytes: Gauge, rxErrors: Gauge, txErrors: Gauge)
+ 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 with SigarExtensionProvider {
+
+ val interfaces: Set[String] = sigar.getNetInterfaceList.toSet
+
+ 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(
+ Gauge.fromConfig(rxBytesConfig, system)(collect(sigar, interfaces)(net ⇒ net.getRxBytes)),
+ Gauge.fromConfig(txBytesConfig, system)(collect(sigar, interfaces)(net ⇒ net.getTxBytes)),
+ Gauge.fromConfig(rxErrorsConfig, system)(collect(sigar, interfaces)(net ⇒ net.getRxErrors)),
+ Gauge.fromConfig(txErrorsConfig, system)(collect(sigar, interfaces)(net ⇒ net.getTxErrors)))
+ }
+
+ private def collect(sigar: SigarProxy, interfaces: Set[String])(block: NetInterfaceStat ⇒ Long) = () ⇒ {
+ interfaces.foldLeft(0L) { (totalBytes, interface) ⇒
+ {
+ val net = sigar.getNetInterfaceStat(interface)
+ totalBytes + block(net)
+ }
+ }
+ }
+ }
+} \ 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..356504b7
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala
@@ -0,0 +1,76 @@
+/*
+ * =========================================================================================
+ * 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.instrument.{ Gauge, Histogram }
+import kamon.metric._
+import kamon.system.SigarExtensionProvider
+
+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: Gauge, system: Gauge)
+ 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 with SigarExtensionProvider {
+ def pid = sigar.getPid
+ def cpu = sigar.getProcCpu(pid)
+
+ 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(
+ Gauge.fromConfig(userConfig, system)(() ⇒ cpu.getUser),
+ Gauge.fromConfig(systemConfig, system)(() ⇒ cpu.getSys))
+ }
+ }
+}
+
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..e46fca24
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala
@@ -0,0 +1,64 @@
+/*
+ * =========================================================================================
+ * 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 kamon.system.native.SigarLoader
+import scala.collection.JavaConverters._
+
+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)
+
+ systemMetricsExtension.register(CPUMetrics(CPU), CPUMetrics.Factory)
+ systemMetricsExtension.register(ProcessCPUMetrics(ProcessCPU), ProcessCPUMetrics.Factory)
+ systemMetricsExtension.register(NetworkMetrics(Network), NetworkMetrics.Factory)
+ systemMetricsExtension.register(MemoryMetrics(Memory), MemoryMetrics.Factory)
+ systemMetricsExtension.register(HeapMetrics(Heap), HeapMetrics.Factory)
+
+ garbageCollectors.map { gc ⇒ systemMetricsExtension.register(GCMetrics(gc.getName), GCMetrics.Factory(gc)) }
+}
+
+object SystemMetricsExtension {
+ val CPU = "cpu"
+ val ProcessCPU = "process-cpu"
+ val Network = "network"
+ val Memory = "memory"
+ val Heap = "heap"
+
+ val garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid)
+}
+
+trait SigarExtensionProvider {
+ lazy val sigar = SigarLoader.sigarProxy
+}
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/SigarLoader.scala b/kamon-system-metrics/src/main/scala/kamon/system/native/SigarLoader.scala
new file mode 100644
index 00000000..6af0a6d2
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/SigarLoader.scala
@@ -0,0 +1,131 @@
+/*
+ * =========================================================================================
+ * 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.native
+
+import java.io._
+import java.util
+import java.util.logging.Logger
+import java.util.{ ArrayList, List }
+
+import org.hyperic.sigar.{ SigarProxy, SigarProxyCache }
+
+import scala.annotation.tailrec
+import scala.collection.JavaConversions._
+import scala.io.Source
+
+object SigarLoader {
+
+ val Version = "1.6.4"
+ val JavaLibraryPath = "java.library.path"
+ val TmpDir = "java.io.tmpdir"
+ val IndexFile = "/kamon/system/native/index"
+ val UsrPathField = "usr_paths"
+
+ private val log = Logger.getLogger("SigarLoader")
+
+ def sigarProxy = init(new File(System.getProperty(TmpDir)))
+
+ private[native] def init(baseTmp: File): SigarProxy = {
+ val tmpDir = createTmpDir(baseTmp)
+ for (lib ← loadIndex) copy(lib, tmpDir)
+
+ attachToLibraryPath(tmpDir)
+
+ try {
+ val sigar = SigarProxyCache.newInstance()
+ sigar.getPid
+ sigar
+ } catch {
+ case t: Throwable ⇒ {
+ log.severe("Failed to load sigar")
+ throw new RuntimeException(t)
+ }
+ }
+ }
+
+ private[native] val usrPathField = {
+ val usrPathField = classOf[ClassLoader].getDeclaredField(UsrPathField)
+ usrPathField.setAccessible(true)
+ usrPathField
+ }
+
+ private[native] 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[native] def newLibraryPath(dirAbsolutePath: String): String = {
+ Option(System.getProperty(JavaLibraryPath)).fold(dirAbsolutePath)(oldValue ⇒ s"$dirAbsolutePath${File.pathSeparator}$oldValue")
+ }
+
+ private[native] def copy(lib: String, tmpDir: File) {
+ val target = new File(tmpDir, lib)
+ if (target.exists()) return
+ write(classOf[Loader].getResourceAsStream(lib), target)
+ }
+
+ private[native] 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[native] 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[native] def write(input: InputStream, to: File) {
+ val out = new FileOutputStream(to)
+ try {
+ transfer(input, out)
+ } finally {
+ out.close()
+ }
+ }
+
+ private[native] 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()
+ }
+
+ class Loader private[native]
+}
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/index b/kamon-system-metrics/src/main/scala/kamon/system/native/index
new file mode 100644
index 00000000..ebc7f952
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/index
@@ -0,0 +1,22 @@
+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
+
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-freebsd-6.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-freebsd-6.so
new file mode 100644
index 00000000..3e94f0d2
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-freebsd-6.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-linux.so
new file mode 100644
index 00000000..5a2e4c24
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-solaris.so
new file mode 100644
index 00000000..6396482a
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-amd64-solaris.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-hpux-11.sl b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-hpux-11.sl
new file mode 100644
index 00000000..d92ea4a9
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-hpux-11.sl
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-linux.so
new file mode 100644
index 00000000..2bd2fc8e
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ia64-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-pa-hpux-11.sl b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-pa-hpux-11.sl
new file mode 100644
index 00000000..0dfd8a11
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-pa-hpux-11.sl
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-aix-5.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-aix-5.so
new file mode 100644
index 00000000..7d4b5199
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-aix-5.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-linux.so
new file mode 100644
index 00000000..4394b1b0
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-aix-5.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-aix-5.so
new file mode 100644
index 00000000..35fd8288
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-aix-5.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-linux.so
new file mode 100644
index 00000000..a1ba2529
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-ppc64-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-s390x-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-s390x-linux.so
new file mode 100644
index 00000000..c275f4ac
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-s390x-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc-solaris.so
new file mode 100644
index 00000000..aa847d2b
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc-solaris.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc64-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc64-solaris.so
new file mode 100644
index 00000000..6c4fe809
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-sparc64-solaris.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal-macosx.dylib b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal-macosx.dylib
new file mode 100644
index 00000000..27ab1071
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal-macosx.dylib
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal64-macosx.dylib b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal64-macosx.dylib
new file mode 100644
index 00000000..0c721fec
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-universal64-macosx.dylib
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-5.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-5.so
new file mode 100644
index 00000000..8c50c611
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-5.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-6.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-6.so
new file mode 100644
index 00000000..f0800274
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-freebsd-6.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-linux.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-linux.so
new file mode 100644
index 00000000..a0b64edd
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-linux.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-solaris.so b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-solaris.so
new file mode 100644
index 00000000..c6452e56
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/libsigar-x86-solaris.so
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-amd64-winnt.dll b/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-amd64-winnt.dll
new file mode 100644
index 00000000..1ec8a035
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-amd64-winnt.dll
Binary files differ
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-x86-winnt.dll b/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-x86-winnt.dll
new file mode 100644
index 00000000..6afdc016
--- /dev/null
+++ b/kamon-system-metrics/src/main/scala/kamon/system/native/sigar-x86-winnt.dll
Binary files differ
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..ed10903f
--- /dev/null
+++ b/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala
@@ -0,0 +1,331 @@
+/* =========================================================================================
+ * 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.HeapMetrics.HeapMetricSnapshot
+import kamon.metrics.MemoryMetrics.MemoryMetricSnapshot
+import kamon.metrics.NetworkMetrics.NetworkMetricSnapshot
+import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsSnapshot
+import kamon.metrics._
+import kamon.metrics.CPUMetrics.CPUMetricSnapshot
+import kamon.metrics.GCMetrics.GCMetricSnapshot
+import kamon.system.SystemMetricsExtension
+import org.scalatest.{ Matchers, WordSpecLike }
+
+import scala.concurrent.duration._
+
+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
+ }
+ }
+}