From 6b196b2626f618e452a25e9437c944ff4138636c Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Tue, 5 Dec 2017 02:11:34 +0100 Subject: rename MinMaxCounter to RangeSampler --- .../test/scala/kamon/metric/MetricLookupSpec.scala | 20 ++-- .../scala/kamon/metric/MinMaxCounterSpec.scala | 90 ---------------- .../test/scala/kamon/metric/RangeSamplerSpec.scala | 90 ++++++++++++++++ .../scala/kamon/metric/RecorderRegistrySpec.scala | 58 ----------- .../metric/instrument/InstrumentFactorySpec.scala | 114 --------------------- kamon-core/src/main/resources/reference.conf | 6 +- kamon-core/src/main/scala/kamon/Kamon.scala | 6 +- .../scala/kamon/metric/InstrumentFactory.scala | 28 ++--- .../src/main/scala/kamon/metric/Metric.scala | 28 ++--- .../src/main/scala/kamon/metric/MetricLookup.scala | 18 ++-- .../main/scala/kamon/metric/MetricRegistry.scala | 10 +- .../main/scala/kamon/metric/MinMaxCounter.scala | 88 ---------------- .../src/main/scala/kamon/metric/RangeSampler.scala | 88 ++++++++++++++++ .../src/main/scala/kamon/metric/TickSnapshot.scala | 2 +- .../scala/kamon/testkit/MetricInspection.scala | 8 +- 15 files changed, 241 insertions(+), 413 deletions(-) delete mode 100644 kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala create mode 100644 kamon-core-tests/src/test/scala/kamon/metric/RangeSamplerSpec.scala delete mode 100644 kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala delete mode 100644 kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala delete mode 100644 kamon-core/src/main/scala/kamon/metric/MinMaxCounter.scala create mode 100644 kamon-core/src/main/scala/kamon/metric/RangeSampler.scala diff --git a/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala index 4df2397b..f6c27b5b 100644 --- a/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala +++ b/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala @@ -42,10 +42,10 @@ class MetricLookupSpec extends WordSpec with Matchers { gaugeOne shouldBe theSameInstanceAs(gaugeTwo) } - "always return the same min-max-counter metric" in { - val minMaxCounterOne = Kamon.minMaxCounter("min-max-counter-lookup") - val minMaxCounterTwo = Kamon.minMaxCounter("min-max-counter-lookup") - minMaxCounterOne shouldBe theSameInstanceAs(minMaxCounterTwo) + "always return the same range sampler metric" in { + val rangeSamplerOne = Kamon.rangeSampler("range-sampler-lookup") + val rangeSamplerTwo = Kamon.rangeSampler("range-sampler-lookup") + rangeSamplerOne shouldBe theSameInstanceAs(rangeSamplerTwo) } } @@ -77,13 +77,13 @@ class MetricLookupSpec extends WordSpec with Matchers { gaugeOne shouldBe theSameInstanceAs(gaugeThree) } - "always return the same min-max-counter for a set of tags" in { - val minMaxCounterOne = Kamon.minMaxCounter("min-max-counter-lookup").refine("tag" -> "value") - val minMaxCounterTwo = Kamon.minMaxCounter("min-max-counter-lookup").refine("tag" -> "value") - val minMaxCounterThree = Kamon.minMaxCounter("min-max-counter-lookup").refine(javaMap("tag", "value")) + "always return the same range-sampler for a set of tags" in { + val rangeSamplerOne = Kamon.rangeSampler("range-sampler-lookup").refine("tag" -> "value") + val rangeSamplerTwo = Kamon.rangeSampler("range-sampler-lookup").refine("tag" -> "value") + val rangeSamplerThree = Kamon.rangeSampler("range-sampler-lookup").refine(javaMap("tag", "value")) - minMaxCounterOne shouldBe theSameInstanceAs(minMaxCounterTwo) - minMaxCounterOne shouldBe theSameInstanceAs(minMaxCounterThree) + rangeSamplerOne shouldBe theSameInstanceAs(rangeSamplerTwo) + rangeSamplerOne shouldBe theSameInstanceAs(rangeSamplerThree) } } } diff --git a/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala deleted file mode 100644 index 0ad3c45c..00000000 --- a/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala +++ /dev/null @@ -1,90 +0,0 @@ -/* ========================================================================================= - * Copyright © 2013-2017 the kamon project - * - * 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.time.Duration - -import org.scalatest.{Matchers, WordSpec} - -case class TemporalBucket(value: Long, frequency: Long) extends Bucket - -class MinMaxCounterSpec extends WordSpec with Matchers { - - "a MinMaxCounter" should { - "track ascending tendencies" in { - val mmCounter = buildMinMaxCounter("track-ascending") - mmCounter.increment() - mmCounter.increment(3) - mmCounter.increment() - - mmCounter.sample() - - val snapshot = mmCounter.snapshot() - - snapshot.distribution.min should be(0) - snapshot.distribution.max should be(5) - } - - "track descending tendencies" in { - val mmCounter = buildMinMaxCounter("track-descending") - mmCounter.increment(5) - mmCounter.decrement() - mmCounter.decrement(3) - mmCounter.decrement() - - mmCounter.sample() - - val snapshot = mmCounter.snapshot() - snapshot.distribution.min should be(0) - snapshot.distribution.max should be(5) - } - - "reset the min and max to the current value after taking a snapshot" in { - val mmCounter = buildMinMaxCounter("reset-min-max-to-current") - - mmCounter.increment(5) - mmCounter.decrement(3) - mmCounter.sample() - - val firstSnapshot = mmCounter.snapshot() - firstSnapshot.distribution.min should be(0) - firstSnapshot.distribution.max should be(5) - - mmCounter.sample() - - val secondSnapshot = mmCounter.snapshot() - secondSnapshot.distribution.min should be(2) - secondSnapshot.distribution.max should be(2) - } - - "report zero as the min and current values if the current value fell bellow zero" in { - val mmCounter = buildMinMaxCounter("report-zero") - - mmCounter.decrement(3) - - mmCounter.sample() - - val snapshot = mmCounter.snapshot() - - snapshot.distribution.min should be(0) - snapshot.distribution.max should be(0) - } - } - - def buildMinMaxCounter(name: String, tags: Map[String, String] = Map.empty, unit: MeasurementUnit = MeasurementUnit.none): SimpleMinMaxCounter = - new SimpleMinMaxCounter(name, tags, new AtomicHdrHistogram(name, tags, unit, dynamicRange = DynamicRange.Default), Duration.ofMillis(100)) -} \ No newline at end of file diff --git a/kamon-core-tests/src/test/scala/kamon/metric/RangeSamplerSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/RangeSamplerSpec.scala new file mode 100644 index 00000000..3aaf57a4 --- /dev/null +++ b/kamon-core-tests/src/test/scala/kamon/metric/RangeSamplerSpec.scala @@ -0,0 +1,90 @@ +/* ========================================================================================= + * Copyright © 2013-2017 the kamon project + * + * 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.time.Duration + +import org.scalatest.{Matchers, WordSpec} + +case class TemporalBucket(value: Long, frequency: Long) extends Bucket + +class RangeSamplerSpec extends WordSpec with Matchers { + + "a RangeSampler" should { + "track ascending tendencies" in { + val rangeSampler = buildRangeSampler("track-ascending") + rangeSampler.increment() + rangeSampler.increment(3) + rangeSampler.increment() + + rangeSampler.sample() + + val snapshot = rangeSampler.snapshot() + + snapshot.distribution.min should be(0) + snapshot.distribution.max should be(5) + } + + "track descending tendencies" in { + val rangeSampler = buildRangeSampler("track-descending") + rangeSampler.increment(5) + rangeSampler.decrement() + rangeSampler.decrement(3) + rangeSampler.decrement() + + rangeSampler.sample() + + val snapshot = rangeSampler.snapshot() + snapshot.distribution.min should be(0) + snapshot.distribution.max should be(5) + } + + "reset the min and max to the current value after taking a snapshot" in { + val rangeSampler = buildRangeSampler("reset-range-sampler-to-current") + + rangeSampler.increment(5) + rangeSampler.decrement(3) + rangeSampler.sample() + + val firstSnapshot = rangeSampler.snapshot() + firstSnapshot.distribution.min should be(0) + firstSnapshot.distribution.max should be(5) + + rangeSampler.sample() + + val secondSnapshot = rangeSampler.snapshot() + secondSnapshot.distribution.min should be(2) + secondSnapshot.distribution.max should be(2) + } + + "report zero as the min and current values if the current value fell bellow zero" in { + val rangeSampler = buildRangeSampler("report-zero") + + rangeSampler.decrement(3) + + rangeSampler.sample() + + val snapshot = rangeSampler.snapshot() + + snapshot.distribution.min should be(0) + snapshot.distribution.max should be(0) + } + } + + def buildRangeSampler(name: String, tags: Map[String, String] = Map.empty, unit: MeasurementUnit = MeasurementUnit.none): SimpleRangeSampler = + new SimpleRangeSampler(name, tags, new AtomicHdrHistogram(name, tags, unit, dynamicRange = DynamicRange.Default), Duration.ofMillis(100)) +} \ No newline at end of file diff --git a/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala deleted file mode 100644 index 1053aa5f..00000000 --- a/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* ========================================================================================= - * Copyright © 2013-2017 the kamon project - * - * 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 com.typesafe.config.ConfigFactory -import org.scalatest.{Matchers, WordSpec} - -//class RecorderRegistrySpec extends WordSpec with Matchers { -// private val testConfig = ConfigFactory.parseString( -// """ -// |kamon.metric.filters { -// | accept-unmatched = false -// | -// | my-category { -// | includes = ["**"] -// | excludes = ["excluded"] -// | } -// |} -// """.stripMargin -// ) -// private val recorderRegistry = new RecorderRegistryImpl(testConfig.withFallback(ConfigFactory.load())) -// -// -// "the RecorderRegistry" should { -// "create entity recorders as requested and always return the same instance for a given entity" in { -// val myFirstEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty)) -// val mySecondEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty)) -// mySecondEntityRecorder shouldBe theSameInstanceAs(myFirstEntityRecorder) -// } -// -// "properly advice regarding entity filtering read from configuration" in { -// recorderRegistry.shouldTrack(Entity("my-entity", "my-category", Map.empty)) shouldBe true -// recorderRegistry.shouldTrack(Entity("other-eny", "my-category", Map.empty)) shouldBe true -// recorderRegistry.shouldTrack(Entity("excluded", "my-category", Map.empty)) shouldBe false -// } -// -// "allow removing entities" in { -// val myFirstEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty)) -// recorderRegistry.removeRecorder(Entity("my-entity", "my-category", Map.empty)) -// -// val mySecondEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty)) -// mySecondEntityRecorder shouldNot be theSameInstanceAs(myFirstEntityRecorder) -// } -// } -//} diff --git a/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala deleted file mode 100644 index 21fe2b4d..00000000 --- a/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala +++ /dev/null @@ -1,114 +0,0 @@ -package kamon.metric.instrument - -//import java.time.Duration -// -//import com.typesafe.config.ConfigFactory -//import kamon.metric.Entity -//import org.scalatest.{Matchers, WordSpec} -// -//class InstrumentFactorySpec extends WordSpec with Matchers{ -// val testEntity = Entity("test", "test-category", Map.empty) -// val customEntity = Entity("test", "custom-category", Map.empty) -// val baseConfiguration = ConfigFactory.parseString( -// """ -// |kamon.metric.instrument-factory { -// | default-settings { -// | histogram { -// | lowest-discernible-value = 100 -// | highest-trackable-value = 5000 -// | significant-value-digits = 2 -// | } -// | -// | min-max-counter { -// | lowest-discernible-value = 200 -// | highest-trackable-value = 6000 -// | significant-value-digits = 3 -// | sample-interval = 647 millis -// | } -// | } -// | -// | custom-settings { -// | -// | } -// |} -// """.stripMargin -// ) -// -// -// "the metrics InstrumentFactory" should { -// "create instruments using the default configuration settings" in { -// val factory = InstrumentFactory.fromConfig(baseConfiguration) -// val histogram = factory.buildHistogram(testEntity, "my-histogram") -// val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter") -// -// histogram.dynamicRange.lowestDiscernibleValue shouldBe(100) -// histogram.dynamicRange.highestTrackableValue shouldBe(5000) -// histogram.dynamicRange.significantValueDigits shouldBe(2) -// -// mmCounter.dynamicRange.lowestDiscernibleValue shouldBe(200) -// mmCounter.dynamicRange.highestTrackableValue shouldBe(6000) -// mmCounter.dynamicRange.significantValueDigits shouldBe(3) -// mmCounter.sampleInterval shouldBe(Duration.ofMillis(647)) -// } -// -// "accept custom settings when building instruments" in { -// val factory = InstrumentFactory.fromConfig(baseConfiguration) -// val histogram = factory.buildHistogram(testEntity, "my-histogram", DynamicRange.Loose) -// val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter", DynamicRange.Fine, Duration.ofMillis(500)) -// -// histogram.dynamicRange shouldBe(DynamicRange.Loose) -// -// mmCounter.dynamicRange shouldBe(DynamicRange.Fine) -// mmCounter.sampleInterval shouldBe(Duration.ofMillis(500)) -// } -// -// "allow overriding any default and provided settings via the custom-settings configuration key" in { -// val customConfig = ConfigFactory.parseString( -// """ -// |kamon.metric.instrument-factory.custom-settings { -// | custom-category { -// | modified-histogram { -// | lowest-discernible-value = 99 -// | highest-trackable-value = 999 -// | significant-value-digits = 4 -// | } -// | -// | modified-mm-counter { -// | lowest-discernible-value = 784 -// | highest-trackable-value = 14785 -// | significant-value-digits = 1 -// | sample-interval = 3 seconds -// | } -// | } -// |} -// """.stripMargin -// ).withFallback(baseConfiguration) -// -// val factory = InstrumentFactory.fromConfig(customConfig) -// val defaultHistogram = factory.buildHistogram(customEntity, "default-histogram") -// val modifiedHistogram = factory.buildHistogram(customEntity, "modified-histogram", DynamicRange.Loose) -// -// defaultHistogram.dynamicRange.lowestDiscernibleValue shouldBe(100) -// defaultHistogram.dynamicRange.highestTrackableValue shouldBe(5000) -// defaultHistogram.dynamicRange.significantValueDigits shouldBe(2) -// -// modifiedHistogram.dynamicRange.lowestDiscernibleValue shouldBe(99) -// modifiedHistogram.dynamicRange.highestTrackableValue shouldBe(999) -// modifiedHistogram.dynamicRange.significantValueDigits shouldBe(4) -// -// -// val defaultMMCounter = factory.buildMinMaxCounter(customEntity, "default-mm-counter") -// val modifiedMMCounter = factory.buildMinMaxCounter(customEntity, "modified-mm-counter", DynamicRange.Loose) -// -// defaultMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(200) -// defaultMMCounter.dynamicRange.highestTrackableValue shouldBe(6000) -// defaultMMCounter.dynamicRange.significantValueDigits shouldBe(3) -// defaultMMCounter.sampleInterval shouldBe(Duration.ofMillis(647)) -// -// modifiedMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(784) -// modifiedMMCounter.dynamicRange.highestTrackableValue shouldBe(14785) -// modifiedMMCounter.dynamicRange.significantValueDigits shouldBe(1) -// modifiedMMCounter.sampleInterval shouldBe(Duration.ofSeconds(3)) -// } -// } -//} diff --git a/kamon-core/src/main/resources/reference.conf b/kamon-core/src/main/resources/reference.conf index a779ad02..af767f60 100644 --- a/kamon-core/src/main/resources/reference.conf +++ b/kamon-core/src/main/resources/reference.conf @@ -18,7 +18,7 @@ kamon { # Example: `reporters = ["kamon.statsd.StatsD", "kamon.zipkin.Zipkin"]`. reporters = [ ] - # Pool size for the executor service that will run sampling on MinMaxCounter instruments. This scheduler is accesible + # Pool size for the executor service that will run sampling on RangeSampler instruments. This scheduler is accesible # through Kamon.scheduler() scheduler-pool-size = 2 @@ -36,7 +36,7 @@ kamon { optimistic-tick-alignment = yes # Thread pool size used by the metrics refresh scheduler. This pool is only used to periodically sampling - # min-max-counter values. + # range-sampler values. refresh-scheduler-pool-size = 2 instrument-factory { @@ -56,7 +56,7 @@ kamon { significant-value-digits = 2 } - min-max-counter { + range-sampler { lowest-discernible-value = 1 highest-trackable-value = 3600000000000 significant-value-digits = 2 diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala index 1a9dbe95..c9a2e415 100644 --- a/kamon-core/src/main/scala/kamon/Kamon.scala +++ b/kamon-core/src/main/scala/kamon/Kamon.scala @@ -83,9 +83,9 @@ object Kamon extends MetricLookup with ReporterRegistry with Tracer { override def gauge(name: String, unit: MeasurementUnit): GaugeMetric = _metrics.gauge(name, unit) - override def minMaxCounter(name: String, unit: MeasurementUnit, sampleInterval: Option[Duration], - dynamicRange: Option[DynamicRange]): MinMaxCounterMetric = - _metrics.minMaxCounter(name, unit, dynamicRange, sampleInterval) + override def rangeSampler(name: String, unit: MeasurementUnit, sampleInterval: Option[Duration], + dynamicRange: Option[DynamicRange]): RangeSamplerMetric = + _metrics.rangeSampler(name, unit, dynamicRange, sampleInterval) override def timer(name: String, dynamicRange: Option[DynamicRange]): TimerMetric = _metrics.timer(name, dynamicRange) diff --git a/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala b/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala index abab1207..7ae9b66c 100644 --- a/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala +++ b/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala @@ -24,19 +24,19 @@ import kamon.metric.InstrumentFactory.CustomInstrumentSettings import java.time.Duration -private[kamon] class InstrumentFactory private (defaultHistogramDynamicRange: DynamicRange, defaultMMCounterDynamicRange: DynamicRange, - defaultMMCounterSampleInterval: Duration, customSettings: Map[String, CustomInstrumentSettings]) { +private[kamon] class InstrumentFactory private (defaultHistogramDynamicRange: DynamicRange, defaultRangeSamplerDynamicRange: DynamicRange, + defaultRangeSamplerSampleInterval: Duration, customSettings: Map[String, CustomInstrumentSettings]) { def buildHistogram(dynamicRange: Option[DynamicRange])(name: String, tags: Map[String, String], unit: MeasurementUnit): AtomicHdrHistogram = new AtomicHdrHistogram(name, tags, unit, instrumentDynamicRange(name, dynamicRange.getOrElse(defaultHistogramDynamicRange))) - def buildMinMaxCounter(dynamicRange: Option[DynamicRange], sampleInterval: Option[Duration]) - (name: String, tags: Map[String, String], unit: MeasurementUnit): SimpleMinMaxCounter = - new SimpleMinMaxCounter( + def buildRangeSampler(dynamicRange: Option[DynamicRange], sampleInterval: Option[Duration]) + (name: String, tags: Map[String, String], unit: MeasurementUnit): SimpleRangeSampler = + new SimpleRangeSampler( name, tags, - buildHistogram(dynamicRange.orElse(Some(defaultMMCounterDynamicRange)))(name, tags, unit), - instrumentSampleInterval(name, sampleInterval.getOrElse(defaultMMCounterSampleInterval))) + buildHistogram(dynamicRange.orElse(Some(defaultRangeSamplerDynamicRange)))(name, tags, unit), + instrumentSampleInterval(name, sampleInterval.getOrElse(defaultRangeSamplerSampleInterval))) def buildGauge(name: String, tags: Map[String, String], unit: MeasurementUnit): AtomicLongGauge = new AtomicLongGauge(name, tags, unit) @@ -68,24 +68,24 @@ object InstrumentFactory { case class InstrumentType(name: String) object InstrumentTypes { - val Histogram = InstrumentType("Histogram") - val MinMaxCounter = InstrumentType("MinMaxCounter") - val Counter = InstrumentType("Counter") - val Gauge = InstrumentType("Gauge") + val Histogram = InstrumentType("Histogram") + val RangeSampler = InstrumentType("RangeSampler") + val Counter = InstrumentType("Counter") + val Gauge = InstrumentType("Gauge") } def fromConfig(config: Config): InstrumentFactory = { val factoryConfig = config.getConfig("kamon.metric.instrument-factory") val histogramDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.histogram")) - val mmCounterDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.min-max-counter")) - val mmCounterSampleInterval = factoryConfig.getDuration("default-settings.min-max-counter.sample-interval") + val rangeSamplerDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.range-sampler")) + val rangeSamplerSampleInterval = factoryConfig.getDuration("default-settings.range-sampler.sample-interval") val customSettings = factoryConfig.getConfig("custom-settings") .configurations .filter(nonEmptySection) .map(readCustomInstrumentSettings) - new InstrumentFactory(histogramDynamicRange, mmCounterDynamicRange, mmCounterSampleInterval, customSettings) + new InstrumentFactory(histogramDynamicRange, rangeSamplerDynamicRange, rangeSamplerSampleInterval, customSettings) } private def nonEmptySection(entry: (String, Config)): Boolean = entry match { diff --git a/kamon-core/src/main/scala/kamon/metric/Metric.scala b/kamon-core/src/main/scala/kamon/metric/Metric.scala index db5ff9c0..f63e3d07 100644 --- a/kamon-core/src/main/scala/kamon/metric/Metric.scala +++ b/kamon-core/src/main/scala/kamon/metric/Metric.scala @@ -46,7 +46,7 @@ trait Metric[T] { trait HistogramMetric extends Metric[Histogram] with Histogram trait TimerMetric extends Metric[Timer] with Timer -trait MinMaxCounterMetric extends Metric[MinMaxCounter] with MinMaxCounter +trait RangeSamplerMetric extends Metric[RangeSampler] with RangeSampler trait GaugeMetric extends Metric[Gauge] with Gauge trait CounterMetric extends Metric[Counter] with Counter @@ -110,11 +110,11 @@ private[kamon] final class HistogramMetricImpl(val name: String, val unit: Measu instrument.asInstanceOf[AtomicHdrHistogram].snapshot(resetState = true) } -private[kamon] final class MinMaxCounterMetricImpl(val name: String, val unit: MeasurementUnit, customDynamicRange: Option[DynamicRange], +private[kamon] final class RangeSamplerMetricImpl(val name: String, val unit: MeasurementUnit, customDynamicRange: Option[DynamicRange], customSampleInterval: Option[Duration], factory: AtomicReference[InstrumentFactory], scheduler: ScheduledExecutorService) - extends BaseMetric[MinMaxCounter, MetricDistribution](MinMaxCounter) with MinMaxCounterMetric { + extends BaseMetric[RangeSampler, MetricDistribution](RangeSampler) with RangeSamplerMetric { - private val logger = LoggerFactory.getLogger(classOf[MinMaxCounterMetric]) + private val logger = LoggerFactory.getLogger(classOf[RangeSamplerMetric]) private val scheduledSamplers = TrieMap.empty[Tags, ScheduledFuture[_]] def dynamicRange: DynamicRange = @@ -138,13 +138,13 @@ private[kamon] final class MinMaxCounterMetricImpl(val name: String, val unit: M override def sample(): Unit = baseInstrument.sample() - override protected def createInstrument(tags: Tags): MinMaxCounter = { - val mmCounter = factory.get().buildMinMaxCounter(customDynamicRange, customSampleInterval)(name, tags, unit) - val sampleInterval = mmCounter.sampleInterval.toMillis - val scheduledFuture = scheduler.scheduleAtFixedRate(scheduledSampler(mmCounter), sampleInterval, sampleInterval, TimeUnit.MILLISECONDS) + override protected def createInstrument(tags: Tags): RangeSampler = { + val rangeSampler = factory.get().buildRangeSampler(customDynamicRange, customSampleInterval)(name, tags, unit) + val sampleInterval = rangeSampler.sampleInterval.toMillis + val scheduledFuture = scheduler.scheduleAtFixedRate(scheduledSampler(rangeSampler), sampleInterval, sampleInterval, TimeUnit.MILLISECONDS) scheduledSamplers.put(tags, scheduledFuture) - mmCounter + rangeSampler } override def remove(tags: JTags): Boolean = @@ -163,17 +163,17 @@ private[kamon] final class MinMaxCounterMetricImpl(val name: String, val unit: M val removed = super.remove(tags) if(removed) scheduledSamplers.get(tags).foreach(sf => { - Try(sf.cancel(false)).failed.foreach(_ => logger.error("Failed to cancel scheduled sampling for MinMaxCounter []", tags.prettyPrint())) + Try(sf.cancel(false)).failed.foreach(_ => logger.error("Failed to cancel scheduled sampling for RangeSampler []", tags.prettyPrint())) }) removed } - override protected def createSnapshot(instrument: MinMaxCounter): MetricDistribution = - instrument.asInstanceOf[SimpleMinMaxCounter].snapshot(resetState = true) + override protected def createSnapshot(instrument: RangeSampler): MetricDistribution = + instrument.asInstanceOf[SimpleRangeSampler].snapshot(resetState = true) - private def scheduledSampler(mmCounter: MinMaxCounter): Runnable = new Runnable { - override def run(): Unit = mmCounter.sample() + private def scheduledSampler(rangeSampler: RangeSampler): Runnable = new Runnable { + override def run(): Unit = rangeSampler.sample() } } diff --git a/kamon-core/src/main/scala/kamon/metric/MetricLookup.scala b/kamon-core/src/main/scala/kamon/metric/MetricLookup.scala index fcca04bd..c8984015 100644 --- a/kamon-core/src/main/scala/kamon/metric/MetricLookup.scala +++ b/kamon-core/src/main/scala/kamon/metric/MetricLookup.scala @@ -45,17 +45,17 @@ trait MetricLookup { gauge(name, MeasurementUnit.none) - def minMaxCounter(name: String): MinMaxCounterMetric = - minMaxCounter(name, MeasurementUnit.none, None, None) + def rangeSampler(name: String): RangeSamplerMetric = + rangeSampler(name, MeasurementUnit.none, None, None) - def minMaxCounter(name: String, unit: MeasurementUnit): MinMaxCounterMetric = - minMaxCounter(name, unit, None, None) + def rangeSampler(name: String, unit: MeasurementUnit): RangeSamplerMetric = + rangeSampler(name, unit, None, None) - def minMaxCounter(name: String, unit: MeasurementUnit, sampleInterval: Duration): MinMaxCounterMetric = - minMaxCounter(name, unit, Option(sampleInterval), None) + def rangeSampler(name: String, unit: MeasurementUnit, sampleInterval: Duration): RangeSamplerMetric = + rangeSampler(name, unit, Option(sampleInterval), None) - def minMaxCounter(name: String, unit: MeasurementUnit, sampleInterval: Duration, dynamicRange: DynamicRange): MinMaxCounterMetric = - minMaxCounter(name, unit, Option(sampleInterval), Option(dynamicRange)) + def rangeSampler(name: String, unit: MeasurementUnit, sampleInterval: Duration, dynamicRange: DynamicRange): RangeSamplerMetric = + rangeSampler(name, unit, Option(sampleInterval), Option(dynamicRange)) def histogram(name: String, unit: MeasurementUnit, dynamicRange: Option[DynamicRange]): HistogramMetric @@ -66,5 +66,5 @@ trait MetricLookup { def gauge(name: String, unit: MeasurementUnit): GaugeMetric - def minMaxCounter(name: String, unit: MeasurementUnit, sampleInterval: Option[Duration], dynamicRange: Option[DynamicRange]): MinMaxCounterMetric + def rangeSampler(name: String, unit: MeasurementUnit, sampleInterval: Option[Duration], dynamicRange: Option[DynamicRange]): RangeSamplerMetric } diff --git a/kamon-core/src/main/scala/kamon/metric/MetricRegistry.scala b/kamon-core/src/main/scala/kamon/metric/MetricRegistry.scala index ebc17834..cc111d95 100644 --- a/kamon-core/src/main/scala/kamon/metric/MetricRegistry.scala +++ b/kamon-core/src/main/scala/kamon/metric/MetricRegistry.scala @@ -50,8 +50,8 @@ class MetricRegistry(initialConfig: Config, scheduler: ScheduledExecutorService) def gauge(name: String, unit: MeasurementUnit): GaugeMetric = lookupMetric(name, unit, InstrumentTypes.Gauge)(new GaugeMetricImpl(name, unit, instrumentFactory)) - def minMaxCounter(name: String, unit: MeasurementUnit, dynamicRange: Option[DynamicRange], sampleInterval: Option[Duration]): MinMaxCounterMetric = - lookupMetric(name, unit, InstrumentTypes.MinMaxCounter)(new MinMaxCounterMetricImpl(name, unit, dynamicRange, sampleInterval, instrumentFactory, scheduler)) + def rangeSampler(name: String, unit: MeasurementUnit, dynamicRange: Option[DynamicRange], sampleInterval: Option[Duration]): RangeSamplerMetric = + lookupMetric(name, unit, InstrumentTypes.RangeSampler)(new RangeSamplerMetricImpl(name, unit, dynamicRange, sampleInterval, instrumentFactory, scheduler)) def timer(name: String, dynamicRange: Option[DynamicRange]): TimerMetric = new TimerMetricImpl(histogram(name, time.nanoseconds, dynamicRange)) @@ -59,21 +59,21 @@ class MetricRegistry(initialConfig: Config, scheduler: ScheduledExecutorService) override def snapshot(): MetricsSnapshot = synchronized { var histograms = Seq.empty[MetricDistribution] - var mmCounters = Seq.empty[MetricDistribution] + var rangeSamplers = Seq.empty[MetricDistribution] var counters = Seq.empty[MetricValue] var gauges = Seq.empty[MetricValue] for(metricEntry <- metrics.values) { metricEntry.instrumentType match { case InstrumentTypes.Histogram => histograms = histograms ++ metricEntry.snapshot().asInstanceOf[Seq[MetricDistribution]] - case InstrumentTypes.MinMaxCounter => mmCounters = mmCounters ++ metricEntry.snapshot().asInstanceOf[Seq[MetricDistribution]] + case InstrumentTypes.RangeSampler => rangeSamplers = rangeSamplers ++ metricEntry.snapshot().asInstanceOf[Seq[MetricDistribution]] case InstrumentTypes.Gauge => gauges = gauges ++ metricEntry.snapshot().asInstanceOf[Seq[MetricValue]] case InstrumentTypes.Counter => counters = counters ++ metricEntry.snapshot().asInstanceOf[Seq[MetricValue]] case other => logger.warn("Unexpected instrument type [{}] found in the registry", other ) } } - MetricsSnapshot(histograms, mmCounters, gauges, counters) + MetricsSnapshot(histograms, rangeSamplers, gauges, counters) } private def lookupMetric[T <: BaseMetric[_, _]](name: String, unit: MeasurementUnit, instrumentType: InstrumentType)(metricBuilder: => T): T = { diff --git a/kamon-core/src/main/scala/kamon/metric/MinMaxCounter.scala b/kamon-core/src/main/scala/kamon/metric/MinMaxCounter.scala deleted file mode 100644 index cdcf3ee9..00000000 --- a/kamon-core/src/main/scala/kamon/metric/MinMaxCounter.scala +++ /dev/null @@ -1,88 +0,0 @@ -/* ========================================================================================= - * Copyright © 2013-2017 the kamon project - * - * 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.lang.Math.abs -import java.time.Duration -import java.util.concurrent.atomic.AtomicLong - -trait MinMaxCounter { - def unit: MeasurementUnit - def dynamicRange: DynamicRange - def sampleInterval: Duration - - def increment(): Unit - def increment(times: Long): Unit - def decrement(): Unit - def decrement(times: Long): Unit - def sample(): Unit -} - -class SimpleMinMaxCounter(name: String, tags: Map[String, String], underlyingHistogram: AtomicHdrHistogram, - val sampleInterval: Duration) extends MinMaxCounter { - - private val min = AtomicLongMaxUpdater() - private val max = AtomicLongMaxUpdater() - private val sum = new AtomicLong() - - def dynamicRange: DynamicRange = - underlyingHistogram.dynamicRange - - def unit: MeasurementUnit = - underlyingHistogram.unit - - def increment(): Unit = - increment(1L) - - def increment(times: Long): Unit = { - val currentValue = sum.addAndGet(times) - max.update(currentValue) - } - - def decrement(): Unit = - decrement(1L) - - def decrement(times: Long): Unit = { - val currentValue = sum.addAndGet(-times) - min.update(-currentValue) - } - - def sample(): Unit = { - val currentValue = { - val value = sum.get() - if (value <= 0) 0 else value - } - - val currentMin = { - val rawMin = min.maxThenReset(-currentValue) - if (rawMin >= 0) - 0 - else - abs(rawMin) - } - - val currentMax = max.maxThenReset(currentValue) - - underlyingHistogram.record(currentValue) - underlyingHistogram.record(currentMin) - underlyingHistogram.record(currentMax) - } - - private[kamon] def snapshot(resetState: Boolean = true): MetricDistribution = { - sample() - underlyingHistogram.snapshot(resetState) - } -} diff --git a/kamon-core/src/main/scala/kamon/metric/RangeSampler.scala b/kamon-core/src/main/scala/kamon/metric/RangeSampler.scala new file mode 100644 index 00000000..2adecf45 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/metric/RangeSampler.scala @@ -0,0 +1,88 @@ +/* ========================================================================================= + * Copyright © 2013-2017 the kamon project + * + * 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.lang.Math.abs +import java.time.Duration +import java.util.concurrent.atomic.AtomicLong + +trait RangeSampler { + def unit: MeasurementUnit + def dynamicRange: DynamicRange + def sampleInterval: Duration + + def increment(): Unit + def increment(times: Long): Unit + def decrement(): Unit + def decrement(times: Long): Unit + def sample(): Unit +} + +class SimpleRangeSampler(name: String, tags: Map[String, String], underlyingHistogram: AtomicHdrHistogram, + val sampleInterval: Duration) extends RangeSampler { + + private val min = AtomicLongMaxUpdater() + private val max = AtomicLongMaxUpdater() + private val sum = new AtomicLong() + + def dynamicRange: DynamicRange = + underlyingHistogram.dynamicRange + + def unit: MeasurementUnit = + underlyingHistogram.unit + + def increment(): Unit = + increment(1L) + + def increment(times: Long): Unit = { + val currentValue = sum.addAndGet(times) + max.update(currentValue) + } + + def decrement(): Unit = + decrement(1L) + + def decrement(times: Long): Unit = { + val currentValue = sum.addAndGet(-times) + min.update(-currentValue) + } + + def sample(): Unit = { + val currentValue = { + val value = sum.get() + if (value <= 0) 0 else value + } + + val currentMin = { + val rawMin = min.maxThenReset(-currentValue) + if (rawMin >= 0) + 0 + else + abs(rawMin) + } + + val currentMax = max.maxThenReset(currentValue) + + underlyingHistogram.record(currentValue) + underlyingHistogram.record(currentMin) + underlyingHistogram.record(currentMax) + } + + private[kamon] def snapshot(resetState: Boolean = true): MetricDistribution = { + sample() + underlyingHistogram.snapshot(resetState) + } +} diff --git a/kamon-core/src/main/scala/kamon/metric/TickSnapshot.scala b/kamon-core/src/main/scala/kamon/metric/TickSnapshot.scala index 83176d4f..45666de1 100644 --- a/kamon-core/src/main/scala/kamon/metric/TickSnapshot.scala +++ b/kamon-core/src/main/scala/kamon/metric/TickSnapshot.scala @@ -28,7 +28,7 @@ case class Interval(from: Long, to: Long) case class MetricsSnapshot( histograms: Seq[MetricDistribution], - minMaxCounters: Seq[MetricDistribution], + rangeSamplers: Seq[MetricDistribution], gauges: Seq[MetricValue], counters: Seq[MetricValue] ) diff --git a/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala b/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala index be340df9..cc73191b 100644 --- a/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala +++ b/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala @@ -62,11 +62,11 @@ trait MetricInspection { } } - implicit class MinMaxCounterMetricSyntax(mmCounter: MinMaxCounter) { + implicit class RangeSamplerMetricSyntax(rangeSampler: RangeSampler) { def distribution(resetState: Boolean = true): Distribution = - mmCounter match { - case mmcm: MinMaxCounterMetric => mmcm.refine(Map.empty[String, String]).distribution(resetState) - case mmc: SimpleMinMaxCounter => mmc.snapshot(resetState).distribution + rangeSampler match { + case rsm: RangeSamplerMetric => rsm.refine(Map.empty[String, String]).distribution(resetState) + case rs: SimpleRangeSampler => rs.snapshot(resetState).distribution } } -- cgit v1.2.3