diff options
author | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-14 13:50:36 +0100 |
---|---|---|
committer | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-14 13:50:36 +0100 |
commit | e931727454fbd97fb39d163255edbcdcd7bcdbc6 (patch) | |
tree | 98b4bdcaa1af1dbbf201036ce05021bc096db62f /kamon-system-metrics/src | |
parent | 8af0dfb1e2c8892023dd1bc6fbae1dae2ffb16ba (diff) | |
parent | 66b35556aa1bf0975cefa35603660991cdfcc526 (diff) | |
download | Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.tar.gz Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.tar.bz2 Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.zip |
Merge branch 'single-kamon-instance-per-jvm' into release-legacy-akka-2.2
Conflicts:
kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala
kamon-core/src/main/scala/kamon/instrumentation/akka/ActorCellInstrumentation.scala
kamon-core/src/main/scala/kamon/instrumentation/akka/AskPatternInstrumentation.scala
kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala
kamon-core/src/main/scala/kamon/metric/Subscriptions.scala
kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala
kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala
kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/RouterMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala
kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala
kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala
kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala
kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala
kamon-play/src/main/scala/kamon/play/Play.scala
kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala
kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala
kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
project/Dependencies.scala
version.sbt
Diffstat (limited to 'kamon-system-metrics/src')
51 files changed, 1255 insertions, 1429 deletions
diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/index b/kamon-system-metrics/src/main/resources/kamon/system/sigar/index deleted file mode 100644 index cad1f326..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/index +++ /dev/null @@ -1,21 +0,0 @@ -libsigar-amd64-freebsd-6.so -libsigar-amd64-linux.so -libsigar-amd64-solaris.so -libsigar-ia64-hpux-11.sl -libsigar-ia64-linux.so -libsigar-pa-hpux-11.sl -libsigar-ppc64-aix-5.so -libsigar-ppc64-linux.so -libsigar-ppc-aix-5.so -libsigar-ppc-linux.so -libsigar-s390x-linux.so -libsigar-sparc64-solaris.so -libsigar-sparc-solaris.so -libsigar-universal64-macosx.dylib -libsigar-universal-macosx.dylib -libsigar-x86-freebsd-5.so -libsigar-x86-freebsd-6.so -libsigar-x86-linux.so -libsigar-x86-solaris.so -sigar-amd64-winnt.dll -sigar-x86-winnt.dll
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-freebsd-6.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-freebsd-6.so Binary files differdeleted file mode 100644 index 3e94f0d2..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-freebsd-6.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-linux.so Binary files differdeleted file mode 100644 index 5a2e4c24..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-solaris.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-solaris.so Binary files differdeleted file mode 100644 index 6396482a..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-amd64-solaris.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-hpux-11.sl b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-hpux-11.sl Binary files differdeleted file mode 100644 index d92ea4a9..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-hpux-11.sl +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-linux.so Binary files differdeleted file mode 100644 index 2bd2fc8e..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ia64-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-pa-hpux-11.sl b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-pa-hpux-11.sl Binary files differdeleted file mode 100644 index 0dfd8a11..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-pa-hpux-11.sl +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-aix-5.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-aix-5.so Binary files differdeleted file mode 100644 index 7d4b5199..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-aix-5.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-linux.so Binary files differdeleted file mode 100644 index 4394b1b0..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-aix-5.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-aix-5.so Binary files differdeleted file mode 100644 index 35fd8288..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-aix-5.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-linux.so Binary files differdeleted file mode 100644 index a1ba2529..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-ppc64-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-s390x-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-s390x-linux.so Binary files differdeleted file mode 100644 index c275f4ac..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-s390x-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc-solaris.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc-solaris.so Binary files differdeleted file mode 100644 index aa847d2b..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc-solaris.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc64-solaris.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc64-solaris.so Binary files differdeleted file mode 100644 index 6c4fe809..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-sparc64-solaris.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal-macosx.dylib b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal-macosx.dylib Binary files differdeleted file mode 100644 index 27ab1071..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal-macosx.dylib +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal64-macosx.dylib b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal64-macosx.dylib Binary files differdeleted file mode 100644 index 0c721fec..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-universal64-macosx.dylib +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-5.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-5.so Binary files differdeleted file mode 100644 index 8c50c611..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-5.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-6.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-6.so Binary files differdeleted file mode 100644 index f0800274..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-freebsd-6.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-linux.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-linux.so Binary files differdeleted file mode 100644 index a0b64edd..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-linux.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-solaris.so b/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-solaris.so Binary files differdeleted file mode 100644 index c6452e56..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/libsigar-x86-solaris.so +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-amd64-winnt.dll b/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-amd64-winnt.dll Binary files differdeleted file mode 100644 index 1ec8a035..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-amd64-winnt.dll +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-x86-winnt.dll b/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-x86-winnt.dll Binary files differdeleted file mode 100644 index 6afdc016..00000000 --- a/kamon-system-metrics/src/main/resources/kamon/system/sigar/sigar-x86-winnt.dll +++ /dev/null diff --git a/kamon-system-metrics/src/main/resources/reference.conf b/kamon-system-metrics/src/main/resources/reference.conf index fbdb3b89..57b34195 100644 --- a/kamon-system-metrics/src/main/resources/reference.conf +++ b/kamon-system-metrics/src/main/resources/reference.conf @@ -3,74 +3,187 @@ # ============================================ # kamon { - 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 - } - } - - network { - rx-bytes = ${kamon.metrics.precision.default-histogram-precision} - tx-bytes = ${kamon.metrics.precision.default-histogram-precision} - rx-errors = ${kamon.metrics.precision.default-histogram-precision} - tx-errors = ${kamon.metrics.precision.default-histogram-precision} - } - - memory { - used = ${kamon.metrics.precision.default-histogram-precision} - free = ${kamon.metrics.precision.default-histogram-precision} - buffer = ${kamon.metrics.precision.default-histogram-precision} - cache = ${kamon.metrics.precision.default-histogram-precision} - swap-used = ${kamon.metrics.precision.default-histogram-precision} - swap-free = ${kamon.metrics.precision.default-histogram-precision} - } - - 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} - } + system-metrics { + + # 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 + } + + # Dispatcher to be used by the ContextSwitchesUpdater actor. + context-switches-dispatcher { + executor = "thread-pool-executor" + type = PinnedDispatcher + } + } + + 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 + } + + 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 } - jvm { - heap { - used = ${kamon.metrics.precision.default-gauge-precision} - max = ${kamon.metrics.precision.default-gauge-precision} - committed = ${kamon.metrics.precision.default-gauge-precision} - } - - gc { - count = ${kamon.metrics.precision.default-gauge-precision} - time = ${kamon.metrics.precision.default-gauge-precision} - } + 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} + + } + } + + modules { + kamon-system-metrics { + auto-start = yes + requires-aspectj = no + extension-id = "kamon.system.SystemMetrics" } } }
\ 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 ef7f225c..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/CPUMetrics.scala +++ /dev/null @@ -1,84 +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 class CPUMetricRecorder(user: Histogram, system: Histogram, cpuWait: Histogram, idle: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - CPUMetricSnapshot(user.collect(context), system.collect(context), cpuWait.collect(context), idle.collect(context)) - } - - def cleanup: Unit = {} - } - - case class CPUMetricSnapshot(user: Histogram.Snapshot, system: Histogram.Snapshot, cpuWait: Histogram.Snapshot, idle: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = CPUMetricSnapshot - - def merge(that: CPUMetricSnapshot, context: CollectionContext): GroupSnapshotType = { - CPUMetricSnapshot(user.merge(that.user, context), system.merge(that.system, context), cpuWait.merge(that.cpuWait, context), idle.merge(that.idle, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - User -> user, - System -> system, - Wait -> cpuWait, - Idle -> idle) - } - - val Factory = 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") - - new CPUMetricRecorder( - Histogram.fromConfig(userConfig), - Histogram.fromConfig(systemConfig), - Histogram.fromConfig(cpuWaitConfig), - Histogram.fromConfig(idleConfig)) - } -} diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/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/GCMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.scala deleted file mode 100644 index bc5fc724..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/GCMetrics.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.metrics - -import java.lang.management.GarbageCollectorMXBean - -import akka.actor.ActorSystem -import com.typesafe.config.Config -import kamon.metric._ -import kamon.metric.instrument.{ Gauge, Histogram } - -case class GCMetrics(name: String) extends MetricGroupIdentity { - val category = GCMetrics -} - -object GCMetrics extends MetricGroupCategory { - val name = "gc" - - case object CollectionCount extends MetricIdentity { val name = "collection-count" } - case object CollectionTime extends MetricIdentity { val name = "collection-time" } - - case class GCMetricRecorder(count: Gauge, time: Gauge) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - GCMetricSnapshot(count.collect(context), time.collect(context)) - } - - def cleanup: Unit = {} - } - - case class GCMetricSnapshot(count: Histogram.Snapshot, time: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = GCMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - GCMetricSnapshot(count.merge(that.count, context), time.merge(that.time, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - CollectionCount -> count, - CollectionTime -> time) - } - - def Factory(gc: GarbageCollectorMXBean) = 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( - Gauge.fromConfig(countConfig, system)(() ⇒ gc.getCollectionCount), - Gauge.fromConfig(timeConfig, system, Scale.Milli)(() ⇒ gc.getCollectionTime)) - } -} - diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/HeapMetrics.scala deleted file mode 100644 index ac033fe2..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-heap" } - case object Max extends MetricIdentity { val name = "max-heap" } - case object Committed extends MetricIdentity { val name = "committed-heap" } - - case class HeapMetricRecorder(used: Gauge, max: Gauge, committed: Gauge) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - HeapMetricSnapshot(used.collect(context), max.collect(context), committed.collect(context)) - } - - def cleanup: Unit = {} - } - - case class HeapMetricSnapshot(used: Histogram.Snapshot, max: Histogram.Snapshot, committed: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = HeapMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - HeapMetricSnapshot(used.merge(that.used, context), max.merge(that.max, context), committed.merge(that.committed, context)) - } - - lazy val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - Used -> used, - Max -> max, - Committed -> committed) - } - - val Factory = 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/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 f348bb0c..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/metrics/NetworkMetrics.scala +++ /dev/null @@ -1,83 +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 class NetworkMetricRecorder(rxBytes: Histogram, txBytes: Histogram, rxErrors: Histogram, txErrors: Histogram) - extends MetricGroupRecorder { - - def collect(context: CollectionContext): MetricGroupSnapshot = { - NetworkMetricSnapshot(rxBytes.collect(context), txBytes.collect(context), rxErrors.collect(context), txErrors.collect(context)) - } - - def cleanup: Unit = {} - } - - case class NetworkMetricSnapshot(rxBytes: Histogram.Snapshot, txBytes: Histogram.Snapshot, rxErrors: Histogram.Snapshot, txErrors: Histogram.Snapshot) - extends MetricGroupSnapshot { - - type GroupSnapshotType = NetworkMetricSnapshot - - def merge(that: GroupSnapshotType, context: CollectionContext): GroupSnapshotType = { - NetworkMetricSnapshot(rxBytes.merge(that.rxBytes, context), txBytes.merge(that.txBytes, context), rxErrors.merge(that.rxErrors, context), txErrors.merge(that.txErrors, context)) - } - - val metrics: Map[MetricIdentity, MetricSnapshot] = Map( - RxBytes -> rxBytes, - TxBytes -> txBytes, - RxErrors -> rxErrors, - TxErrors -> txErrors) - } - - val Factory = 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") - - new NetworkMetricRecorder( - Histogram.fromConfig(rxBytesConfig, Scale.Kilo), - Histogram.fromConfig(txBytesConfig, Scale.Kilo), - Histogram.fromConfig(rxErrorsConfig), - Histogram.fromConfig(txErrorsConfig)) - } -}
\ No newline at end of file diff --git a/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/metrics/ProcessCPUMetrics.scala 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/system/SystemMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala deleted file mode 100644 index 62ffdb33..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetrics.scala +++ /dev/null @@ -1,64 +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 systemMetricsExtension = Kamon(Metrics)(system) - - //JVM Metrics - systemMetricsExtension.register(HeapMetrics(Heap), HeapMetrics.Factory) - garbageCollectors.map { gc ⇒ systemMetricsExtension.register(GCMetrics(gc.getName), GCMetrics.Factory(gc)) } - - //System Metrics - system.actorOf(SystemMetricsCollector.props(1 second), "system-metrics-collector") -} - -object SystemMetricsExtension { - val CPU = "cpu" - val ProcessCPU = "process-cpu" - val Network = "network" - val Memory = "memory" - val Heap = "heap" - val ContextSwitches = "context-switches" - - def toKB(value: Long): Long = (value / 1024) - def toMB(value: Long): Long = (value / 1024 / 1024) - def toLong(value: Double): Long = math round (value * 100L) - - val garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) -} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala deleted file mode 100644 index f41a76d5..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsCollector.scala +++ /dev/null @@ -1,176 +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.IOException - -import akka.actor.{ ActorLogging, Actor, Props } -import kamon.Kamon -import kamon.metric.Metrics -import kamon.metrics.CPUMetrics.CPUMetricRecorder -import kamon.metrics.ContextSwitchesMetrics.ContextSwitchesMetricsRecorder -import kamon.metrics.MemoryMetrics.MemoryMetricRecorder -import kamon.metrics.NetworkMetrics.NetworkMetricRecorder -import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsRecorder -import kamon.metrics._ -import kamon.system.sigar.SigarHolder -import org.hyperic.sigar.{ Mem, NetInterfaceStat, SigarProxy } - -import scala.concurrent.duration.FiniteDuration -import scala.io.Source - -class SystemMetricsCollector(collectInterval: FiniteDuration) extends Actor with ActorLogging with SigarExtensionProvider { - import kamon.system.SystemMetricsCollector._ - import kamon.system.SystemMetricsExtension._ - - val collectSchedule = context.system.scheduler.schedule(collectInterval, collectInterval, self, Collect)(context.dispatcher) - - val systemMetricsExtension = Kamon(Metrics)(context.system) - - val cpuRecorder = systemMetricsExtension.register(CPUMetrics(CPU), CPUMetrics.Factory) - val processCpuRecorder = systemMetricsExtension.register(ProcessCPUMetrics(ProcessCPU), ProcessCPUMetrics.Factory) - val memoryRecorder = systemMetricsExtension.register(MemoryMetrics(Memory), MemoryMetrics.Factory) - val networkRecorder = systemMetricsExtension.register(NetworkMetrics(Network), NetworkMetrics.Factory) - val contextSwitchesRecorder = systemMetricsExtension.register(ContextSwitchesMetrics(ContextSwitches), ContextSwitchesMetrics.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) - - 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)) - } - - 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) = { - nr.rxBytes.record(collect(sigar, interfaces)(net ⇒ toKB(net.getRxBytes))) - nr.txBytes.record(collect(sigar, interfaces)(net ⇒ toKB(net.getTxBytes))) - nr.rxErrors.record(collect(sigar, interfaces)(net ⇒ net.getRxErrors)) - nr.txErrors.record(collect(sigar, interfaces)(net ⇒ net.getTxErrors)) - - def collect(sigar: SigarProxy, interfaces: Set[String])(block: NetInterfaceStat ⇒ Long): Long = { - interfaces.foldLeft(0L) { (totalBytes, interface) ⇒ - { - val net = sigar.getNetInterfaceStat(interface) - totalBytes + block(net) - } - } - } - } - - private def recordContextSwitches(ctxt: 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("ctxt")) { - contextSwitches = line.substring(line.indexOf(" ") + 1).toLong - } - } - } catch { - case ex: IOException ⇒ { - log.error("Error trying to read [{}]", filename) - } - } - contextSwitches - } - - val (perProcessVoluntary, perProcessNonVoluntary) = contextSwitchesByProcess(pid) - ctxt.perProcessVoluntary.record(perProcessVoluntary) - ctxt.perProcessNonVoluntary.record(perProcessNonVoluntary) - ctxt.global.record(contextSwitches) - } -} - -object SystemMetricsCollector { - case object Collect - - object OsUtils { - def isLinux: Boolean = System.getProperty("os.name").indexOf("Linux") != -1; - } - - def props(collectInterval: FiniteDuration): Props = Props[SystemMetricsCollector](new SystemMetricsCollector(collectInterval)) -} - -trait SigarExtensionProvider { - lazy val sigar = SigarHolder.instance() - - def pid = sigar.getPid - - val interfaces: Set[String] = sigar.getNetInterfaceList.toSet -} 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..ebdaf01f --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala @@ -0,0 +1,71 @@ +/* + * ========================================================================================= + * 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.supervisor.ModuleSupervisor +import kamon.system.custom.{ ContextSwitchesUpdater, ContextSwitchesMetrics } +import kamon.system.jmx._ +import kamon.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 + + // 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..384c89f1 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala @@ -0,0 +1,118 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.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 + +/** + * Context Switches metrics: + * - process-voluntary: Total number of voluntary context switches related to the current process (one thread explicitly yield the CPU to another). + * - process-non-voluntary: Total number of involuntary context switches related to the current process (the system scheduler suspends and active thread, and switches control to a different thread). + * - global: Total number of context switches across all CPUs. + */ +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("ctxt")) { + 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 + 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..568f1b71 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ClassLoadingMetrics.scala @@ -0,0 +1,48 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +/** + * Class Loading metrics, as reported by JMX: + * - @see [[http://docs.oracle.com/javase/7/docs/api/java/lang/management/ClassLoadingMXBean.html "ClassLoadingMXBean"]] + */ +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..a9ab4b62 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala @@ -0,0 +1,54 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.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._ + +/** + * Garbage Collection metrics, as reported by JMX: + * - @see [[http://docs.oracle.com/javase/7/docs/api/java/lang/management/GarbageCollectorMXBean.html "GarbageCollectorMXBean"]] + */ +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..cd2e3e8e --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/HeapMemoryMetrics.scala @@ -0,0 +1,49 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +/** + * Heap Memory metrics, as reported by JMX: + * - @see [[http://docs.oracle.com/javase/7/docs/api/java/lang/management/MemoryMXBean.html "MemoryMXBean"]] + */ +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..be0ee08c --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala @@ -0,0 +1,29 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.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..7425972b --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/NonHeapMemoryMetrics.scala @@ -0,0 +1,53 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } + +/** + * Non Heap Memory metrics, as reported by JMX: + * - @see [[http://docs.oracle.com/javase/7/docs/api/java/lang/management/MemoryMXBean.html "MemoryMXBean"]] + */ +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..b9bf9622 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/ThreadsMetrics.scala @@ -0,0 +1,48 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.jmx + +import java.lang.management.ManagementFactory + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory + +/** + * Threads metrics, as reported by JMX: + * - @see [[http://docs.oracle.com/javase/7/docs/api/java/lang/management/ThreadMXBean.html "ThreadMXBean"]] + */ +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..0e9a5b53 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/CpuMetrics.scala @@ -0,0 +1,53 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.Sigar + +/** + * Cpu usage metrics, as reported by Sigar: + * - user: Total percentage of system cpu user time. + * - system: Total percentage of system cpu kernel time. + * - wait: Total percentage of system cpu io wait time. + * - idle: Total percentage of system cpu idle time + * - stolen: Total percentage of system cpu involuntary wait time. @see [[https://www.datadoghq.com/2013/08/understanding-aws-stolen-cpu-and-how-it-affects-your-apps/ "Understanding Stolen Cpu"]] + */ +class CpuMetrics(sigar: Sigar, 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(): 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(sigar: Sigar, instrumentFactory: InstrumentFactory): CpuMetrics = + new CpuMetrics(sigar, 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..06e3e37d --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/DiffRecordingHistogram.scala @@ -0,0 +1,60 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.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) { + val diff = value - _lastObservedValue.getAndSet(value) + val current = if (diff >= 0) diff else 0L + + wrappedHistogram.record(current, 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..d3bfefbe --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/FileSystemMetrics.scala @@ -0,0 +1,48 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.{ Memory, InstrumentFactory } +import org.hyperic.sigar.{ DiskUsage, FileSystem, Sigar } +import scala.util.Try + +/** + * Disk usage metrics, as reported by Sigar: + * - readBytes: Total number of physical disk reads. + * - writesBytes: Total number of physical disk writes. + */ +class FileSystemMetrics(sigar: Sigar, 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)) + + val fileSystems = sigar.getFileSystemList.filter(_.getType == FileSystem.TYPE_LOCAL_DISK).map(_.getDevName).toSet + + def sumOfAllFileSystems(sigar: Sigar, thunk: DiskUsage ⇒ Long): Long = Try { + fileSystems.map(i ⇒ thunk(sigar.getDiskUsage(i))).fold(0L)(_ + _) + } getOrElse 0L + + def update(): Unit = { + reads.record(sumOfAllFileSystems(sigar, _.getReadBytes)) + writes.record(sumOfAllFileSystems(sigar, _.getWriteBytes)) + } +} + +object FileSystemMetrics extends SigarMetricRecorderCompanion("file-system") { + def apply(sigar: Sigar, instrumentFactory: InstrumentFactory): FileSystemMetrics = + new FileSystemMetrics(sigar, 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..8d7bd808 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/LoadAverageMetrics.scala @@ -0,0 +1,45 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.Sigar + +/** + * Load Average metrics, as reported by Sigar: + * - The system load averages for the past 1, 5, and 15 minutes. + */ +class LoadAverageMetrics(sigar: Sigar, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val oneMinute = histogram("one-minute") + val fiveMinutes = histogram("five-minutes") + val fifteenMinutes = histogram("fifteen-minutes") + + def update(): 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(sigar: Sigar, instrumentFactory: InstrumentFactory): LoadAverageMetrics = + new LoadAverageMetrics(sigar, 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..787c9f2f --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/MemoryMetrics.scala @@ -0,0 +1,57 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.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(sigar: Sigar, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val used = histogram("memory-used", Memory.Bytes) + val cached = histogram("memory-cache-and-buffer", Memory.Bytes) + val free = histogram("memory-free", Memory.Bytes) + val total = histogram("memory-total", Memory.Bytes) + val swapUsed = histogram("swap-used", Memory.Bytes) + val swapFree = histogram("swap-free", Memory.Bytes) + + def update(): Unit = { + val mem = sigar.getMem + val swap = sigar.getSwap + val cachedMemory = if (mem.getActualFree > mem.getFree) mem.getActualFree - mem.getFree else 0L + + used.record(mem.getActualUsed) + free.record(mem.getActualFree) + cached.record(cachedMemory) + total.record(mem.getTotal) + swapUsed.record(swap.getUsed) + swapFree.record(swap.getFree) + } +} + +object MemoryMetrics extends SigarMetricRecorderCompanion("memory") { + + def apply(sigar: Sigar, instrumentFactory: InstrumentFactory): MemoryMetrics = + new MemoryMetrics(sigar, 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..30575508 --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/NetworkMetrics.scala @@ -0,0 +1,61 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument._ +import org.hyperic.sigar.{ NetInterfaceStat, Sigar } +import scala.util.Try + +/** + * Network metrics, as reported by Sigar: + * - rxBytes: Total number of received packets in bytes. + * - txBytes: Total number of transmitted packets in bytes. + * - rxErrors: Total number of packets received with errors. This includes too-long-frames errors, ring-buffer overflow errors, etc. + * - txErrors: Total number of errors encountered while transmitting packets. This list includes errors due to the transmission being aborted, errors due to the carrier, etc. + * - rxDropped: Total number of incoming packets dropped. + * - txDropped: Total number of outgoing packets dropped. + */ +class NetworkMetrics(sigar: Sigar, 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")) + + val interfaces = sigar.getNetInterfaceList.toList.filter(_ != "lo") + + def sumOfAllInterfaces(sigar: Sigar, thunk: NetInterfaceStat ⇒ Long): Long = Try { + interfaces.map(i ⇒ thunk(sigar.getNetInterfaceStat(i))).fold(0L)(_ + _) + + } getOrElse 0L + + def update(): 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(sigar: Sigar, instrumentFactory: InstrumentFactory): NetworkMetrics = + new NetworkMetrics(sigar, 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..4432e6cd --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/ProcessCpuMetrics.scala @@ -0,0 +1,77 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import kamon.metric.GenericEntityRecorder +import kamon.metric.instrument.InstrumentFactory +import org.hyperic.sigar.{ ProcCpu, Sigar } + +/** + * Process Cpu usage metrics, as reported by Sigar: + * - user: Process cpu user time. + * - total: Process cpu time (sum of User and Sys). + * - system: Process cpu kernel time. + */ +class ProcessCpuMetrics(sigar: Sigar, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SigarMetric { + val processUserCpu = histogram("process-user-cpu") + val processSystemCpu = histogram("process-system-cpu") + val processTotalCpu = histogram("process-cpu") + + val pid = sigar.getPid + val totalCores = sigar.getCpuInfoList.headOption.map(_.getTotalCores.toLong).getOrElse(1L) + + var lastProcCpu: ProcCpu = sigar.getProcCpu(pid) + var currentLoad: Long = 0 + + /** + * While CPU usage time updates not very often, We have introduced a simple heuristic, that supposes that the load is the same as previous, + * while CPU usage time doesn't update. But supposing that it could be zero load for a process for some time, + * We used an arbitrary duration of 2000 milliseconds, after which the same CPU usage time value become legal, and it is supposed that the load is really zero. + * + * @see [[http://stackoverflow.com/questions/19323364/using-sigar-api-to-get-jvm-cpu-usage "StackOverflow: Using Sigar API to get JVM Cpu usage"]] + */ + def update(): Unit = { + val currentProcCpu = sigar.getProcCpu(pid) + val totalDiff = currentProcCpu.getTotal - lastProcCpu.getTotal + val userDiff = currentProcCpu.getUser - lastProcCpu.getUser + val systemDiff = currentProcCpu.getSys - lastProcCpu.getSys + val timeDiff = currentProcCpu.getLastTime - lastProcCpu.getLastTime + + def percentUsage(delta: Long): Long = 100 * delta / timeDiff / totalCores + + if (totalDiff == 0) { + if (timeDiff > 2000) currentLoad = 0 + if (currentLoad == 0) lastProcCpu = currentProcCpu + } else { + val totalPercent = percentUsage(totalDiff) + val userPercent = percentUsage(userDiff) + val systemPercent = percentUsage(systemDiff) + + processUserCpu.record(userPercent) + processSystemCpu.record(systemPercent) + processTotalCpu.record(userPercent + systemPercent) + + currentLoad = totalPercent + lastProcCpu = currentProcCpu + } + } +} + +object ProcessCpuMetrics extends SigarMetricRecorderCompanion("process-cpu") { + def apply(sigar: Sigar, instrumentFactory: InstrumentFactory): ProcessCpuMetrics = + new ProcessCpuMetrics(sigar, instrumentFactory) +} diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala deleted file mode 100644 index 607ebe13..00000000 --- a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarLoader.scala +++ /dev/null @@ -1,203 +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.sigar - -import java.io._ -import java.text.SimpleDateFormat -import java.util -import java.util.logging.Logger -import java.util.{ ArrayList, Date, List } - -import org.hyperic.sigar._ - -import scala.annotation.tailrec -import scala.collection.JavaConversions._ -import scala.io.Source - -object SigarHolder { - private lazy val sigarProxy = SigarLoader.sigarProxy - def instance() = sigarProxy -} - -object SigarLoader { - - val Version = "1.6.4" - val JavaLibraryPath = "java.library.path" - val TmpDir = "java.io.tmpdir" - val IndexFile = "/kamon/system/sigar/index" - val UsrPathField = "usr_paths" - - private val log = Logger.getLogger("SigarLoader") - - def sigarProxy = init(new File(System.getProperty(TmpDir))) - - private[sigar] def init(baseTmp: File): SigarProxy = { - val tmpDir = createTmpDir(baseTmp) - for (lib ← loadIndex) copy(lib, tmpDir) - - attachToLibraryPath(tmpDir) - - try { - val sigar = new Sigar() - printBanner(sigar) - sigar - } catch { - case t: Throwable ⇒ { - log.severe("Failed to load sigar") - throw new RuntimeException(t) - } - } - } - - private[sigar] val usrPathField = { - val usrPathField = classOf[ClassLoader].getDeclaredField(UsrPathField) - usrPathField.setAccessible(true) - usrPathField - } - - private[sigar] def attachToLibraryPath(dir: File): Unit = { - val dirAbsolute = dir.getAbsolutePath - System.setProperty(JavaLibraryPath, newLibraryPath(dirAbsolute)) - var paths = usrPathField.get(null).asInstanceOf[Array[String]] - if (paths == null) paths = new Array[String](0) - for (path ← paths) if (path == dirAbsolute) return - val newPaths = util.Arrays.copyOf(paths, paths.length + 1) - newPaths(newPaths.length - 1) = dirAbsolute - usrPathField.set(null, newPaths) - } - - private[sigar] def newLibraryPath(dirAbsolutePath: String): String = { - Option(System.getProperty(JavaLibraryPath)).fold(dirAbsolutePath)(oldValue ⇒ s"$dirAbsolutePath${File.pathSeparator}$oldValue") - } - - private[sigar] def copy(lib: String, tmpDir: File) { - val target = new File(tmpDir, lib) - if (target.exists()) return - write(classOf[Loader].getResourceAsStream(lib), target) - } - - private[sigar] def createTmpDir(baseTmp: File): File = { - val tmpDir = new File(baseTmp, s"sigar-$Version") - if (!tmpDir.exists()) { - if (!tmpDir.mkdirs()) throw new RuntimeException(s"Could not create temp sigar directory: ${tmpDir.getAbsolutePath}") - } - if (!tmpDir.isDirectory) throw new RuntimeException(s"sigar temp directory path is not a directory: ${tmpDir.getAbsolutePath}") - if (!tmpDir.canWrite()) throw new RuntimeException(s"sigar temp directory not writeable: ${tmpDir.getAbsolutePath}") - tmpDir - } - - private[sigar] def loadIndex(): List[String] = { - val libs = new ArrayList[String]() - val is = classOf[Loader].getResourceAsStream(IndexFile) - - for (line ← Source.fromInputStream(is).getLines()) { - val currentLine = line.trim() - libs add currentLine - } - libs - } - - private[sigar] def write(input: InputStream, to: File) { - val out = new FileOutputStream(to) - try { - transfer(input, out) - } finally { - out.close() - } - } - - private[sigar] def transfer(input: InputStream, out: OutputStream) { - val buffer = new Array[Byte](8192) - - @tailrec def transfer() { - val read = input.read(buffer) - if (read >= 0) { - out.write(buffer, 0, read) - transfer() - } - } - transfer() - } - - private[sigar] def printBanner(sigar: Sigar) = { - 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 Loader private[sigar] -} 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..e68b0ede --- /dev/null +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala @@ -0,0 +1,75 @@ +/* + * ========================================================================================= + * Copyright © 2013-2015 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.system.sigar + +import akka.actor.{ Props, Actor } +import kamon.Kamon +import kamon.metric.instrument.InstrumentFactory +import kamon.metric.{ Entity, EntityRecorder, MetricsExtension } +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 + + val sigarMetrics = List( + CpuMetrics.register(sigar, metricsExtension), + FileSystemMetrics.register(sigar, metricsExtension), + LoadAverageMetrics.register(sigar, metricsExtension), + MemoryMetrics.register(sigar, metricsExtension), + NetworkMetrics.register(sigar, metricsExtension), + ProcessCpuMetrics.register(sigar, metricsExtension)) + + val refreshSchedule = context.system.scheduler.schedule(refreshInterval, refreshInterval, self, UpdateSigarMetrics)(context.dispatcher) + + def receive = { + case UpdateSigarMetrics ⇒ updateMetrics() + } + + def updateMetrics(): Unit = { + sigarMetrics.foreach(_.update()) + } + + 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(): Unit +} + +abstract class SigarMetricRecorderCompanion(metricName: String) { + def register(sigar: Sigar, metricsExtension: MetricsExtension): SigarMetric = { + val instrumentFactory = metricsExtension.instrumentFactory("system-metric") + metricsExtension.register(Entity(metricName, "system-metric"), apply(sigar, instrumentFactory)).recorder + } + + def apply(sigar: Sigar, instrumentFactory: InstrumentFactory): SigarMetric +} + diff --git a/kamon-system-metrics/src/test/scala/kamon/metrics/RedirectLogging.scala b/kamon-system-metrics/src/test/scala/kamon/metrics/RedirectLogging.scala new file mode 100644 index 00000000..fbf42cf0 --- /dev/null +++ b/kamon-system-metrics/src/test/scala/kamon/metrics/RedirectLogging.scala @@ -0,0 +1,34 @@ +/* ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.metric + +import java.util.logging.LogManager +import org.slf4j.bridge.SLF4JBridgeHandler + +/** + * Redirect different logging sources to SLF4J. + */ +trait RedirectLogging { + + def redirectLogging(): Unit = { + // Redirect JUL to SLF4J. + LogManager.getLogManager().reset(); + SLF4JBridgeHandler.install(); + } + + redirectLogging() + +} 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 4f7867ed..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,362 +15,140 @@ package kamon.metric -import akka.actor.ActorSystem -import akka.testkit.{ TestKitBase, TestProbe } +import java.lang.management.ManagementFactory + import com.typesafe.config.ConfigFactory -import kamon.Kamon -import kamon.metric.Subscriptions.TickMetricSnapshot -import kamon.metrics.CPUMetrics.CPUMetricSnapshot -import kamon.metrics.ContextSwitchesMetrics.ContextSwitchesMetricsSnapshot -import kamon.metrics.GCMetrics.GCMetricSnapshot -import kamon.metrics.HeapMetrics.HeapMetricSnapshot -import kamon.metrics.MemoryMetrics.MemoryMetricSnapshot -import kamon.metrics.NetworkMetrics.NetworkMetricSnapshot -import kamon.metrics.ProcessCPUMetrics.ProcessCPUMetricsSnapshot -import kamon.metrics._ -import kamon.system.SystemMetricsExtension -import org.scalatest.{ Matchers, WordSpecLike } - -import scala.concurrent.duration._ - -class SystemMetricsSpec extends TestKitBase with WordSpecLike with Matchers { - implicit lazy val system: ActorSystem = ActorSystem("system-metrics-spec", ConfigFactory.parseString( - """ - |akka { - | extensions = ["kamon.system.SystemMetrics"] - |} - | - |kamon.metrics { - | - | disable-aspectj-weaver-missing-error = true - | - | tick-interval = 1 second - | - | system { - | cpu { - | user { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | system { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | wait { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | idle { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | } - | process-cpu { - | user { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | system { - | highest-trackable-value = 999999999 - | significant-value-digits = 2 - | } - | } - | memory { - | used { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | free { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | buffer { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | cache { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | swap-used { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | swap-free { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | } - | context-switches { - | per-process-voluntary { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | per-process-non-voluntary { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | global { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | } - | network { - | rx-bytes { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | tx-bytes { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | rx-errors { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | tx-errors { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | } - | heap { - | used { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | max { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | committed { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | } - | gc { - | count { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | time { - | highest-trackable-value = 3600000000000 - | significant-value-digits = 2 - | } - | } - | } - |} - """.stripMargin)) - - "the Kamon CPU Metrics" should { - "record user, system, wait, idle metrics" in new CPUMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val CPUMetrics = expectCPUMetrics(metricsListener, 3 seconds) - CPUMetrics.user.max should be >= 0L - CPUMetrics.system.max should be >= 0L - CPUMetrics.cpuWait.max should be >= 0L - CPUMetrics.idle.max should be >= 0L - } - } - "the Kamon GC Metrics" should { - "record count, time metrics" in new GCMetricsListenerFixture { - val metricsListener = subscribeToMetrics() +import kamon.system.jmx.GarbageCollectionMetrics +import kamon.testkit.BaseKamonSpec +import scala.collection.JavaConverters._ - val GCMetrics = expectGCMetrics(metricsListener, 3 seconds) - GCMetrics.count.max should be > 0L - GCMetrics.time.max should be > 0L - } - } +class SystemMetricsSpec extends BaseKamonSpec("system-metrics-spec") with RedirectLogging { - "the Kamon Heap Metrics" should { - "record used, max, commited metrics" in new HeapMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + override lazy val config = + ConfigFactory.parseString( + """ + |kamon.metric { + | tick-interval = 1 hour + |} + | + |akka { + | extensions = ["kamon.system.SystemMetrics"] + |} + """.stripMargin) - val HeapMetrics = expectHeapMetrics(metricsListener, 3 seconds) - HeapMetrics.used.max should be >= 0L - HeapMetrics.max.max should be >= 0L - HeapMetrics.committed.max should be >= 0L - } - } + override protected def beforeAll(): Unit = + Thread.sleep(2000) // Give some room to the recorders to store some values. - "the Kamon Memory Metrics" should { - "record used, free, buffer, cache, swap used, swap free metrics" in new MemoryMetricsListenerFixture { - val metricsListener = subscribeToMetrics() - - val MemoryMetrics = expectMemoryMetrics(metricsListener, 3 seconds) - MemoryMetrics.used.max should be >= 0L - MemoryMetrics.free.max should be >= 0L - MemoryMetrics.buffer.max should be >= 0L - MemoryMetrics.cache.max should be >= 0L - MemoryMetrics.swapUsed.max should be >= 0L - MemoryMetrics.swapFree.max should be >= 0L + "the Kamon System Metrics module" should { + "record user, system, wait, idle and stolen CPU metrics" in { + val cpuMetrics = takeSnapshotOf("cpu", "system-metric") + + 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 } - } - "the Kamon Network Metrics" should { - "record rxBytes, txBytes, rxErrors, txErrors metrics" in new NetworkMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + "record count and time garbage collection metrics" in { + val availableGarbageCollectors = ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) - 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 + for (collectorName ← availableGarbageCollectors) { + val sanitizedName = GarbageCollectionMetrics.sanitizeCollectorName(collectorName.getName) + val collectorMetrics = takeSnapshotOf(s"$sanitizedName-garbage-collector", "system-metric") + + collectorMetrics.gauge("garbage-collection-count").get.numberOfMeasurements should be > 0L + collectorMetrics.gauge("garbage-collection-time").get.numberOfMeasurements should be > 0L + } } - } - "the Kamon Process CPU Metrics" should { - "record Cpu Percent, Total Process Time metrics" in new ProcessCPUMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + "record used, max and committed heap metrics" in { + val heapMetrics = takeSnapshotOf("heap-memory", "system-metric") - val ProcessCPUMetrics = expectProcessCPUMetrics(metricsListener, 3 seconds) - ProcessCPUMetrics.cpuPercent.max should be > 0L - ProcessCPUMetrics.totalProcessTime.max should be > 0L + 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 } - } - "the Kamon ContextSwitches Metrics" should { - "record Context Switches Global, Voluntary and Non Voluntary metrics" in new ContextSwitchesMetricsListenerFixture { - val metricsListener = subscribeToMetrics() + "record used, max and committed non-heap metrics" in { + val nonHeapMetrics = takeSnapshotOf("non-heap-memory", "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 + 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 } - } - 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 daemon, count and peak jvm threads metrics" in { + val threadsMetrics = takeSnapshotOf("threads", "system-metric") - 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 + 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 } - } - def expectGCMetrics(listener: TestProbe, waitTime: FiniteDuration): GCMetricSnapshot = { - val tickSnapshot = within(waitTime) { - listener.expectMsgType[TickMetricSnapshot] + "record loaded, unloaded and current class loading metrics" in { + val classLoadingMetrics = takeSnapshotOf("class-loading", "system-metric") + + 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 gcMetricsOption = tickSnapshot.metrics.get(GCMetrics(SystemMetricsExtension.garbageCollectors(0).getName)) - gcMetricsOption should not be empty - gcMetricsOption.get.asInstanceOf[GCMetricSnapshot] - } + "record reads, writes, queue time and service time file system metrics" in { + val fileSystemMetrics = takeSnapshotOf("file-system", "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 + fileSystemMetrics.histogram("file-system-reads").get.numberOfMeasurements should be > 0L + fileSystemMetrics.histogram("file-system-writes").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 1 minute, 5 minutes and 15 minutes metrics load average metrics" in { + val loadAverage = takeSnapshotOf("load-average", "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 + 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 } - } - 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 used, free, swap used, swap free system memory metrics" in { + val memoryMetrics = takeSnapshotOf("memory", "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 + 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 } - } - 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 rxBytes, txBytes, rxErrors, txErrors, rxDropped, txDropped network metrics" in { + val networkMetrics = takeSnapshotOf("network", "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 + 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 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 system and user CPU percentage for the application process" in { + val processCpuMetrics = takeSnapshotOf("process-cpu", "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 + 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 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] - } + "record Context Switches Global, Voluntary and Non Voluntary metrics when running on Linux" in { + if (isLinux) { + val contextSwitchesMetrics = takeSnapshotOf("context-switches", "system-metric") - 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 + 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 isLinux: Boolean = + System.getProperty("os.name").indexOf("Linux") != -1 + } |