diff options
Diffstat (limited to 'kamon-system-metrics/src')
34 files changed, 874 insertions, 1996 deletions
diff --git a/kamon-system-metrics/src/main/resources/reference.conf b/kamon-system-metrics/src/main/resources/reference.conf index ba439024..7f65e477 100644 --- a/kamon-system-metrics/src/main/resources/reference.conf +++ b/kamon-system-metrics/src/main/resources/reference.conf @@ -2,129 +2,180 @@ # Kamon-System-Metrics Reference Configuration # # ============================================ # -# Sigar provisioner native library extract location. -# Use per-application-instance scoped location, such as program working directory. -kamon.sigar.folder = ${user.dir}"/native" - kamon { system-metrics { - default-gauge-precision { - refresh-interval = 1 second - highest-trackable-value = 999999999 - significant-value-digits = 2 + # Sigar provisioner native library extract location. Use per-application-instance scoped location, such as program + # working directory. + sigar-native-folder = ${user.dir}"/native" + + # Frequency with which all Sigar-based metrics will be updated. Setting this value to less than 1 second + # might cause some Sigar metrics to behave incorrectly. + sigar-metrics-refresh-interval = 1 second + + # Frequency with which context-switches metrics will be updated. + context-switches-refresh-interval = 1 second + + # Dispatcher to be used by the SigarMetricsUpdater actor. + sigar-dispatcher { + executor = "thread-pool-executor" + type = PinnedDispatcher } - # Default dispatcher for all system-metrics module operations - dispatcher = ${kamon.default-dispatcher} + # Dispatcher to be used by the ContextSwitchesUpdater actor. + context-switches-dispatcher { + executor = "thread-pool-executor" + type = PinnedDispatcher + } } - metrics { - precision { - - system { - process-cpu { - cpu-percentage = { - highest-trackable-value = 999999999 - significant-value-digits = 2 - } - total-process-time = { - highest-trackable-value = 999999999 - significant-value-digits = 2 - } - } - - cpu { - user = { - highest-trackable-value = 999 - significant-value-digits = 2 - } - system = { - highest-trackable-value = 999 - significant-value-digits = 2 - } - wait = { - highest-trackable-value = 999 - significant-value-digits = 2 - } - idle = { - highest-trackable-value = 999 - significant-value-digits = 2 - } - stolen = { - 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} - rx-dropped = ${kamon.metrics.precision.default-histogram-precision} - tx-dropped = ${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} - } - - context-switches { - per-process-voluntary = ${kamon.metrics.precision.default-histogram-precision} - per-process-non-voluntary = ${kamon.metrics.precision.default-histogram-precision} - global = ${kamon.metrics.precision.default-histogram-precision} - } - - disk { - reads = ${kamon.metrics.precision.default-histogram-precision} - writes = ${kamon.metrics.precision.default-histogram-precision} - queue = ${kamon.metrics.precision.default-histogram-precision} - service-time = ${kamon.metrics.precision.default-histogram-precision} - } - - load-average { - one = ${kamon.metrics.precision.default-histogram-precision} - five = ${kamon.metrics.precision.default-histogram-precision} - fifteen = ${kamon.metrics.precision.default-histogram-precision} - } + metrics.instrument-settings { + system-metric { + + # + # CPU + # + cpu-user { + highest-trackable-value = 100 + } + + cpu-system = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + cpu-wait = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + cpu-idle = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + cpu-stolen = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + + + # + # Process CPU + # + process-user-cpu = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + process-system-cpu = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + process-cpu = ${kamon.metrics.instrument-settings.system-metric.cpu-user} + + + # + # Garbage Collection + # + garbage-collection-count { + highest-trackable-value = 1000000 + refresh-interval = 1 second + } + + garbage-collection-time { + highest-trackable-value = 3600000 + refresh-interval = 1 second + } + + + # + # Heap Memory + # + heap-used { + # 50 GB, which is way too much for a non-Zing JVM + highest-trackable-value = 5368709120 + refresh-interval = 1 second } - jvm { - heap { - used = ${kamon.system-metrics.default-gauge-precision} - max = ${kamon.system-metrics.default-gauge-precision} - committed = ${kamon.system-metrics.default-gauge-precision} - } - - non-heap { - used = ${kamon.system-metrics.default-gauge-precision} - max = ${kamon.system-metrics.default-gauge-precision} - committed = ${kamon.system-metrics.default-gauge-precision} - } - - thread { - daemon = ${kamon.system-metrics.default-gauge-precision} - count = ${kamon.system-metrics.default-gauge-precision} - peak = ${kamon.system-metrics.default-gauge-precision} - } - - classes { - total-loaded = ${kamon.system-metrics.default-gauge-precision} - total-unloaded = ${kamon.system-metrics.default-gauge-precision} - current-loaded = ${kamon.system-metrics.default-gauge-precision} - } - - gc { - count = ${kamon.metrics.precision.default-histogram-precision} - time = ${kamon.metrics.precision.default-histogram-precision} - } + heap-max = ${kamon.metrics.instrument-settings.system-metric.heap-used} + heap-committed = ${kamon.metrics.instrument-settings.system-metric.heap-used} + + + # + # Non-Heap Memory + # + non-heap-used { + highest-trackable-value = 5368709120 + refresh-interval = 1 second + } + non-heap-max = ${kamon.metrics.instrument-settings.system-metric.non-heap-used} + non-heap-committed = ${kamon.metrics.instrument-settings.system-metric.non-heap-used} + + + # + # JVM Threads + # + thread-count { + highest-trackable-value = 10000 + refresh-interval = 1 second + } + + daemon-thread-count = ${kamon.metrics.instrument-settings.system-metric.thread-count} + peak-thread-count = ${kamon.metrics.instrument-settings.system-metric.thread-count} + + + # + # Class Loading + # + classes-loaded { + highest-trackable-value = 10000000 + refresh-interval = 1 second } + + classes-unloaded = ${kamon.metrics.instrument-settings.system-metric.classes-loaded} + classes-currently-loaded = ${kamon.metrics.instrument-settings.system-metric.classes-loaded} + + + # + # File System + # + file-system-reads { + highest-trackable-value = 107374182400 + } + + file-system-writes = ${kamon.metrics.instrument-settings.system-metric.file-system-reads} + + + # + # Load Average + # + one-minute { + highest-trackable-value = 10000 + } + + five-minutes = ${kamon.metrics.instrument-settings.system-metric.one-minute} + fifteen-minutes = ${kamon.metrics.instrument-settings.system-metric.one-minute} + + + # + # System Memory + # + memory-used { + highest-trackable-value = 5368709120 + } + + memory-free = ${kamon.metrics.instrument-settings.system-metric.memory-used} + swap-free = ${kamon.metrics.instrument-settings.system-metric.memory-used} + swap-used = ${kamon.metrics.instrument-settings.system-metric.memory-used} + + + # + # Network + # + tx-bytes { + highest-trackable-value = 107374182400 + } + + rx-bytes = ${kamon.metrics.instrument-settings.system-metric.tx-bytes} + + tx-errors { + highest-trackable-value = 10000000 + } + + rx-errors = ${kamon.metrics.instrument-settings.system-metric.tx-errors} + tx-dropped = ${kamon.metrics.instrument-settings.system-metric.tx-errors} + rx-dropped = ${kamon.metrics.instrument-settings.system-metric.tx-errors} + + + # + # Context Switches + # + context-switches-process-voluntary { + highest-trackable-value = 10000000 + } + + context-switches-process-non-voluntary = ${kamon.metrics.instrument-settings.system-metric.context-switches-process-voluntary} + context-switches-global = ${kamon.metrics.instrument-settings.system-metric.context-switches-process-voluntary} + } } }
\ 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 deleted file mode 100644 index 20789039..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala +++ /dev/null @@ -1,88 +0,0 @@ -/* - * ========================================================================================= - * 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 object Stolen extends MetricIdentity { val name = "stolen" } - - case class CPUMetricRecorder(user: Histogram, system: Histogram, cpuWait: Histogram, idle: Histogram, stolen: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - CPUMetricSnapshot(user.collect(context), system.collect(context), cpuWait.collect(context), idle.collect(context), stolen.collect(context)) - } - - def cleanup: Unit = {} - } - - case class CPUMetricSnapshot(user: Histogram.Snapshot, system: Histogram.Snapshot, cpuWait: Histogram.Snapshot, idle: Histogram.Snapshot, stolen: 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), stolen.merge(that.stolen, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - User -> user, - System -> system, - Wait -> cpuWait, - Idle -> idle, - Stolen -> stolen) - } - - val Factory = CPUMetricGroupFactory -} - -case object CPUMetricGroupFactory extends MetricGroupFactory { - - import CPUMetrics._ - - 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") - val stolenConfig = settings.getConfig("stolen") - - new CPUMetricRecorder( - Histogram.fromConfig(userConfig), - Histogram.fromConfig(systemConfig), - Histogram.fromConfig(cpuWaitConfig), - Histogram.fromConfig(idleConfig), - Histogram.fromConfig(stolenConfig)) - } -} diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ClassLoadingMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ClassLoadingMetrics.scala deleted file mode 100644 index 1e3bee27..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/ClassLoadingMetrics.scala +++ /dev/null @@ -1,85 +0,0 @@ -/* - * ========================================================================================= - * 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 ClassLoadingMetrics(name: String) extends MetricGroupIdentity { - val category = ClassLoadingMetrics -} - -object ClassLoadingMetrics extends MetricGroupCategory { - val name = "classes" - - case object Loaded extends MetricIdentity { val name = "total-loaded" } - case object Unloaded extends MetricIdentity { val name = "total-unloaded" } - case object Current extends MetricIdentity { val name = "current-loaded" } - - case class ClassLoadingMetricRecorder(loaded: Gauge, unloaded: Gauge, current: Gauge) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - ClassLoadingMetricSnapshot(loaded.collect(context), unloaded.collect(context), current.collect(context)) - } - - def cleanup: Unit = {} - } - - case class ClassLoadingMetricSnapshot(loaded: Histogram.Snapshot, unloaded: Histogram.Snapshot, current: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = ClassLoadingMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - ClassLoadingMetricSnapshot(loaded.merge(that.loaded, context), unloaded.merge(that.unloaded, context), current.merge(that.current, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - Loaded -> loaded, - Unloaded -> unloaded, - Current -> current) - } - - val Factory = ClassLoadingMetricGroupFactory -} - -case object ClassLoadingMetricGroupFactory extends MetricGroupFactory { - - import ClassLoadingMetrics._ - - val classes = ManagementFactory.getClassLoadingMXBean - - type GroupRecorder = ClassLoadingMetricRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.jvm.classes") - - val totalLoadedConfig = settings.getConfig("total-loaded") - val totalUnloadedConfig = settings.getConfig("total-unloaded") - val currentLoadedConfig = settings.getConfig("current-loaded") - - new ClassLoadingMetricRecorder( - Gauge.fromConfig(totalLoadedConfig, system)(() ⇒ classes.getTotalLoadedClassCount), - Gauge.fromConfig(totalUnloadedConfig, system)(() ⇒ classes.getUnloadedClassCount), - Gauge.fromConfig(currentLoadedConfig, system)(() ⇒ classes.getLoadedClassCount.toLong)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ContextSwitchesMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ContextSwitchesMetrics.scala deleted file mode 100644 index 86aeabce..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/ContextSwitchesMetrics.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* - * ========================================================================================= - * 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 ContextSwitchesMetrics(name: String) extends MetricGroupIdentity { - val category = ContextSwitchesMetrics -} - -object ContextSwitchesMetrics extends MetricGroupCategory { - val name = "context-switches" - - case object PerProcessVoluntary extends MetricIdentity { val name = "per-process-voluntary" } - case object PerProcessNonVoluntary extends MetricIdentity { val name = "per-process-non-voluntary" } - case object Global extends MetricIdentity { val name = "global" } - - case class ContextSwitchesMetricsRecorder(perProcessVoluntary: Histogram, perProcessNonVoluntary: Histogram, global: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - ContextSwitchesMetricsSnapshot(perProcessVoluntary.collect(context), perProcessNonVoluntary.collect(context), global.collect(context)) - } - - def cleanup: Unit = {} - } - - case class ContextSwitchesMetricsSnapshot(perProcessVoluntary: Histogram.Snapshot, perProcessNonVoluntary: Histogram.Snapshot, global: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = ContextSwitchesMetricsSnapshot - - def merge(that: ContextSwitchesMetricsSnapshot, context: CollectionContext): GroupSnapshotType = { - ContextSwitchesMetricsSnapshot(perProcessVoluntary.merge(that.perProcessVoluntary, context), perProcessVoluntary.merge(that.perProcessVoluntary, context), global.merge(that.global, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - PerProcessVoluntary -> perProcessVoluntary, - PerProcessNonVoluntary -> perProcessNonVoluntary, - Global -> global) - } - - val Factory = ContextSwitchesMetricGroupFactory -} - -case object ContextSwitchesMetricGroupFactory extends MetricGroupFactory { - import ContextSwitchesMetrics._ - - type GroupRecorder = ContextSwitchesMetricsRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.system.context-switches") - - val perProcessVoluntary = settings.getConfig("per-process-voluntary") - val perProcessNonVoluntary = settings.getConfig("per-process-non-voluntary") - val global = settings.getConfig("global") - - new ContextSwitchesMetricsRecorder( - Histogram.fromConfig(perProcessVoluntary), - Histogram.fromConfig(perProcessNonVoluntary), - Histogram.fromConfig(global)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/DiskMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/DiskMetrics.scala deleted file mode 100644 index eeb6002b..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/DiskMetrics.scala +++ /dev/null @@ -1,85 +0,0 @@ -/* - * ========================================================================================= - * 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 DiskMetrics(name: String) extends MetricGroupIdentity { - val category = DiskMetrics -} - -object DiskMetrics extends MetricGroupCategory { - val name = "disk" - - case object Reads extends MetricIdentity { val name = "reads" } - case object Writes extends MetricIdentity { val name = "writes" } - case object Queue extends MetricIdentity { val name = "queue" } - case object ServiceTime extends MetricIdentity { val name = "service-time" } - - case class DiskMetricsRecorder(reads: Histogram, writes: Histogram, queue: Histogram, serviceTime: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - DiskMetricsSnapshot(reads.collect(context), writes.collect(context), queue.collect(context), serviceTime.collect(context)) - } - - def cleanup: Unit = {} - } - - case class DiskMetricsSnapshot(reads: Histogram.Snapshot, writes: Histogram.Snapshot, queue: Histogram.Snapshot, serviceTime: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = DiskMetricsSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - DiskMetricsSnapshot(reads.merge(that.reads, context), writes.merge(that.writes, context), queue.merge(that.queue, context), serviceTime.merge(that.serviceTime, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - Reads -> reads, - Writes -> writes, - Queue -> queue, - ServiceTime -> serviceTime) - } - - val Factory = DiskMetricGroupFactory -} - -case object DiskMetricGroupFactory extends MetricGroupFactory { - - import DiskMetrics._ - - type GroupRecorder = DiskMetricsRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.system.disk") - - val readsDiskConfig = settings.getConfig("reads") - val writesDiskConfig = settings.getConfig("writes") - val queueDiskConfig = settings.getConfig("queue") - val serviceTimeDiskConfig = settings.getConfig("service-time") - - new DiskMetricsRecorder( - Histogram.fromConfig(readsDiskConfig), - Histogram.fromConfig(writesDiskConfig), - Histogram.fromConfig(queueDiskConfig), - Histogram.fromConfig(serviceTimeDiskConfig)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala deleted file mode 100644 index 5aa679c9..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * ========================================================================================= - * 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.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: Histogram, time: Histogram) - 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) = GCMetricGroupFactory(gc) -} - -case class GCMetricGroupFactory(gc: GarbageCollectorMXBean) extends MetricGroupFactory { - import GCMetrics._ - - 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( - Histogram.fromConfig(countConfig), - Histogram.fromConfig(timeConfig)) - } -}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala deleted file mode 100644 index 5bba5bf6..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* - * ========================================================================================= - * 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" } - case object Max extends MetricIdentity { val name = "max" } - case object Committed extends MetricIdentity { val name = "committed" } - - 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 = HeapMetricGroupFactory -} - -case object HeapMetricGroupFactory extends MetricGroupFactory { - - import HeapMetrics._ - import kamon.system.SystemMetricsExtension._ - - def heap = ManagementFactory.getMemoryMXBean.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/LoadAverageMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/LoadAverageMetrics.scala deleted file mode 100644 index cd196adf..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/LoadAverageMetrics.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ========================================================================================= - * 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 LoadAverageMetrics(name: String) extends MetricGroupIdentity { - val category = LoadAverageMetrics -} - -object LoadAverageMetrics extends MetricGroupCategory { - val name = "load-average" - - case object OneMinute extends MetricIdentity { val name = "last-minute" } - case object FiveMinutes extends MetricIdentity { val name = "last-five-minutes" } - case object FifteenMinutes extends MetricIdentity { val name = "last-fifteen-minutes" } - - case class LoadAverageMetricsRecorder(one: Histogram, five: Histogram, fifteen: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - LoadAverageMetricsSnapshot(one.collect(context), five.collect(context), fifteen.collect(context)) - } - - def cleanup: Unit = {} - } - - case class LoadAverageMetricsSnapshot(one: Histogram.Snapshot, five: Histogram.Snapshot, fifteen: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = LoadAverageMetricsSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - LoadAverageMetricsSnapshot(one.merge(that.one, context), five.merge(that.five, context), fifteen.merge(that.fifteen, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - OneMinute -> one, - FiveMinutes -> five, - FifteenMinutes -> fifteen) - } - - val Factory = LoadAverageMetricGroupFactory -} - -case object LoadAverageMetricGroupFactory extends MetricGroupFactory { - - import LoadAverageMetrics._ - - type GroupRecorder = LoadAverageMetricsRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.system.load-average") - - val oneMinuteConfig = settings.getConfig("one") - val fiveMinutesConfig = settings.getConfig("five") - val fifteenMinutesConfig = settings.getConfig("fifteen") - - new LoadAverageMetricsRecorder( - Histogram.fromConfig(oneMinuteConfig), - Histogram.fromConfig(fiveMinutesConfig), - Histogram.fromConfig(fifteenMinutesConfig)) - } -} diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala deleted file mode 100644 index 14051427..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/MemoryMetrics.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* - * ========================================================================================= - * 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 = MemoryMetricGroupFactory -} - -case object MemoryMetricGroupFactory extends MetricGroupFactory { - - import MemoryMetrics._ - - 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 deleted file mode 100644 index d8a38f6d..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ========================================================================================= - * 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 object RxDropped extends MetricIdentity { val name = "rx-dropped" } - case object TxDropped extends MetricIdentity { val name = "tx-dropped" } - - case class NetworkMetricRecorder(rxBytes: Histogram, txBytes: Histogram, rxErrors: Histogram, txErrors: Histogram, rxDropped: Histogram, txDropped: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - NetworkMetricSnapshot(rxBytes.collect(context), txBytes.collect(context), rxErrors.collect(context), txErrors.collect(context), rxDropped.collect(context), txDropped.collect(context)) - } - - def cleanup: Unit = {} - } - - case class NetworkMetricSnapshot(rxBytes: Histogram.Snapshot, txBytes: Histogram.Snapshot, rxErrors: Histogram.Snapshot, txErrors: Histogram.Snapshot, rxDropped: Histogram.Snapshot, txDropped: 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), rxDropped.merge(that.rxDropped, context), txDropped.merge(that.txDropped, context)) - } - - val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - RxBytes -> rxBytes, - TxBytes -> txBytes, - RxErrors -> rxErrors, - TxErrors -> txErrors, - RxDropped -> rxDropped, - TxDropped -> txDropped) - } - - val Factory = NetworkMetricGroupFactory -} - -case object NetworkMetricGroupFactory extends MetricGroupFactory { - import NetworkMetrics._ - - 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") - val rxDroppedConfig = settings.getConfig("rx-dropped") - val txDroppedConfig = settings.getConfig("tx-dropped") - - new NetworkMetricRecorder( - Histogram.fromConfig(rxBytesConfig, Scale.Kilo), - Histogram.fromConfig(txBytesConfig, Scale.Kilo), - Histogram.fromConfig(rxErrorsConfig), - Histogram.fromConfig(txErrorsConfig), - Histogram.fromConfig(rxDroppedConfig), - Histogram.fromConfig(txDroppedConfig)) - } -}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/NonHeapMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/NonHeapMetrics.scala deleted file mode 100644 index c2b9f9af..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/NonHeapMetrics.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* - * ========================================================================================= - * 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 NonHeapMetrics(name: String) extends MetricGroupIdentity { - val category = NonHeapMetrics -} - -object NonHeapMetrics extends MetricGroupCategory { - val name = "non-heap" - - case object Used extends MetricIdentity { val name = "used" } - case object Max extends MetricIdentity { val name = "max" } - case object Committed extends MetricIdentity { val name = "committed" } - - case class NonHeapMetricRecorder(used: Gauge, max: Gauge, committed: Gauge) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - NonHeapMetricSnapshot(used.collect(context), max.collect(context), committed.collect(context)) - } - - def cleanup: Unit = {} - } - - case class NonHeapMetricSnapshot(used: Histogram.Snapshot, max: Histogram.Snapshot, committed: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = NonHeapMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - NonHeapMetricSnapshot(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 = NonHeapMetricGroupFactory -} - -case object NonHeapMetricGroupFactory extends MetricGroupFactory { - - import NonHeapMetrics._ - import kamon.system.SystemMetricsExtension._ - - def nonHeap = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage - - type GroupRecorder = NonHeapMetricRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.jvm.non-heap") - - val usedNonHeapConfig = settings.getConfig("used") - val maxNonHeapConfig = settings.getConfig("max") - val committedNonHeapConfig = settings.getConfig("committed") - - new NonHeapMetricRecorder( - Gauge.fromConfig(usedNonHeapConfig, system, Scale.Mega)(() ⇒ toMB(nonHeap.getUsed)), - Gauge.fromConfig(maxNonHeapConfig, system, Scale.Mega)(() ⇒ toMB(nonHeap.getMax)), - Gauge.fromConfig(committedNonHeapConfig, system, Scale.Mega)(() ⇒ toMB(nonHeap.getCommitted))) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala deleted file mode 100644 index ebd79d48..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ========================================================================================= - * 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 CpuPercent extends MetricIdentity { val name = "cpu-percentage" } - case object TotalProcessTime extends MetricIdentity { val name = "total-process-time" } - - case class ProcessCPUMetricsRecorder(cpuPercent: Histogram, totalProcessTime: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - ProcessCPUMetricsSnapshot(cpuPercent.collect(context), totalProcessTime.collect(context)) - } - - def cleanup: Unit = {} - } - - case class ProcessCPUMetricsSnapshot(cpuPercent: Histogram.Snapshot, totalProcessTime: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = ProcessCPUMetricsSnapshot - - def merge(that: ProcessCPUMetricsSnapshot, context: CollectionContext): GroupSnapshotType = { - ProcessCPUMetricsSnapshot(cpuPercent.merge(that.cpuPercent, context), totalProcessTime.merge(that.totalProcessTime, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - CpuPercent -> cpuPercent, - TotalProcessTime -> totalProcessTime) - } - - val Factory = ProcessCPUMetricGroupFactory -} - -case object ProcessCPUMetricGroupFactory extends MetricGroupFactory { - import ProcessCPUMetrics._ - - type GroupRecorder = ProcessCPUMetricsRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.system.process-cpu") - - val cpuPercentageConfig = settings.getConfig("cpu-percentage") - val totalProcessTimeConfig = settings.getConfig("total-process-time") - - new ProcessCPUMetricsRecorder( - Histogram.fromConfig(cpuPercentageConfig), - Histogram.fromConfig(totalProcessTimeConfig)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ThreadMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ThreadMetrics.scala deleted file mode 100644 index fc039ffa..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/ThreadMetrics.scala +++ /dev/null @@ -1,85 +0,0 @@ -/* - * ========================================================================================= - * 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 ThreadMetrics(name: String) extends MetricGroupIdentity { - val category = ThreadMetrics -} - -object ThreadMetrics extends MetricGroupCategory { - val name = "thread" - - case object Damon extends MetricIdentity { val name = "daemon-count" } - case object Count extends MetricIdentity { val name = "count" } - case object Peak extends MetricIdentity { val name = "peak-count" } - - case class ThreadMetricRecorder(daemon: Gauge, count: Gauge, peak: Gauge) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - ThreadMetricSnapshot(daemon.collect(context), count.collect(context), peak.collect(context)) - } - - def cleanup: Unit = {} - } - - case class ThreadMetricSnapshot(daemon: Histogram.Snapshot, count: Histogram.Snapshot, peak: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = ThreadMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - ThreadMetricSnapshot(daemon.merge(that.daemon, context), count.merge(that.count, context), peak.merge(that.peak, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - Damon -> daemon, - Count -> count, - Peak -> peak) - } - - val Factory = ThreadMetricGroupFactory -} - -case object ThreadMetricGroupFactory extends MetricGroupFactory { - - import ThreadMetrics._ - - def threads = ManagementFactory.getThreadMXBean - - type GroupRecorder = ThreadMetricRecorder - - def create(config: Config, system: ActorSystem): GroupRecorder = { - val settings = config.getConfig("precision.jvm.thread") - - val daemonThreadConfig = settings.getConfig("daemon") - val countThreadsConfig = settings.getConfig("count") - val peakThreadsConfig = settings.getConfig("peak") - - new ThreadMetricRecorder( - Gauge.fromConfig(daemonThreadConfig, system)(() ⇒ threads.getDaemonThreadCount.toLong), - Gauge.fromConfig(countThreadsConfig, system)(() ⇒ threads.getThreadCount.toLong), - Gauge.fromConfig(peakThreadsConfig, system)(() ⇒ threads.getPeakThreadCount.toLong)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/system/GcMetricsCollector.scala b/kamon-system-metrics/src/main/scala/kamon/system/GcMetricsCollector.scala deleted file mode 100644 index ae2f50cf..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/GcMetricsCollector.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * ========================================================================================= - * 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.GarbageCollectorMXBean - -import akka.actor.{ Actor, Props } -import kamon.metrics.GCMetrics.GCMetricRecorder - -import scala.concurrent.duration.FiniteDuration - -class GcMetricsCollector(collectInterval: FiniteDuration, recorder: Option[GCMetricRecorder], extractor: GcMetricExtractor) extends Actor { - import kamon.system.GcMetricsCollector._ - - val collectSchedule = context.system.scheduler.schedule(collectInterval, collectInterval, self, Collect)(SystemMetrics(context.system).dispatcher) - - def receive: Receive = { - case Collect ⇒ collectMetrics() - } - - override def postStop() = collectSchedule.cancel() - - def collectMetrics(): Unit = recorder.map(recordGc) - - private def recordGc(gcr: GCMetricRecorder) = { - val gcMeasure = extractor.extract() - - gcr.count.record(gcMeasure.collectionCount) - gcr.time.record(gcMeasure.collectionTime) - } -} - -object GcMetricsCollector { - case object Collect - - def props(collectInterval: FiniteDuration, recorder: Option[GCMetricRecorder], extractor: GcMetricExtractor): Props = Props(classOf[GcMetricsCollector], collectInterval, recorder, extractor) -} - -case class GcMeasure(collectionCount: Long, collectionTime: Long) - -case class GcMetricExtractor(gc: GarbageCollectorMXBean) { - var previousGcCount = 0L - var previousGcTime = 0L - - def extract(): GcMeasure = { - var diffCollectionCount = 0L - var diffCollectionTime = 0L - - val collectionCount = gc.getCollectionCount - val collectionTime = gc.getCollectionTime - - if (collectionCount > 0) - diffCollectionCount = collectionCount - previousGcCount - - if (collectionTime > 0) - diffCollectionTime = collectionTime - previousGcTime - - previousGcCount = collectionCount - previousGcTime = collectionTime - - GcMeasure(diffCollectionCount, diffCollectionTime) - } -}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala deleted file mode 100644 index cb3e2695..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * ========================================================================================= - * 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 config = system.settings.config.getConfig("kamon.system-metrics") - val dispatcher = system.dispatchers.lookup(config.getString("dispatcher")) - val sigarFolder = system.settings.config.getString("kamon.sigar.folder") - val systemMetricsExtension = Kamon(Metrics)(system) - - //System Metrics - system.actorOf(SystemMetricsCollector.props(1 second), "system-metrics-collector") - - //JVM Metrics - systemMetricsExtension.register(HeapMetrics(Heap), HeapMetrics.Factory) - systemMetricsExtension.register(NonHeapMetrics(NonHeap), NonHeapMetrics.Factory) - systemMetricsExtension.register(ClassLoadingMetrics(Classes), ClassLoadingMetrics.Factory) - systemMetricsExtension.register(ThreadMetrics(Threads), ThreadMetrics.Factory) - - garbageCollectors.map { gc ⇒ - val gcName = sanitize(gc.getName) - val recorder = systemMetricsExtension.register(GCMetrics(gcName), GCMetrics.Factory(gc)) - system.actorOf(GcMetricsCollector.props(1 second, recorder, GcMetricExtractor(gc)), s"$gcName-collector") - } -} - -object SystemMetricsExtension { - val CPU = "cpu" - val ProcessCPU = "process-cpu" - val Network = "network" - val Memory = "memory" - val Heap = "heap" - val NonHeap = "non-heap" - val Classes = "classes" - val Threads = "thread" - val ContextSwitches = "context-switches" - val Disk = "disk" - val LoadAverage = "load-average" - - def toKB(value: Long): Long = value / 1024 - def toMB(value: Long): Long = value / 1024 / 1024 - def toLong(value: Double): Long = math round (value * 100L) - def sanitize(str: String): String = str.replaceAll("""[^\w]""", "-") - - val garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) -} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsBanner.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsBanner.scala deleted file mode 100644 index 99e09da9..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsBanner.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ========================================================================================= - * 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.ActorLogging -import org.hyperic.sigar._ - -import scala.util.control.NoStackTrace - -trait SystemMetricsBanner { - self: ActorLogging ⇒ - - def printBanner(sigar: Sigar) = { - val os = OperatingSystem.getInstance - - def loadAverage(sigar: Sigar) = try { - val average = sigar.getLoadAverage - (average(0), average(1), average(2)) - } catch { - case s: org.hyperic.sigar.SigarNotImplementedException ⇒ (0d, 0d, 0d) - } - - def uptime(sigar: Sigar) = { - def formatUptime(uptime: Double): String = { - var retval: String = "" - val days: Int = uptime.toInt / (60 * 60 * 24) - var minutes: Int = 0 - var hours: Int = 0 - - if (days != 0) { - retval += s"$days ${if (days > 1) "days" else "day"}, " - } - - minutes = uptime.toInt / 60 - hours = minutes / 60 - hours %= 24 - minutes %= 60 - - if (hours != 0) { - retval += hours + ":" + minutes - } else { - retval += minutes + " min" - } - retval - } - - val uptime = sigar.getUptime - val now = System.currentTimeMillis() - - s"up ${formatUptime(uptime.getUptime)}" - } - - val message = - """ - | - | _____ _ __ __ _ _ _ _ _ - | / ____| | | | \/ | | | (_) | | | | | | - || (___ _ _ ___| |_ ___ _ __ ___ | \ / | ___| |_ _ __ _ ___ ___| | ___ __ _ __| | ___ __| | - | \___ \| | | / __| __/ _ \ '_ ` _ \| |\/| |/ _ \ __| '__| |/ __/ __| | / _ \ / _` |/ _` |/ _ \/ _` | - | ____) | |_| \__ \ || __/ | | | | | | | | __/ |_| | | | (__\__ \ |___| (_) | (_| | (_| | __/ (_| | - ||_____/ \__, |___/\__\___|_| |_| |_|_| |_|\___|\__|_| |_|\___|___/______\___/ \__,_|\__,_|\___|\__,_| - | __/ | - | |___/ - | - | [System Status] [OS Information] - | |--------------------------------| |----------------------------------------| - | Up Time: %-10s Description: %s - | Load Average: %-16s Name: %s - | Version: %s - | Arch: %s - | - """.stripMargin.format(uptime(sigar), os.getDescription, loadAverage(sigar), os.getName, os.getVersion, os.getArch) - log.info(message) - } - - class UnexpectedSigarException(message: String) extends RuntimeException(message) with NoStackTrace -} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala deleted file mode 100644 index 4391240a..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala +++ /dev/null @@ -1,266 +0,0 @@ -/* - * ========================================================================================= - * 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.io.{ File, IOException } - -import akka.actor.{ Actor, ActorLogging, Props } -import kamon.Kamon -import kamon.metric.Metrics -import kamon.metrics.CPUMetrics.CPUMetricRecorder -import kamon.metrics.ContextSwitchesMetrics.ContextSwitchesMetricsRecorder -import kamon.metrics.DiskMetrics.DiskMetricsRecorder -import kamon.metrics.LoadAverageMetrics.LoadAverageMetricsRecorder -import kamon.metrics.MemoryMetrics.MemoryMetricRecorder -import kamon.metrics.NetworkMetrics.NetworkMetricRecorder -import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsRecorder -import kamon.metrics._ -import kamon.sigar.SigarProvisioner -import org.hyperic.sigar._ - -import scala.collection.concurrent.TrieMap -import scala.concurrent.duration.FiniteDuration -import scala.io.Source -import scala.collection.mutable - -class SystemMetricsCollector(collectInterval: FiniteDuration) extends Actor with ActorLogging with SystemMetricsBanner { - import kamon.system.SystemMetricsCollector._ - import kamon.system.SystemMetricsExtension._ - - lazy val sigar = createSigarInstance - def pid = sigar.getPid - - val interfaces = sigar.getNetInterfaceList.filterNot(NetworkFilter).toSet - val fileSystems = sigar.getFileSystemList.filter(_.getType == FileSystem.TYPE_LOCAL_DISK).map(_.getDevName).toSet - - val metricExtension = Kamon(Metrics)(context.system) - val collectSchedule = context.system.scheduler.schedule(collectInterval, collectInterval, self, Collect)(SystemMetrics(context.system).dispatcher) - - val cpuRecorder = metricExtension.register(CPUMetrics(CPU), CPUMetrics.Factory) - val processCpuRecorder = metricExtension.register(ProcessCPUMetrics(ProcessCPU), ProcessCPUMetrics.Factory) - val memoryRecorder = metricExtension.register(MemoryMetrics(Memory), MemoryMetrics.Factory) - val networkRecorder = metricExtension.register(NetworkMetrics(Network), NetworkMetrics.Factory) - val contextSwitchesRecorder = metricExtension.register(ContextSwitchesMetrics(ContextSwitches), ContextSwitchesMetrics.Factory) - val diskRecorder = metricExtension.register(DiskMetrics(Disk), DiskMetrics.Factory) - val loadAverageRecorder = metricExtension.register(LoadAverageMetrics(LoadAverage), LoadAverageMetrics.Factory) - - def receive: Receive = { - case Collect ⇒ collectMetrics() - } - - override def postStop() = collectSchedule.cancel() - - def collectMetrics() = { - cpuRecorder.map(recordCpu) - processCpuRecorder.map(recordProcessCpu) - memoryRecorder.map(recordMemory) - networkRecorder.map(recordNetwork) - diskRecorder.map(recordDisk) - loadAverageRecorder.map(recordLoadAverage) - - if (OsUtils.isLinux) - contextSwitchesRecorder.map(recordContextSwitches) - } - - private def recordCpu(cpur: CPUMetricRecorder) = { - val cpuPerc = sigar.getCpuPerc - cpur.user.record(toLong(cpuPerc.getUser)) - cpur.system.record(toLong(cpuPerc.getSys)) - cpur.cpuWait.record(toLong(cpuPerc.getWait)) - cpur.idle.record(toLong(cpuPerc.getIdle)) - cpur.stolen.record(toLong(cpuPerc.getStolen)) - } - - private def recordProcessCpu(pcpur: ProcessCPUMetricsRecorder) = { - val procCpu = sigar.getProcCpu(pid) - val procTime = sigar.getProcTime(pid) - - pcpur.cpuPercent.record(toLong(procCpu.getPercent)) - pcpur.totalProcessTime.record(procTime.getTotal) // gives an idea of what is really measured and then interpreted as % - } - - private def recordMemory(mr: MemoryMetricRecorder) = { - val mem = sigar.getMem - val swap = sigar.getSwap - - 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) = { - import Networks._ - nr.rxBytes.record(collect(sigar, interfaces, RxBytes, previousNetworkMetrics)(net ⇒ toKB(net.getRxBytes))) - nr.txBytes.record(collect(sigar, interfaces, TxBytes, previousNetworkMetrics)(net ⇒ toKB(net.getTxBytes))) - nr.rxErrors.record(collect(sigar, interfaces, RxErrors, previousNetworkMetrics)(net ⇒ net.getRxErrors)) - nr.txErrors.record(collect(sigar, interfaces, TxErrors, previousNetworkMetrics)(net ⇒ net.getTxErrors)) - nr.rxDropped.record(collect(sigar, interfaces, RxDropped, previousNetworkMetrics)(net ⇒ net.getRxDropped)) - nr.txDropped.record(collect(sigar, interfaces, TxDropped, previousNetworkMetrics)(net ⇒ net.getTxDropped)) - - def collect(sigar: SigarProxy, interfaces: Set[String], name: String, previousMetrics: TrieMap[String, mutable.Map[String, Long]])(thunk: NetInterfaceStat ⇒ Long): Long = { - interfaces.foldLeft(0L) { (acc, interface) ⇒ - { - val net = sigar.getNetInterfaceStat(interface) - val previous = previousMetrics.getOrElse(interface, mutable.Map.empty[String, Long]) - val current = thunk(net) - val delta = current - previous.getOrElse(name, 0L) - previousMetrics.put(interface, previous += name -> current) - acc + delta - } - } - } - } - - private def recordDisk(rd: DiskMetricsRecorder) = { - import Disks._ - - rd.reads.record(collect(sigar, fileSystems, Reads, previousDiskMetrics)(disk ⇒ disk.getReads)) - rd.writes.record(collect(sigar, fileSystems, Writes, previousDiskMetrics)(disk ⇒ disk.getWrites)) - rd.queue.record(collect(sigar, fileSystems, Queue, previousDiskMetrics)(disk ⇒ toLong(disk.getQueue))) - rd.serviceTime.record(collect(sigar, fileSystems, Service, previousDiskMetrics)(disk ⇒ toLong(disk.getServiceTime))) - } - - def collect(sigar: SigarProxy, fileSystems: Set[String], name: String, previousMetrics: TrieMap[String, mutable.Map[String, Long]])(thunk: DiskUsage ⇒ Long): Long = { - fileSystems.foldLeft(0L) { (acc, fileSystem) ⇒ - { - val disk = sigar.getDiskUsage(fileSystem) - val previous = previousMetrics.getOrElse(fileSystem, mutable.Map.empty[String, Long]) - val value = thunk(disk) - val current = if (value == Sigar.FIELD_NOTIMPL) 0L else value - val delta = current - previous.getOrElse(name, 0L) - previousMetrics.put(fileSystem, previous += name -> current) - acc + delta - } - } - } - - private def recordLoadAverage(lar: LoadAverageMetricsRecorder) = { - val loadAverage = sigar.getLoadAverage - val (one, five, fifteen) = (loadAverage(0), loadAverage(1), loadAverage(2)) - - lar.one.record(toLong(one)) - lar.five.record(toLong(five)) - lar.fifteen.record(toLong(fifteen)) - } - - private def recordContextSwitches(rcs: ContextSwitchesMetricsRecorder) = { - def contextSwitchesByProcess(pid: Long): (Long, Long) = { - val filename = s"/proc/$pid/status" - var voluntaryContextSwitches = 0L - var nonVoluntaryContextSwitches = 0L - - try { - for (line ← Source.fromFile(filename).getLines()) { - if (line.startsWith("voluntary_ctxt_switches")) { - voluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong - } - if (line.startsWith("nonvoluntary_ctxt_switches")) { - nonVoluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong - } - } - } catch { - case ex: IOException ⇒ log.error("Error trying to read [{}]", filename) - } - (voluntaryContextSwitches, nonVoluntaryContextSwitches) - } - - def contextSwitches: Long = { - val filename = "/proc/stat" - var contextSwitches = 0L - - try { - for (line ← Source.fromFile(filename).getLines()) { - if (line.startsWith("rcs")) { - contextSwitches = line.substring(line.indexOf(" ") + 1).toLong - } - } - } catch { - case ex: IOException ⇒ log.error("Error trying to read [{}]", filename) - } - contextSwitches - } - - val (perProcessVoluntary, perProcessNonVoluntary) = contextSwitchesByProcess(pid) - rcs.perProcessVoluntary.record(perProcessVoluntary) - rcs.perProcessNonVoluntary.record(perProcessNonVoluntary) - rcs.global.record(contextSwitches) - } - - def verifiedSigarInstance: SigarProxy = { - val sigar = new Sigar() - printBanner(sigar) - sigar - } - - def provisionSigarLibrary: Unit = { - val folder = SystemMetrics(context.system).sigarFolder - SigarProvisioner.provision(new File(folder)) - } - - def createSigarInstance: SigarProxy = { - // 1) Assume that library is already provisioned. - try { - return verifiedSigarInstance - } catch { - // Not using [[Try]] - any error is non-fatal in this case. - case e: Throwable ⇒ log.info(s"Sigar is not yet provisioned: ${e}") - } - - // 2) Attempt to provision library via sigar-loader. - try { - provisionSigarLibrary - return verifiedSigarInstance - } catch { - // Not using [[Try]] - any error is non-fatal in this case. - case e: Throwable ⇒ throw new UnexpectedSigarException(s"Failed to load Sigar: ${e}") - } - } -} - -object SystemMetricsCollector { - val NetworkFilter = Set("lo") - val previousDiskMetrics = TrieMap[String, mutable.Map[String, Long]]() - val previousNetworkMetrics = TrieMap[String, mutable.Map[String, Long]]() - - object Networks { - val RxBytes = "rxBytes" - val TxBytes = "txBytes" - val RxErrors = "rxErrors" - val TxErrors = "txErrors" - val RxDropped = "rxDropped" - val TxDropped = "txDropped" - } - - object Disks { - val Reads = "reads" - val Writes = "writes" - val Queue = "queue" - val Service = "service" - } - case object Collect - - object OsUtils { - def isLinux: Boolean = System.getProperty("os.name").indexOf("Linux") != -1 - } - - def props(collectInterval: FiniteDuration): Props = Props(classOf[SystemMetricsCollector], collectInterval) -}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala new file mode 100644 index 00000000..df120611 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala @@ -0,0 +1,70 @@ +/* + * ========================================================================================= + * 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.io.File +import akka.actor._ +import akka.event.Logging +import kamon.system.custom.{ ContextSwitchesUpdater, ContextSwitchesMetrics } +import kamon.system.jmx._ +import kamon.{ ModuleSupervisor, Kamon } +import kamon.metric._ +import kamon.sigar.SigarProvisioner +import kamon.system.sigar.SigarMetricsUpdater + +import kamon.util.ConfigTools.Syntax + +object SystemMetrics extends ExtensionId[SystemMetricsExtension] with ExtensionIdProvider { + override def lookup(): ExtensionId[_ <: Extension] = SystemMetrics + override def createExtension(system: ExtendedActorSystem): SystemMetricsExtension = new SystemMetricsExtension(system) +} + +class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extension { + + val log = Logging(system, classOf[SystemMetricsExtension]) + log.info(s"Starting the Kamon(SystemMetrics) extension") + + val config = system.settings.config.getConfig("kamon.system-metrics") + val sigarFolder = config.getString("sigar-native-folder") + val sigarRefreshInterval = config.getFiniteDuration("sigar-metrics-refresh-interval") + val contextSwitchesRefreshInterval = config.getFiniteDuration("context-switches-refresh-interval") + val metricsExtension = Kamon(Metrics)(system) + + // Sigar-based metrics + SigarProvisioner.provision(new File(sigarFolder)) + val sigarMetricsRecorder = ModuleSupervisor.get(system).createModule("sigar-metrics-recorder", + SigarMetricsUpdater.props(sigarRefreshInterval).withDispatcher("kamon.system-metrics.sigar-dispatcher")) + + // JMX Metrics + ClassLoadingMetrics.register(metricsExtension) + GarbageCollectionMetrics.register(metricsExtension) + HeapMemoryMetrics.register(metricsExtension) + NonHeapMemoryMetrics.register(metricsExtension) + ThreadsMetrics.register(metricsExtension) + + // If we are in Linux, add ContextSwitchesMetrics as well. + if (isLinux) { + val contextSwitchesRecorder = ContextSwitchesMetrics.register(system, contextSwitchesRefreshInterval) + + ModuleSupervisor.get(system).createModule("context-switches-metrics-recorder", + ContextSwitchesUpdater.props(contextSwitchesRecorder, sigarRefreshInterval) + .withDispatcher("kamon.system-metrics.context-switches-dispatcher")) + } + + def isLinux: Boolean = + System.getProperty("os.name").indexOf("Linux") != -1 + +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala new file mode 100644 index 00000000..a3c56733 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala @@ -0,0 +1,96 @@ +package kamon.system.custom + +import java.io.IOException +import java.nio.charset.StandardCharsets +import java.nio.file.{ Paths, Files } + +import akka.actor.{ Props, Actor, ActorSystem } +import akka.event.{ Logging, LoggingAdapter } +import kamon.Kamon +import kamon.metric._ +import kamon.metric.instrument.InstrumentFactory +import kamon.system.custom.ContextSwitchesUpdater.UpdateContextSwitches +import org.hyperic.sigar.Sigar +import scala.collection.JavaConverters.iterableAsScalaIterableConverter +import scala.concurrent.duration.FiniteDuration + +class ContextSwitchesMetrics(pid: Long, log: LoggingAdapter, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + val perProcessVoluntary = histogram("context-switches-process-voluntary") + val perProcessNonVoluntary = histogram("context-switches-process-non-voluntary") + val global = histogram("context-switches-global") + + def update(): Unit = { + def contextSwitchesByProcess(pid: Long): (Long, Long) = { + val filename = s"/proc/$pid/status" + var voluntaryContextSwitches = 0L + var nonVoluntaryContextSwitches = 0L + + try { + for (line ← Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.toList) { + if (line.startsWith("voluntary_ctxt_switches")) { + voluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong + } + if (line.startsWith("nonvoluntary_ctxt_switches")) { + nonVoluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong + } + } + } catch { + case ex: IOException ⇒ log.error("Error trying to read [{}]", filename) + } + (voluntaryContextSwitches, nonVoluntaryContextSwitches) + } + + def contextSwitches: Long = { + val filename = "/proc/stat" + var contextSwitches = 0L + + try { + for (line ← Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.toList) { + if (line.startsWith("rcs")) { + contextSwitches = line.substring(line.indexOf(" ") + 1).toLong + } + } + } catch { + case ex: IOException ⇒ log.error("Error trying to read [{}]", filename) + } + contextSwitches + } + + val (voluntary, nonVoluntary) = contextSwitchesByProcess(pid) + perProcessVoluntary.record(voluntary) + perProcessNonVoluntary.record(nonVoluntary) + global.record(contextSwitches) + } +} + +object ContextSwitchesMetrics { + + def register(system: ActorSystem, refreshInterval: FiniteDuration): ContextSwitchesMetrics = { + val metricsExtension = Kamon(Metrics)(system) + val log = Logging(system, "ContextSwitchesMetrics") + val pid = (new Sigar).getPid + + val instrumentFactory = metricsExtension.instrumentFactory("system-metric") + metricsExtension.register(Entity("context-switches", "system-metric"), new ContextSwitchesMetrics(pid, log, instrumentFactory)).recorder + } +} + +class ContextSwitchesUpdater(csm: ContextSwitchesMetrics, refreshInterval: FiniteDuration) extends Actor { + val schedule = context.system.scheduler.schedule(refreshInterval, refreshInterval, self, UpdateContextSwitches)(context.dispatcher) + + def receive = { + case UpdateContextSwitches ⇒ csm.update() + } + + override def postStop(): Unit = { + schedule.cancel() + super.postStop() + } +} + +object ContextSwitchesUpdater { + case object UpdateContextSwitches + + def props(csm: ContextSwitchesMetrics, refreshInterval: FiniteDuration): Props = + Props(new ContextSwitchesUpdater(csm, refreshInterval)) +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/ClassLoadingMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ClassLoadingMetrics.scala new file mode 100644 index 00000000..d9379738 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ClassLoadingMetrics.scala @@ -0,0 +1,28 @@ +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +class ClassLoadingMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + val classLoadingBean = ManagementFactory.getClassLoadingMXBean + + gauge("classes-loaded", Memory.Bytes, () ⇒ { + classLoadingBean.getTotalLoadedClassCount + }) + + gauge("classes-unloaded", Memory.Bytes, () ⇒ { + classLoadingBean.getUnloadedClassCount + }) + + gauge("classes-currently-loaded", Memory.Bytes, () ⇒ { + classLoadingBean.getLoadedClassCount.toLong + }) + +} + +object ClassLoadingMetrics extends JmxSystemMetricRecorderCompanion("class-loading") { + def apply(instrumentFactory: InstrumentFactory): ClassLoadingMetrics = + new ClassLoadingMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala new file mode 100644 index 00000000..b7d2fe6a --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala @@ -0,0 +1,34 @@ +package kamon.system.jmx + +import java.lang.management.{ GarbageCollectorMXBean, ManagementFactory } + +import kamon.metric.{ Entity, MetricsExtension, GenericEntityRecorder } +import kamon.metric.instrument.{ DifferentialValueCollector, Time, InstrumentFactory } +import scala.collection.JavaConverters._ + +class GarbageCollectionMetrics(gc: GarbageCollectorMXBean, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + + gauge("garbage-collection-count", DifferentialValueCollector(() ⇒ { + gc.getCollectionCount + })) + + gauge("garbage-collection-time", Time.Milliseconds, DifferentialValueCollector(() ⇒ { + gc.getCollectionTime + })) + +} + +object GarbageCollectionMetrics { + + def sanitizeCollectorName(name: String): String = + name.replaceAll("""[^\w]""", "-").toLowerCase + + def register(metricsExtension: MetricsExtension): Unit = { + + val instrumentFactory = metricsExtension.instrumentFactory("system-metric") + ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) map { gc ⇒ + val gcName = sanitizeCollectorName(gc.getName) + metricsExtension.register(Entity(s"$gcName-garbage-collector", "system-metric"), new GarbageCollectionMetrics(gc, instrumentFactory)) + } + } +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/HeapMemoryMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/HeapMemoryMetrics.scala new file mode 100644 index 00000000..a96b5319 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/HeapMemoryMetrics.scala @@ -0,0 +1,29 @@ +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +class HeapMemoryMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + val memoryBean = ManagementFactory.getMemoryMXBean + def nonHeapUsage = memoryBean.getHeapMemoryUsage + + gauge("heap-used", Memory.Bytes, () ⇒ { + nonHeapUsage.getUsed + }) + + gauge("heap-max", Memory.Bytes, () ⇒ { + nonHeapUsage.getMax + }) + + gauge("heap-committed", Memory.Bytes, () ⇒ { + nonHeapUsage.getCommitted + }) + +} + +object HeapMemoryMetrics extends JmxSystemMetricRecorderCompanion("heap-memory") { + def apply(instrumentFactory: InstrumentFactory): HeapMemoryMetrics = + new HeapMemoryMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala new file mode 100644 index 00000000..d19622e6 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala @@ -0,0 +1,13 @@ +package kamon.system.jmx + +import kamon.metric.instrument.InstrumentFactory +import kamon.metric.{ Entity, EntityRecorder, MetricsExtension } + +abstract class JmxSystemMetricRecorderCompanion(metricName: String) { + def register(metricsExtension: MetricsExtension): EntityRecorder = { + val instrumentFactory = metricsExtension.instrumentFactory("system-metric") + metricsExtension.register(Entity(metricName, "system-metric"), apply(instrumentFactory)).recorder + } + + def apply(instrumentFactory: InstrumentFactory): EntityRecorder +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/NonHeapMemoryMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/NonHeapMemoryMetrics.scala new file mode 100644 index 00000000..34a23d4f --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/NonHeapMemoryMetrics.scala @@ -0,0 +1,33 @@ +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +class NonHeapMemoryMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + val memoryBean = ManagementFactory.getMemoryMXBean + def nonHeapUsage = memoryBean.getNonHeapMemoryUsage + + gauge("non-heap-used", Memory.Bytes, () ⇒ { + nonHeapUsage.getUsed + }) + + gauge("non-heap-max", Memory.Bytes, () ⇒ { + val max = nonHeapUsage.getMax + + // .getMax can return -1 if the max is not defined. + if (max >= 0) max + else 0 + }) + + gauge("non-heap-committed", Memory.Bytes, () ⇒ { + nonHeapUsage.getCommitted + }) + +} + +object NonHeapMemoryMetrics extends JmxSystemMetricRecorderCompanion("non-heap-memory") { + def apply(instrumentFactory: InstrumentFactory): NonHeapMemoryMetrics = + new NonHeapMemoryMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/ThreadsMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ThreadsMetrics.scala new file mode 100644 index 00000000..b33eb3e6 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ThreadsMetrics.scala @@ -0,0 +1,28 @@ +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory + +class ThreadsMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) { + val threadsBean = ManagementFactory.getThreadMXBean + + gauge("daemon-thread-count", () ⇒ { + threadsBean.getDaemonThreadCount.toLong + }) + + gauge("peak-thread-count", () ⇒ { + threadsBean.getPeakThreadCount.toLong + }) + + gauge("thread-count", () ⇒ { + threadsBean.getThreadCount.toLong + }) + +} + +object ThreadsMetrics extends JmxSystemMetricRecorderCompanion("threads") { + def apply(instrumentFactory: InstrumentFactory): ThreadsMetrics = + new ThreadsMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/CpuMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/CpuMetrics.scala new file mode 100644 index 00000000..0a5f6494 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/CpuMetrics.scala @@ -0,0 +1,29 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.Sigar + +class CpuMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val user = histogram("cpu-user") + val system = histogram("cpu-system") + val Wait = histogram("cpu-wait") + val idle = histogram("cpu-idle") + val stolen = histogram("cpu-stolen") + + def update(sigar: Sigar): Unit = { + val cpuPerc = sigar.getCpuPerc + + user.record((cpuPerc.getUser * 100L).toLong) + system.record((cpuPerc.getSys * 100L).toLong) + Wait.record((cpuPerc.getWait * 100L).toLong) + idle.record((cpuPerc.getIdle * 100L).toLong) + stolen.record((cpuPerc.getStolen * 100L).toLong) + } +} + +object CpuMetrics extends SigarMetricRecorderCompanion("cpu") { + + def apply(instrumentFactory: InstrumentFactory): CpuMetrics = + new CpuMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/DiffRecordingHistogram.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/DiffRecordingHistogram.scala new file mode 100644 index 00000000..94aa76d1 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/DiffRecordingHistogram.scala @@ -0,0 +1,41 @@ +package kamon.system.sigar + +import java.util.concurrent.atomic.AtomicLong + +import kamon.metric.instrument.{ CollectionContext, Histogram } + +/** + * Wrapper Histogram for cases in which the recorded values should always be the difference + * between the current value and the last recorded value. This is not thread-safe and only + * to be used with Sigar-based metrics that are securely updated within an actor. + */ +class DiffRecordingHistogram(wrappedHistogram: Histogram) extends Histogram { + @volatile private var _recordedAtLeastOnce = false + private val _lastObservedValue = new AtomicLong(0) + + private def processRecording(value: Long, count: Long): Unit = { + if (_recordedAtLeastOnce) + wrappedHistogram.record(value - _lastObservedValue.getAndSet(value), count) + else { + _lastObservedValue.set(value) + _recordedAtLeastOnce = true + } + } + + def record(value: Long): Unit = + processRecording(value, 1) + + def record(value: Long, count: Long): Unit = + processRecording(value, count) + + def cleanup: Unit = + wrappedHistogram.cleanup + + def collect(context: CollectionContext): Histogram.Snapshot = + wrappedHistogram.collect(context) +} + +object DiffRecordingHistogram { + def apply(histogram: Histogram): DiffRecordingHistogram = + new DiffRecordingHistogram(histogram) +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/FileSystemMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/FileSystemMetrics.scala new file mode 100644 index 00000000..dffebf5a --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/FileSystemMetrics.scala @@ -0,0 +1,25 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } +import org.hyperic.sigar.{ DiskUsage, FileSystem, Sigar } + +class FileSystemMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val reads = DiffRecordingHistogram(histogram("file-system-reads", Memory.Bytes)) + val writes = DiffRecordingHistogram(histogram("file-system-writes", Memory.Bytes)) + + def sumOfAllFileSystems(sigar: Sigar, thunk: DiskUsage ⇒ Long): Long = { + val fileSystems = sigar.getFileSystemList.filter(_.getType == FileSystem.TYPE_LOCAL_DISK).map(_.getDevName).toSet + fileSystems.map(i ⇒ thunk(sigar.getDiskUsage(i))).fold(0L)(_ + _) + } + + def update(sigar: Sigar): Unit = { + reads.record(sumOfAllFileSystems(sigar, _.getReadBytes)) + writes.record(sumOfAllFileSystems(sigar, _.getWriteBytes)) + } +} + +object FileSystemMetrics extends SigarMetricRecorderCompanion("file-system") { + def apply(instrumentFactory: InstrumentFactory): FileSystemMetrics = + new FileSystemMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/LoadAverageMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/LoadAverageMetrics.scala new file mode 100644 index 00000000..3e02cc8f --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/LoadAverageMetrics.scala @@ -0,0 +1,25 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.Sigar + +class LoadAverageMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val oneMinute = histogram("one-minute") + val fiveMinutes = histogram("five-minutes") + val fifteenMinutes = histogram("fifteen-minutes") + + def update(sigar: Sigar): Unit = { + val loadAverage = sigar.getLoadAverage + + oneMinute.record(loadAverage(0).toLong) + fiveMinutes.record(loadAverage(1).toLong) + fifteenMinutes.record(loadAverage(2).toLong) + } +} + +object LoadAverageMetrics extends SigarMetricRecorderCompanion("load-average") { + + def apply(instrumentFactory: InstrumentFactory): LoadAverageMetrics = + new LoadAverageMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/MemoryMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/MemoryMetrics.scala new file mode 100644 index 00000000..ab7fcd88 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/MemoryMetrics.scala @@ -0,0 +1,36 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } +import org.hyperic.sigar.Sigar + +/** + * System memory usage metrics, as reported by Sigar: + * - used: Total used system memory. + * - free: Total free system memory (e.g. Linux plus cached). + * - swap-used: Total used system swap.. + * - swap-free: Total free system swap. + */ +class MemoryMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val used = histogram("memory-used", Memory.Bytes) + val free = histogram("memory-free", Memory.Bytes) + val swapUsed = histogram("swap-used", Memory.Bytes) + val swapFree = histogram("swap-free", Memory.Bytes) + + def update(sigar: Sigar): Unit = { + val mem = sigar.getMem + val swap = sigar.getSwap + + used.record(mem.getUsed) + free.record(mem.getFree) + swapUsed.record(swap.getUsed) + swapFree.record(swap.getFree) + } +} + +object MemoryMetrics extends SigarMetricRecorderCompanion("memory") { + + def apply(instrumentFactory: InstrumentFactory): MemoryMetrics = + new MemoryMetrics(instrumentFactory) +} + diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/NetworkMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/NetworkMetrics.scala new file mode 100644 index 00000000..fb33b7e4 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/NetworkMetrics.scala @@ -0,0 +1,33 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument._ +import org.hyperic.sigar.{ NetInterfaceStat, Sigar } + +class NetworkMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val receivedBytes = DiffRecordingHistogram(histogram("rx-bytes", Memory.Bytes)) + val transmittedBytes = DiffRecordingHistogram(histogram("tx-bytes", Memory.Bytes)) + val receiveErrors = DiffRecordingHistogram(histogram("rx-errors")) + val transmitErrors = DiffRecordingHistogram(histogram("tx-errors")) + val receiveDrops = DiffRecordingHistogram(histogram("rx-dropped")) + val transmitDrops = DiffRecordingHistogram(histogram("tx-dropped")) + + def sumOfAllInterfaces(sigar: Sigar, thunk: NetInterfaceStat ⇒ Long): Long = { + val interfaces = sigar.getNetInterfaceList.toList.filter(_ != "lo") + interfaces.map(i ⇒ thunk(sigar.getNetInterfaceStat(i))).fold(0L)(_ + _) + } + + def update(sigar: Sigar): Unit = { + receivedBytes.record(sumOfAllInterfaces(sigar, _.getRxBytes)) + transmittedBytes.record(sumOfAllInterfaces(sigar, _.getTxBytes)) + receiveErrors.record(sumOfAllInterfaces(sigar, _.getRxErrors)) + transmitErrors.record(sumOfAllInterfaces(sigar, _.getTxErrors)) + receiveDrops.record(sumOfAllInterfaces(sigar, _.getRxDropped)) + transmitDrops.record(sumOfAllInterfaces(sigar, _.getTxDropped)) + } +} + +object NetworkMetrics extends SigarMetricRecorderCompanion("network") { + def apply(instrumentFactory: InstrumentFactory): NetworkMetrics = + new NetworkMetrics(instrumentFactory) +}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/ProcessCpuMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/ProcessCpuMetrics.scala new file mode 100644 index 00000000..0ca5c1c8 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/ProcessCpuMetrics.scala @@ -0,0 +1,39 @@ +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.{ ProcCpu, Sigar } + +class ProcessCpuMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val processUserCpu = histogram("process-user-cpu") + val processSystemCpu = histogram("process-system-cpu") + val processTotalCpu = histogram("process-cpu") + + var lastProcCpu: Option[ProcCpu] = None + + def update(sigar: Sigar): Unit = { + val pid = sigar.getPid + val procCpu = sigar.getProcCpu(pid) + + lastProcCpu.map { last ⇒ + val timeDiff = procCpu.getLastTime - last.getLastTime + if (timeDiff > 0) { + val userPercent = (((procCpu.getUser - last.getUser) / timeDiff.toDouble) * 100).toLong + val systemPercent = (((procCpu.getSys - last.getSys) / timeDiff.toDouble) * 100).toLong + + processUserCpu.record(userPercent) + processSystemCpu.record(systemPercent) + processTotalCpu.record(userPercent + systemPercent) + } + } + + lastProcCpu = Some(procCpu) + + } +} + +object ProcessCpuMetrics extends SigarMetricRecorderCompanion("process-cpu") { + + def apply(instrumentFactory: InstrumentFactory): ProcessCpuMetrics = + new ProcessCpuMetrics(instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala new file mode 100644 index 00000000..8a430427 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala @@ -0,0 +1,59 @@ +package kamon.system.sigar + +import akka.actor.{ Props, Actor } +import kamon.Kamon +import kamon.metric.instrument.InstrumentFactory +import kamon.metric.{ Entity, EntityRecorder, MetricsExtension, Metrics } +import kamon.system.sigar.SigarMetricsUpdater.UpdateSigarMetrics +import org.hyperic.sigar.Sigar + +import scala.concurrent.duration.FiniteDuration + +class SigarMetricsUpdater(refreshInterval: FiniteDuration) extends Actor { + val sigar = new Sigar + val metricsExtension = Kamon(Metrics)(context.system) + + val sigarMetrics = List( + CpuMetrics.register(metricsExtension), + FileSystemMetrics.register(metricsExtension), + LoadAverageMetrics.register(metricsExtension), + MemoryMetrics.register(metricsExtension), + NetworkMetrics.register(metricsExtension), + ProcessCpuMetrics.register(metricsExtension)) + + val refreshSchedule = context.system.scheduler.schedule(refreshInterval, refreshInterval, self, UpdateSigarMetrics)(context.dispatcher) + + def receive = { + case UpdateSigarMetrics ⇒ updateMetrics() + } + + def updateMetrics(): Unit = { + sigarMetrics.foreach(_.update(sigar)) + } + + override def postStop(): Unit = { + refreshSchedule.cancel() + super.postStop() + } +} + +object SigarMetricsUpdater { + def props(refreshInterval: FiniteDuration): Props = + Props(new SigarMetricsUpdater((refreshInterval))) + + case object UpdateSigarMetrics +} + +trait SigarMetric extends EntityRecorder { + def update(sigar: Sigar): Unit +} + +abstract class SigarMetricRecorderCompanion(metricName: String) { + def register(metricsExtension: MetricsExtension): SigarMetric = { + val instrumentFactory = metricsExtension.instrumentFactory("system-metric") + metricsExtension.register(Entity(metricName, "system-metric"), apply(instrumentFactory)).recorder + } + + def apply(instrumentFactory: InstrumentFactory): SigarMetric +} + diff --git a/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala b/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala index c9530160..4d633952 100644 --- a/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala +++ b/kamon-system-metrics/src/test/scala/kamon/metrics/SystemMetricsSpec.scala @@ -15,406 +15,140 @@ 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.ClassLoadingMetrics.ClassLoadingMetricSnapshot -import kamon.metrics.ContextSwitchesMetrics.ContextSwitchesMetricsSnapshot -import kamon.metrics.DiskMetrics.DiskMetricsSnapshot -import kamon.metrics.GCMetrics.GCMetricSnapshot -import kamon.metrics.HeapMetrics.HeapMetricSnapshot -import kamon.metrics.LoadAverageMetrics.LoadAverageMetricsSnapshot -import kamon.metrics.MemoryMetrics.MemoryMetricSnapshot -import kamon.metrics.NetworkMetrics.NetworkMetricSnapshot -import kamon.metrics.NonHeapMetrics.NonHeapMetricSnapshot -import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsSnapshot -import kamon.metrics.ThreadMetrics.ThreadMetricSnapshot -import kamon.metrics._ -import kamon.system.SystemMetricsExtension -import org.scalatest.{ Matchers, WordSpecLike } - -import scala.concurrent.duration._ - -class SystemMetricsSpec extends TestKitBase with WordSpecLike with Matchers with RedirectLogging { - 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 - |} - """.stripMargin)) - - "the Kamon CPU Metrics" should { - "record user, system, wait, idle, stolen 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 - CPUMetrics.stolen.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 Non-Heap Metrics" should { - "record used, max, commited metrics" in new NonHeapMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val NonHeapMetrics = expectNonHeapMetrics(metricsListener, 3 seconds) - NonHeapMetrics.used.max should be >= 0L - NonHeapMetrics.max.max should be >= 0L - NonHeapMetrics.committed.max should be >= 0L - } - } - - "the Kamon Thread Metrics" should { - "record daemon, count, peak metrics" in new ThreadMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val ThreadMetrics = expectThreadMetrics(metricsListener, 3 seconds) - ThreadMetrics.daemon.max should be >= 0L - ThreadMetrics.count.max should be >= 0L - ThreadMetrics.peak.max should be >= 0L - } - } - - "the Kamon ClassLoading Metrics" should { - "record loaded, unloaded, current metrics" in new ClassLoadingMetricsListenerFixture { - val metricsListener = subscribeToMetrics() +import java.lang.management.ManagementFactory - val ClassLoadingMetrics = expectClassLoadingMetrics(metricsListener, 3 seconds) - ClassLoadingMetrics.loaded.max should be >= 0L - ClassLoadingMetrics.unloaded.max should be >= 0L - ClassLoadingMetrics.current.max should be >= 0L - } - } - - "the Kamon Disk Metrics" should { - "record reads, writes, queue, service time metrics" in new DiskMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val DiskMetrics = expectDiskMetrics(metricsListener, 3 seconds) - DiskMetrics.reads.max should be >= 0L - DiskMetrics.writes.max should be >= 0L - DiskMetrics.queue.max should be >= 0L - DiskMetrics.serviceTime.max should be >= 0L - } - } - - "the Kamon Load Average Metrics" should { - "record 1 minute, 5 minutes and 15 minutes metrics" in new LoadAverageMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val LoadAverageMetrics = expectLoadAverageMetrics(metricsListener, 3 seconds) - LoadAverageMetrics.one.max should be >= 0L - LoadAverageMetrics.five.max should be >= 0L - LoadAverageMetrics.fifteen.max should be >= 0L - } - } +import com.typesafe.config.ConfigFactory +import kamon.system.jmx.GarbageCollectionMetrics +import kamon.testkit.BaseKamonSpec +import scala.collection.JavaConverters._ - "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 - } - } +class SystemMetricsSpec extends BaseKamonSpec("system-metrics-spec") with RedirectLogging { - "the Kamon Network Metrics" should { - "record rxBytes, txBytes, rxErrors, txErrors, rxDropped, txDropped 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 - NetworkMetrics.rxDropped.max should be >= 0L - NetworkMetrics.txDropped.max should be >= 0L - } - } - - "the Kamon Process CPU Metrics" should { - "record Cpu Percent, Total Process Time metrics" in new ProcessCPUMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + override lazy val config = + ConfigFactory.parseString( + """ + |kamon.metric { + | tick-interval = 1 hour + |} + | + |akka { + | extensions = ["kamon.system.SystemMetrics"] + |} + """.stripMargin) - val ProcessCPUMetrics = expectProcessCPUMetrics(metricsListener, 3 seconds) - ProcessCPUMetrics.cpuPercent.max should be >= 0L - ProcessCPUMetrics.totalProcessTime.max should be >= 0L - } - } + override protected def beforeAll(): Unit = + Thread.sleep(2000) // Give some room to the recorders to store some values. - "the Kamon ContextSwitches Metrics" should { - "record Context Switches Global, Voluntary and Non Voluntary metrics" in new ContextSwitchesMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + "the Kamon System Metrics module" should { + "record user, system, wait, idle and stolen CPU metrics" in { + val cpuMetrics = takeSnapshotOf("cpu", "system-metric") - val ContextSwitchesMetrics = expectContextSwitchesMetrics(metricsListener, 3 seconds) - ContextSwitchesMetrics.perProcessVoluntary.max should be >= 0L - ContextSwitchesMetrics.perProcessNonVoluntary.max should be >= 0L - ContextSwitchesMetrics.global.max should be >= 0L + cpuMetrics.histogram("cpu-user").get.numberOfMeasurements should be > 0L + cpuMetrics.histogram("cpu-system").get.numberOfMeasurements should be > 0L + cpuMetrics.histogram("cpu-wait").get.numberOfMeasurements should be > 0L + cpuMetrics.histogram("cpu-idle").get.numberOfMeasurements should be > 0L + cpuMetrics.histogram("cpu-stolen").get.numberOfMeasurements 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] - } + "record count and time garbage collection metrics" in { + val availableGarbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) - 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 - } - } + for (collectorName ← availableGarbageCollectors) { + val sanitizedName = GarbageCollectionMetrics.sanitizeCollectorName(collectorName.getName) + val collectorMetrics = takeSnapshotOf(s"$sanitizedName-garbage-collector", "system-metric") - def expectGCMetrics(listener: TestProbe, waitTime: FiniteDuration): GCMetricSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + collectorMetrics.gauge("garbage-collection-count").get.numberOfMeasurements should be > 0L + collectorMetrics.gauge("garbage-collection-time").get.numberOfMeasurements should be > 0L + } } - val gcMetricsOption = tickSnapshot.metrics.get(GCMetrics(SystemMetricsExtension.garbageCollectors(0).getName.replaceAll("""[^\w]""", "-"))) - gcMetricsOption should not be empty - gcMetricsOption.get.asInstanceOf[GCMetricSnapshot] - } + "record used, max and committed heap metrics" in { + val heapMetrics = takeSnapshotOf("heap-memory", "system-metric") - 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 + heapMetrics.gauge("heap-used").get.numberOfMeasurements should be > 0L + heapMetrics.gauge("heap-max").get.numberOfMeasurements should be > 0L + heapMetrics.gauge("heap-committed").get.numberOfMeasurements should be > 0L } - } - 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] - } + "record used, max and committed non-heap metrics" in { + val nonHeapMetrics = takeSnapshotOf("non-heap-memory", "system-metric") - 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 + nonHeapMetrics.gauge("non-heap-used").get.numberOfMeasurements should be > 0L + nonHeapMetrics.gauge("non-heap-max").get.numberOfMeasurements should be > 0L + nonHeapMetrics.gauge("non-heap-committed").get.numberOfMeasurements should be > 0L } - } - trait NonHeapMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(NonHeapMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } + "record daemon, count and peak jvm threads metrics" in { + val threadsMetrics = takeSnapshotOf("threads", "system-metric") - def expectNonHeapMetrics(listener: TestProbe, waitTime: FiniteDuration): NonHeapMetricSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + threadsMetrics.gauge("daemon-thread-count").get.numberOfMeasurements should be > 0L + threadsMetrics.gauge("peak-thread-count").get.numberOfMeasurements should be > 0L + threadsMetrics.gauge("thread-count").get.numberOfMeasurements should be > 0L } - val nonHeapMetricsOption = tickSnapshot.metrics.get(NonHeapMetrics(SystemMetricsExtension.NonHeap)) - nonHeapMetricsOption should not be empty - nonHeapMetricsOption.get.asInstanceOf[NonHeapMetricSnapshot] - } - trait ThreadMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(ThreadMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } + "record loaded, unloaded and current class loading metrics" in { + val classLoadingMetrics = takeSnapshotOf("class-loading", "system-metric") - def expectThreadMetrics(listener: TestProbe, waitTime: FiniteDuration): ThreadMetricSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + classLoadingMetrics.gauge("classes-loaded").get.numberOfMeasurements should be > 0L + classLoadingMetrics.gauge("classes-unloaded").get.numberOfMeasurements should be > 0L + classLoadingMetrics.gauge("classes-currently-loaded").get.numberOfMeasurements should be > 0L } - val threadMetricsOption = tickSnapshot.metrics.get(ThreadMetrics(SystemMetricsExtension.Threads)) - threadMetricsOption should not be empty - threadMetricsOption.get.asInstanceOf[ThreadMetricSnapshot] - } - trait ClassLoadingMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(ClassLoadingMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } + "record reads, writes, queue time and service time file system metrics" in { + val fileSystemMetrics = takeSnapshotOf("file-system", "system-metric") - def expectClassLoadingMetrics(listener: TestProbe, waitTime: FiniteDuration): ClassLoadingMetricSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + fileSystemMetrics.histogram("file-system-reads").get.numberOfMeasurements should be > 0L + fileSystemMetrics.histogram("file-system-writes").get.numberOfMeasurements should be > 0L } - val classLoadingMetricsOption = tickSnapshot.metrics.get(ClassLoadingMetrics(SystemMetricsExtension.Classes)) - classLoadingMetricsOption should not be empty - classLoadingMetricsOption.get.asInstanceOf[ClassLoadingMetricSnapshot] - } - trait DiskMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(DiskMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } + "record 1 minute, 5 minutes and 15 minutes metrics load average metrics" in { + val loadAverage = takeSnapshotOf("load-average", "system-metric") - def expectDiskMetrics(listener: TestProbe, waitTime: FiniteDuration): DiskMetricsSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + loadAverage.histogram("one-minute").get.numberOfMeasurements should be > 0L + loadAverage.histogram("five-minutes").get.numberOfMeasurements should be > 0L + loadAverage.histogram("fifteen-minutes").get.numberOfMeasurements should be > 0L } - val diskMetricsOption = tickSnapshot.metrics.get(DiskMetrics(SystemMetricsExtension.Disk)) - diskMetricsOption should not be empty - diskMetricsOption.get.asInstanceOf[DiskMetricsSnapshot] - } - trait LoadAverageMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(LoadAverageMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } + "record used, free, swap used, swap free system memory metrics" in { + val memoryMetrics = takeSnapshotOf("memory", "system-metric") - def expectLoadAverageMetrics(listener: TestProbe, waitTime: FiniteDuration): LoadAverageMetricsSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + memoryMetrics.histogram("memory-used").get.numberOfMeasurements should be > 0L + memoryMetrics.histogram("memory-free").get.numberOfMeasurements should be > 0L + memoryMetrics.histogram("swap-used").get.numberOfMeasurements should be > 0L + memoryMetrics.histogram("swap-free").get.numberOfMeasurements should be > 0L } - val loadAverageMetricsOption = tickSnapshot.metrics.get(LoadAverageMetrics(SystemMetricsExtension.LoadAverage)) - loadAverageMetricsOption should not be empty - loadAverageMetricsOption.get.asInstanceOf[LoadAverageMetricsSnapshot] - } - 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] - } + "record rxBytes, txBytes, rxErrors, txErrors, rxDropped, txDropped network metrics" in { + val networkMetrics = takeSnapshotOf("network", "system-metric") - 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 + networkMetrics.histogram("tx-bytes").get.numberOfMeasurements should be > 0L + networkMetrics.histogram("rx-bytes").get.numberOfMeasurements should be > 0L + networkMetrics.histogram("tx-errors").get.numberOfMeasurements should be > 0L + networkMetrics.histogram("rx-errors").get.numberOfMeasurements should be > 0L + networkMetrics.histogram("tx-dropped").get.numberOfMeasurements should be > 0L + networkMetrics.histogram("rx-dropped").get.numberOfMeasurements should be > 0L } - } - 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] - } + "record system and user CPU percentage for the application process" in { + val processCpuMetrics = takeSnapshotOf("process-cpu", "system-metric") - 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 + processCpuMetrics.histogram("process-user-cpu").get.numberOfMeasurements should be > 0L + processCpuMetrics.histogram("process-system-cpu").get.numberOfMeasurements should be > 0L + processCpuMetrics.histogram("process-cpu").get.numberOfMeasurements should be > 0L } - } - 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] - } + "record Context Switches Global, Voluntary and Non Voluntary metrics when running on Linux" in { + if (isLinux) { + val contextSwitchesMetrics = takeSnapshotOf("context-switches", "system-metric") - 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 + contextSwitchesMetrics.histogram("context-switches-process-voluntary").get.numberOfMeasurements should be > 0L + contextSwitchesMetrics.histogram("context-switches-process-non-voluntary").get.numberOfMeasurements should be > 0L + contextSwitchesMetrics.histogram("context-switches-global").get.numberOfMeasurements should be > 0L + } } } - def expectContextSwitchesMetrics(listener: TestProbe, waitTime: FiniteDuration): ContextSwitchesMetricsSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] - } - val contextSwitchesMetricsOption = tickSnapshot.metrics.get(ContextSwitchesMetrics(SystemMetricsExtension.ContextSwitches)) - contextSwitchesMetricsOption should not be empty - contextSwitchesMetricsOption.get.asInstanceOf[ContextSwitchesMetricsSnapshot] - } + def isLinux: Boolean = + System.getProperty("os.name").indexOf("Linux") != -1 - trait ContextSwitchesMetricsListenerFixture { - def subscribeToMetrics(): TestProbe = { - val metricsListener = TestProbe() - Kamon(Metrics).subscribe(ContextSwitchesMetrics, "*", metricsListener.ref, permanently = true) - // Wait for one empty snapshot before proceeding to the test. - metricsListener.expectMsgType[TickMetricSnapshot] - metricsListener - } - } } |