diff options
author | Eugene Platonov <jozic@live.com> | 2015-12-01 13:26:50 -0500 |
---|---|---|
committer | Eugene Platonov <jozic@live.com> | 2015-12-01 14:15:06 -0500 |
commit | 4a918ce6570129d3d4fd2d3e98d43f8078bb2d99 (patch) | |
tree | 9885a814379a7cc918607c7aa75ab8f8a3f8e6c5 /kamon-core/src/test/scala | |
parent | db5d0293dac1715bf585af42c0b86e668eba6803 (diff) | |
download | Kamon-4a918ce6570129d3d4fd2d3e98d43f8078bb2d99.tar.gz Kamon-4a918ce6570129d3d4fd2d3e98d43f8078bb2d99.tar.bz2 Kamon-4a918ce6570129d3d4fd2d3e98d43f8078bb2d99.zip |
+ core: provide generic way to scale time and memory metrics
Diffstat (limited to 'kamon-core/src/test/scala')
5 files changed, 302 insertions, 1 deletions
diff --git a/kamon-core/src/test/scala/kamon/metric/MetricScaleDecoratorSpec.scala b/kamon-core/src/test/scala/kamon/metric/MetricScaleDecoratorSpec.scala new file mode 100644 index 00000000..04821923 --- /dev/null +++ b/kamon-core/src/test/scala/kamon/metric/MetricScaleDecoratorSpec.scala @@ -0,0 +1,100 @@ +/* + * ========================================================================================= + * 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.metric + +import kamon.Kamon +import kamon.metric.SubscriptionsDispatcher.TickMetricSnapshot +import kamon.metric.instrument.{InstrumentFactory, Memory, Time, UnitOfMeasurement} +import kamon.testkit.BaseKamonSpec +import kamon.util.MilliTimestamp +import org.scalatest.OptionValues._ + +class MetricScaleDecoratorSpec extends BaseKamonSpec("metrics-scale-decorator-spec") with SnapshotFixtures { + "the MetricScaleDecorator" when { + "receives a snapshot" which { + + val scaleDecorator = system.actorOf(MetricScaleDecorator.props( + Some(Time.Milliseconds), Some(Memory.KiloBytes), testActor)) + "is empty" should { + "do nothing for empty snapshots" in { + scaleDecorator ! emptySnapshot + expectMsg(emptySnapshot) + } + } + "is non empty" should { + scaleDecorator ! nonEmptySnapshot + val scaled = expectMsgType[TickMetricSnapshot] + val snapshot = scaled.metrics(testEntity) + + "scale time metrics" in { + snapshot.histogram("nano-time").value.max should be(10L +- 1L) + snapshot.counter("micro-time").value.count should be(1000L) + } + "scale memory metrics" in { + snapshot.histogram("byte-memory").value.max should be(1) + snapshot.counter("kbyte-memory").value.count should be(100L) + } + "do nothing with unknown metrics" in { + snapshot.histogram("unknown-histogram").value.max should be(1000L) + snapshot.counter("unknown-counter").value.count should be(10L) + } + "not change from and to" in { + scaled.from.millis should be(1000) + scaled.to.millis should be(2000) + } + } + } + } +} + +trait SnapshotFixtures { + self: BaseKamonSpec => + + class ScaleDecoratorTestMetrics(instrumentFactory: InstrumentFactory) + extends GenericEntityRecorder(instrumentFactory) { + val nanoTime = histogram("nano-time", Time.Nanoseconds) + val microTime = counter("micro-time", Time.Microseconds) + val byteMemory = histogram("byte-memory", Memory.Bytes) + val kbyteMemory = counter("kbyte-memory", Memory.KiloBytes) + val unknownHistogram = histogram("unknown-histogram", UnitOfMeasurement.Unknown) + val unknownCounter = counter("unknown-counter", UnitOfMeasurement.Unknown) + } + + object ScaleDecoratorTestMetrics extends EntityRecorderFactory[ScaleDecoratorTestMetrics] { + override def category: String = "decorator-spec" + + override def createRecorder(instrumentFactory: InstrumentFactory): ScaleDecoratorTestMetrics = + new ScaleDecoratorTestMetrics(instrumentFactory) + } + + val testEntity = Entity("metrics-scale-decorator-spec", "decorator-spec") + val recorder = Kamon.metrics.entity(ScaleDecoratorTestMetrics, "metrics-scale-decorator-spec") + + val emptySnapshot = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map.empty) + + recorder.unknownCounter.increment(10) + recorder.unknownHistogram.record(1000L) + recorder.nanoTime.record(10000000L) + recorder.microTime.increment(1000000L) + recorder.byteMemory.record(1024L) + recorder.kbyteMemory.increment(100L) + + val nonEmptySnapshot = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map( + (testEntity -> recorder.collect(collectionContext)))) + +} + diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala index 094baf4c..850200d4 100644 --- a/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala @@ -56,6 +56,15 @@ class CounterSpec extends WordSpec with Matchers { counterBSnapshot.merge(counterASnapshot, collectionContext).count should be(300) } + "produce a snapshot that can be scaled" in new CounterFixture { + counter.increment(100) + + val counterSnapshot = takeSnapshotFrom(counter) + + val scaledSnapshot = counterSnapshot.scale(Time.Milliseconds, Time.Microseconds) + scaledSnapshot.count should be(100000) + } + } trait CounterFixture { diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala index 9a50e149..ff71cd56 100644 --- a/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala @@ -48,7 +48,7 @@ class HistogramSpec extends WordSpec with Matchers { } "produce a snapshot" which { - "supports min, max, percentile, sum and numberOfMeasurements operations" in new HistogramFixture { + "supports min, max, percentile, sum, numberOfMeasurements and recordsIterator operations" in new HistogramFixture { histogram.record(100) histogram.record(200, count = 200) histogram.record(300) @@ -64,6 +64,36 @@ class HistogramSpec extends WordSpec with Matchers { snapshot.sum should be(41300) snapshot.numberOfMeasurements should be(203) + val records = snapshot.recordsIterator.map(r => r.level -> r.count).toSeq + records.size should be (4) + records(0) should be(100 -> 1) + records(1) should be(200 -> 200) + records(2) should be(300 -> 1) + records(3) should be(900 -> 1) + } + + "can be scaled" in new HistogramFixture { + histogram.record(100) + histogram.record(200, count = 200) + histogram.record(300) + histogram.record(900) + + val snapshot = takeSnapshot().scale(Time.Seconds, Time.Milliseconds) + + snapshot.min should equal(100000L +- 1000L) + snapshot.max should equal(900000L +- 9000L) + snapshot.percentile(50.0D) should be(200000) + snapshot.percentile(99.5D) should be(300000) + snapshot.percentile(99.9D) should be(900000) + snapshot.sum should be(41300000) + snapshot.numberOfMeasurements should be(203) + + val records = snapshot.recordsIterator.map(r => r.level -> r.count).toSeq + records.size should be (4) + records(0) should be(100000 -> 1) + records(1) should be(200000 -> 200) + records(2) should be(300000 -> 1) + records(3) should be(900000 -> 1) } "can be merged with another snapshot" in new MultipleHistogramFixture { diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/UnitOfMeasurementSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/UnitOfMeasurementSpec.scala new file mode 100644 index 00000000..7133579e --- /dev/null +++ b/kamon-core/src/test/scala/kamon/metric/instrument/UnitOfMeasurementSpec.scala @@ -0,0 +1,98 @@ +/* + * ========================================================================================= + * 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.metric.instrument + +import kamon.metric.instrument.UnitOfMeasurement.Unknown +import org.scalatest.{Matchers, WordSpec} + +class UnitOfMeasurementSpec extends WordSpec with Matchers { + + "Time unit" should { + "resolve Time Unit by valid name" in { + Time("s") should be(Time.Seconds) + Time("n") should be(Time.Nanoseconds) + Time("ms") should be(Time.Milliseconds) + Time("µs") should be(Time.Microseconds) + } + "fail to resolve Time Unit by invalid name" in { + val ex = intercept[IllegalArgumentException](Time("boo")) + ex.getMessage should be("Can't recognize time unit 'boo'") + } + "scale time properly" in { + val epsilon = 0.0001 + + Time.Nanoseconds.scale(Time.Nanoseconds)(1000000D) should be(1000000D +- epsilon) + Time.Nanoseconds.scale(Time.Microseconds)(1000000D) should be(1000D +- epsilon) + Time.Nanoseconds.scale(Time.Milliseconds)(1000000D) should be(1D +- epsilon) + Time.Nanoseconds.scale(Time.Seconds)(1000000D) should be(0.001D +- epsilon) + Time.Seconds.scale(Time.Nanoseconds)(1D) should be(1000000000D +- epsilon) + } + "allow scale only time" in { + intercept[IllegalArgumentException](Time.Nanoseconds.tryScale(Unknown)(100)) + .getMessage should be("Can't scale different types of units `time` and `unknown`") + intercept[IllegalArgumentException](Time.Nanoseconds.tryScale(Memory.Bytes)(100)) + .getMessage should be("Can't scale different types of units `time` and `bytes`") + val epsilon = 0.0001 + + Time.Nanoseconds.tryScale(Time.Nanoseconds)(100D) should be(100D +- epsilon) + } + } + + "Memory unit" should { + "resolve Memory Unit by valid name" in { + Memory("b") should be(Memory.Bytes) + Memory("Kb") should be(Memory.KiloBytes) + Memory("Mb") should be(Memory.MegaBytes) + Memory("Gb") should be(Memory.GigaBytes) + } + "fail to resolve Memory Unit by invalid name" in { + val ex = intercept[IllegalArgumentException](Memory("boo")) + ex.getMessage should be("Can't recognize memory unit 'boo'") + } + "scale memory properly" in { + val epsilon = 0.0001 + + Memory.Bytes.scale(Memory.Bytes)(1000000D) should be(1000000D +- epsilon) + Memory.Bytes.scale(Memory.KiloBytes)(1000000D) should be(976.5625D +- epsilon) + Memory.Bytes.scale(Memory.MegaBytes)(1000000D) should be(0.9536D +- epsilon) + Memory.Bytes.scale(Memory.GigaBytes)(1000000D) should be(9.3132E-4D +- epsilon) + Memory.MegaBytes.scale(Memory.Bytes)(1D) should be(1048576D +- epsilon) + } + "allow scale only memory" in { + intercept[IllegalArgumentException](Memory.Bytes.tryScale(Unknown)(100)) + .getMessage should be("Can't scale different types of units `bytes` and `unknown`") + intercept[IllegalArgumentException](Memory.Bytes.tryScale(Time.Nanoseconds)(100)) + .getMessage should be("Can't scale different types of units `bytes` and `time`") + val epsilon = 0.0001 + + Memory.Bytes.tryScale(Memory.Bytes)(100D) should be(100D +- epsilon) + } + + } + + "Unknown unit" should { + "allow scale only Unknown" in { + intercept[IllegalArgumentException](Unknown.tryScale(Memory.Bytes)(100)) + .getMessage should be("Can't scale different types of units `unknown` and `bytes`") + intercept[IllegalArgumentException](Unknown.tryScale(Time.Nanoseconds)(100)) + .getMessage should be("Can't scale different types of units `unknown` and `time`") + + Unknown.scale(Unknown)(100D) should be(100D) + } + + } +} diff --git a/kamon-core/src/test/scala/kamon/util/NeedToScaleSpec.scala b/kamon-core/src/test/scala/kamon/util/NeedToScaleSpec.scala new file mode 100644 index 00000000..3486a60c --- /dev/null +++ b/kamon-core/src/test/scala/kamon/util/NeedToScaleSpec.scala @@ -0,0 +1,64 @@ +/* + * ========================================================================================= + * 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.util + +import com.typesafe.config.ConfigFactory +import kamon.metric.instrument.{Memory, Time} +import org.scalatest.{Matchers, WordSpec} + +class NeedToScaleSpec extends WordSpec with Matchers { + + "NeedToScale" should { + "extract time unit to scale to from config" in { + val config = ConfigFactory.parseString( + """ + |time-units = "ms" + """.stripMargin) + + config match { + case NeedToScale(timeUnits, memoryUnits) => + timeUnits should be(Some(Time.Milliseconds)) + memoryUnits should be(None) + } + } + "extract memory unit to scale to from config" in { + val config = ConfigFactory.parseString( + """ + |memory-units = "kb" + """.stripMargin) + + config match { + case NeedToScale(timeUnits, memoryUnits) => + timeUnits should be(None) + memoryUnits should be(Some(Memory.KiloBytes)) + } + } + "extract nothing if config has no proper keys" in { + val config = ConfigFactory.parseString( + """ + |some-other-key = "value" + """.stripMargin) + + config match { + case NeedToScale(timeUnits, memoryUnits) => + fail("Should not match") + case _ => + } + } + } + +} |