aboutsummaryrefslogtreecommitdiff
path: root/kamon-core/src/test/scala/kamon
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-core/src/test/scala/kamon')
-rw-r--r--kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala (renamed from kamon-core/src/test/scala/kamon/trace/instrumentation/ActorMessagePassingTracingSpec.scala)6
-rw-r--r--kamon-core/src/test/scala/kamon/instrumentation/akka/ActorLoggingInstrumentationSpec.scala (renamed from kamon-core/src/test/scala/kamon/trace/instrumentation/ActorLoggingSpec.scala)11
-rw-r--r--kamon-core/src/test/scala/kamon/instrumentation/akka/ActorSystemMessageInstrumentationSpec.scala (renamed from kamon-core/src/test/scala/kamon/trace/instrumentation/ActorSystemMessagePassingInstrumentationSpec.scala)17
-rw-r--r--kamon-core/src/test/scala/kamon/instrumentation/akka/AskPatternInstrumentationSpec.scala (renamed from kamon-core/src/test/scala/kamon/trace/instrumentation/AskPatternTracingSpec.scala)15
-rw-r--r--kamon-core/src/test/scala/kamon/instrumentation/scala/FutureInstrumentationSpec.scala (renamed from kamon-core/src/test/scala/kamon/trace/instrumentation/FutureTracingSpec.scala)15
-rw-r--r--kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala204
-rw-r--r--kamon-core/src/test/scala/kamon/metric/DispatcherMetricsSpec.scala (renamed from kamon-core/src/test/scala/kamon/metrics/DispatcherMetricsSpec.scala)17
-rw-r--r--kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala134
-rw-r--r--kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala (renamed from kamon-core/src/test/scala/kamon/metrics/TickMetricSnapshotBufferSpec.scala)69
-rw-r--r--kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala96
-rw-r--r--kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala304
-rw-r--r--kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala55
-rw-r--r--kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala72
-rw-r--r--kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala130
-rw-r--r--kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala108
-rw-r--r--kamon-core/src/test/scala/kamon/metrics/ActorMetricsSpec.scala172
-rw-r--r--kamon-core/src/test/scala/kamon/metrics/CustomMetricSpec.scala78
-rw-r--r--kamon-core/src/test/scala/kamon/metrics/MetricSnapshotSpec.scala72
-rw-r--r--kamon-core/src/test/scala/kamon/metrics/instrument/MinMaxCounterSpec.scala110
-rw-r--r--kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala96
20 files changed, 1295 insertions, 486 deletions
diff --git a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorMessagePassingTracingSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala
index acc939fb..ee3857c3 100644
--- a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorMessagePassingTracingSpec.scala
+++ b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala
@@ -23,9 +23,11 @@ import kamon.trace.TraceRecorder
import akka.pattern.{ pipe, ask }
import akka.util.Timeout
import scala.concurrent.duration._
-import akka.routing.RoundRobinRouter
+import akka.routing.{ RoundRobinRouter }
+
+class ActorCellInstrumentationSpec extends TestKit(ActorSystem("actor-cell-instrumentation-spec")) with WordSpecLike
+ with ImplicitSender {
-class ActorMessagePassingTracingSpec extends TestKit(ActorSystem("actor-message-passing-tracing-spec")) with WordSpecLike with ImplicitSender {
implicit val executionContext = system.dispatcher
"the message passing instrumentation" should {
diff --git a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorLoggingSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorLoggingInstrumentationSpec.scala
index 81fd9cbc..3dab44bc 100644
--- a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorLoggingSpec.scala
+++ b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorLoggingInstrumentationSpec.scala
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
-package kamon.trace.instrumentation
+package kamon.instrumentation.akka
-import akka.testkit.TestKit
-import org.scalatest.{ Inspectors, Matchers, WordSpecLike }
-import akka.actor.{ Props, ActorLogging, Actor, ActorSystem }
+import akka.actor.{ Actor, ActorLogging, ActorSystem, Props }
import akka.event.Logging.LogEvent
+import akka.testkit.TestKit
import kamon.trace.{ TraceContextAware, TraceRecorder }
+import org.scalatest.{ Inspectors, Matchers, WordSpecLike }
-class ActorLoggingSpec extends TestKit(ActorSystem("actor-logging-spec")) with WordSpecLike with Matchers with Inspectors {
+class ActorLoggingInstrumentationSpec extends TestKit(ActorSystem("actor-logging-instrumentation-spec")) with WordSpecLike
+ with Matchers with Inspectors {
"the ActorLogging instrumentation" should {
"attach the TraceContext (if available) to log events" in {
diff --git a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorSystemMessagePassingInstrumentationSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorSystemMessageInstrumentationSpec.scala
index ed239b38..47867c55 100644
--- a/kamon-core/src/test/scala/kamon/trace/instrumentation/ActorSystemMessagePassingInstrumentationSpec.scala
+++ b/kamon-core/src/test/scala/kamon/instrumentation/akka/ActorSystemMessageInstrumentationSpec.scala
@@ -1,14 +1,17 @@
-package kamon.trace.instrumentation
+package kamon.instrumentation.akka
-import akka.testkit.{ ImplicitSender, TestKit }
+import akka.actor.SupervisorStrategy.{ Escalate, Restart, Resume, Stop }
import akka.actor._
-import org.scalatest.WordSpecLike
+import akka.testkit.{ ImplicitSender, TestKit }
import kamon.trace.TraceRecorder
-import scala.util.control.NonFatal
-import akka.actor.SupervisorStrategy.{ Escalate, Stop, Restart, Resume }
+import org.scalatest.WordSpecLike
+
import scala.concurrent.duration._
+import scala.util.control.NonFatal
+
+class ActorSystemMessageInstrumentationSpec extends TestKit(ActorSystem("actor-system-message-instrumentation-spec"))
+ with WordSpecLike with ImplicitSender {
-class ActorSystemMessagePassingInstrumentationSpec extends TestKit(ActorSystem("actor-message-passing-tracing-spec")) with WordSpecLike with ImplicitSender {
implicit val executionContext = system.dispatcher
"the system message passing instrumentation" should {
@@ -107,7 +110,7 @@ class ActorSystemMessagePassingInstrumentationSpec extends TestKit(ActorSystem("
}
def supervisorWithDirective(directive: SupervisorStrategy.Directive, sendPreRestart: Boolean = false, sendPostRestart: Boolean = false,
- sendPostStop: Boolean = false, sendPreStart: Boolean = false): ActorRef = {
+ sendPostStop: Boolean = false, sendPreStart: Boolean = false): ActorRef = {
class GrandParent extends Actor {
val child = context.actorOf(Props(new Parent))
diff --git a/kamon-core/src/test/scala/kamon/trace/instrumentation/AskPatternTracingSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/akka/AskPatternInstrumentationSpec.scala
index fb886de6..d914ffe8 100644
--- a/kamon-core/src/test/scala/kamon/trace/instrumentation/AskPatternTracingSpec.scala
+++ b/kamon-core/src/test/scala/kamon/instrumentation/akka/AskPatternInstrumentationSpec.scala
@@ -14,19 +14,20 @@
* =========================================================================================
*/
-package kamon.trace.instrumentation
+package kamon.instrumentation.akka
-import akka.testkit.TestKitBase
-import akka.actor.{ Props, Actor, ActorSystem }
-import org.scalatest.{ Matchers, WordSpecLike }
+import akka.actor.{ Actor, ActorSystem, Props }
import akka.event.Logging.Warning
-import scala.concurrent.duration._
import akka.pattern.ask
+import akka.testkit.TestKitBase
import akka.util.Timeout
-import kamon.trace.{ TraceContextAware, TraceRecorder }
import com.typesafe.config.ConfigFactory
+import kamon.trace.{ TraceContextAware, TraceRecorder }
+import org.scalatest.{ Matchers, WordSpecLike }
+
+import scala.concurrent.duration._
-class AskPatternTracingSpec extends TestKitBase with WordSpecLike with Matchers {
+class AskPatternInstrumentationSpec extends TestKitBase with WordSpecLike with Matchers {
implicit lazy val system: ActorSystem = ActorSystem("ask-pattern-tracing-spec", ConfigFactory.parseString(
"""
|kamon {
diff --git a/kamon-core/src/test/scala/kamon/trace/instrumentation/FutureTracingSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/scala/FutureInstrumentationSpec.scala
index b1765fd8..31afd3ff 100644
--- a/kamon-core/src/test/scala/kamon/trace/instrumentation/FutureTracingSpec.scala
+++ b/kamon-core/src/test/scala/kamon/instrumentation/scala/FutureInstrumentationSpec.scala
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
-package kamon.trace.instrumentation
+package kamon.instrumentation.scala
-import scala.concurrent.{ ExecutionContext, Future }
-import org.scalatest.{ Matchers, OptionValues, WordSpecLike }
-import org.scalatest.concurrent.{ ScalaFutures, PatienceConfiguration }
-import kamon.trace.TraceRecorder
-import akka.testkit.TestKit
import akka.actor.ActorSystem
+import akka.testkit.TestKit
+import kamon.trace.TraceRecorder
+import org.scalatest.concurrent.{ PatienceConfiguration, ScalaFutures }
+import org.scalatest.{ Matchers, OptionValues, WordSpecLike }
+
+import scala.concurrent.Future
-class FutureTracingSpec extends TestKit(ActorSystem("actor-message-passing-tracing-spec")) with WordSpecLike with Matchers
+class FutureInstrumentationSpec extends TestKit(ActorSystem("future-instrumentation-spec")) with WordSpecLike with Matchers
with ScalaFutures with PatienceConfiguration with OptionValues {
implicit val execContext = system.dispatcher
diff --git a/kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala
new file mode 100644
index 00000000..0029fd7c
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala
@@ -0,0 +1,204 @@
+/* =========================================================================================
+ * Copyright © 2013 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.nio.LongBuffer
+
+import akka.instrumentation.ActorCellMetrics
+import kamon.metric.ActorMetricsTestActor._
+import org.scalatest.{ WordSpecLike, Matchers }
+import akka.testkit.{ ImplicitSender, TestProbe, TestKitBase }
+import akka.actor._
+import com.typesafe.config.ConfigFactory
+import scala.concurrent.duration._
+import kamon.metric.ActorMetrics.{ ActorMetricsRecorder, ActorMetricSnapshot }
+
+class ActorMetricsSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 10
+ |
+ | filters = [
+ | {
+ | actor {
+ | includes = [ "user/tracked-*", "user/measuring-*", "user/clean-after-collect" ]
+ | excludes = [ "user/tracked-explicitly-excluded"]
+ | }
+ | }
+ | ]
+ | precision {
+ | default-histogram-precision {
+ | highest-trackable-value = 3600000000000
+ | significant-value-digits = 2
+ | }
+ |
+ | default-min-max-counter-precision {
+ | refresh-interval = 1 second
+ | highest-trackable-value = 999999999
+ | significant-value-digits = 2
+ | }
+ | }
+ |}
+ """.stripMargin))
+
+ "the Kamon actor metrics" should {
+ "respect the configured include and exclude filters" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("tracked-actor")
+ actorMetricsRecorderOf(trackedActor) should not be empty
+
+ val nonTrackedActor = createTestActor("non-tracked-actor")
+ actorMetricsRecorderOf(nonTrackedActor) shouldBe empty
+
+ val trackedButExplicitlyExcluded = createTestActor("tracked-explicitly-excluded")
+ actorMetricsRecorderOf(trackedButExplicitlyExcluded) shouldBe empty
+ }
+
+ "reset all recording instruments after taking a snapshot" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("clean-after-collect")
+ val trackedActorMetrics = actorMetricsRecorderOf(trackedActor).get
+ for (i ← 1 to 100) {
+ trackedActor ! Discard
+ }
+ trackedActor ! Fail
+ trackedActor ! TrackTimings(sleep = Some(1 second))
+ expectMsgType[TrackedTimings]
+
+ val firstSnapshot = takeSnapshotOf(trackedActorMetrics)
+ firstSnapshot.errors.count should be(1L)
+ firstSnapshot.mailboxSize.numberOfMeasurements should be > 0L
+ firstSnapshot.processingTime.numberOfMeasurements should be(103L) // 102 examples + Initialize message
+ firstSnapshot.timeInMailbox.numberOfMeasurements should be(103L) // 102 examples + Initialize message
+
+ val secondSnapshot = takeSnapshotOf(trackedActorMetrics) // Ensure that the recorders are clean
+ secondSnapshot.errors.count should be(0L)
+ secondSnapshot.mailboxSize.numberOfMeasurements should be <= 3L
+ secondSnapshot.processingTime.numberOfMeasurements should be(0L) // 102 examples + Initialize message
+ secondSnapshot.timeInMailbox.numberOfMeasurements should be(0L) // 102 examples + Initialize message
+ }
+
+ "record the processing-time of the receive function" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("measuring-processing-time")
+ val trackedActorMetrics = actorMetricsRecorderOf(trackedActor).get
+ takeSnapshotOf(trackedActorMetrics) // Ensure that the recorders are clean
+
+ trackedActor ! TrackTimings(sleep = Some(1 second))
+ val timings = expectMsgType[TrackedTimings]
+ val snapshot = takeSnapshotOf(trackedActorMetrics)
+
+ snapshot.processingTime.numberOfMeasurements should be(1L)
+ snapshot.processingTime.recordsIterator.next().count should be(1L)
+ snapshot.processingTime.recordsIterator.next().level should be(timings.approximateProcessingTime +- 10.millis.toNanos)
+ }
+
+ "record the number of errors" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("measuring-errors")
+ val trackedActorMetrics = actorMetricsRecorderOf(trackedActor).get
+ takeSnapshotOf(trackedActorMetrics) // Ensure that the recorders are clean
+
+ for (i ← 1 to 10) { trackedActor ! Fail }
+ trackedActor ! Ping
+ expectMsg(Pong)
+ val snapshot = takeSnapshotOf(trackedActorMetrics)
+
+ snapshot.errors.count should be(10)
+ }
+
+ "record the mailbox-size" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("measuring-mailbox-size")
+ val trackedActorMetrics = actorMetricsRecorderOf(trackedActor).get
+ takeSnapshotOf(trackedActorMetrics) // Ensure that the recorders are clean
+
+ trackedActor ! TrackTimings(sleep = Some(1 second))
+ for (i ← 1 to 10) {
+ trackedActor ! Discard
+ }
+ trackedActor ! Ping
+
+ val timings = expectMsgType[TrackedTimings]
+ expectMsg(Pong)
+ val snapshot = takeSnapshotOf(trackedActorMetrics)
+
+ snapshot.mailboxSize.min should be(0L)
+ snapshot.mailboxSize.max should be(11L +- 1L)
+ }
+
+ "record the time-in-mailbox" in new ActorMetricsFixtures {
+ val trackedActor = createTestActor("measuring-time-in-mailbox")
+ val trackedActorMetrics = actorMetricsRecorderOf(trackedActor).get
+ takeSnapshotOf(trackedActorMetrics) // Ensure that the recorders are clean
+
+ trackedActor ! TrackTimings(sleep = Some(1 second))
+ val timings = expectMsgType[TrackedTimings]
+ val snapshot = takeSnapshotOf(trackedActorMetrics)
+
+ snapshot.timeInMailbox.numberOfMeasurements should be(1L)
+ snapshot.timeInMailbox.recordsIterator.next().count should be(1L)
+ snapshot.timeInMailbox.recordsIterator.next().level should be(timings.approximateTimeInMailbox +- 10.millis.toNanos)
+ }
+ }
+
+ trait ActorMetricsFixtures {
+ val collectionContext = new CollectionContext {
+ val buffer: LongBuffer = LongBuffer.allocate(10000)
+ }
+
+ def actorMetricsRecorderOf(ref: ActorRef): Option[ActorMetricsRecorder] = {
+ val initialisationListener = TestProbe()
+ ref.tell(Ping, initialisationListener.ref)
+ initialisationListener.expectMsg(Pong)
+
+ val underlyingCellField = ref.getClass.getDeclaredMethod("underlying")
+ val cell = underlyingCellField.invoke(ref).asInstanceOf[ActorCellMetrics]
+
+ cell.actorMetricsRecorder
+ }
+
+ def createTestActor(name: String): ActorRef = system.actorOf(Props[ActorMetricsTestActor], name)
+
+ def takeSnapshotOf(amr: ActorMetricsRecorder): ActorMetricSnapshot = amr.collect(collectionContext)
+ }
+}
+
+class ActorMetricsTestActor extends Actor {
+ def receive = {
+ case Discard ⇒
+ case Fail ⇒ 1 / 0
+ case Ping ⇒ sender ! Pong
+ case TrackTimings(sendTimestamp, sleep) ⇒ {
+ val dequeueTimestamp = System.nanoTime()
+ sleep.map(s ⇒ Thread.sleep(s.toMillis))
+ val afterReceiveTimestamp = System.nanoTime()
+
+ sender ! TrackedTimings(sendTimestamp, dequeueTimestamp, afterReceiveTimestamp)
+ }
+ }
+}
+
+object ActorMetricsTestActor {
+ case object Ping
+ case object Pong
+ case object Fail
+ case object Discard
+
+ case class TrackTimings(sendTimestamp: Long = System.nanoTime(), sleep: Option[Duration] = None)
+ case class TrackedTimings(sendTimestamp: Long, dequeueTimestamp: Long, afterReceiveTimestamp: Long) {
+ def approximateTimeInMailbox: Long = dequeueTimestamp - sendTimestamp
+ def approximateProcessingTime: Long = afterReceiveTimestamp - dequeueTimestamp
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metrics/DispatcherMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/DispatcherMetricsSpec.scala
index 2a9cb6b4..ae324b73 100644
--- a/kamon-core/src/test/scala/kamon/metrics/DispatcherMetricsSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/DispatcherMetricsSpec.scala
@@ -13,7 +13,7 @@
* =========================================================================================
*/
-package kamon.metrics
+package kamon.metric
import org.scalatest.{ WordSpecLike, Matchers }
import akka.testkit.{ TestProbe, TestKitBase }
@@ -21,13 +21,16 @@ import akka.actor.{ ActorRef, Props, ActorSystem }
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import kamon.Kamon
-import kamon.metrics.Subscriptions.TickMetricSnapshot
-import kamon.metrics.DispatcherMetrics.DispatcherMetricSnapshot
+import kamon.metric.Subscriptions.TickMetricSnapshot
+import kamon.metric.DispatcherMetrics.DispatcherMetricSnapshot
class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers {
implicit lazy val system: ActorSystem = ActorSystem("dispatcher-metrics-spec", ConfigFactory.parseString(
"""
|kamon.metrics {
+ | tick-interval = 1 second
+ | default-collection-context-buffer-size = 10
+ |
| filters = [
| {
| dispatcher {
@@ -52,8 +55,8 @@ class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers
"the Kamon dispatcher metrics" should {
"respect the configured include and exclude filters" in {
- system.actorOf(Props[DelayableActor].withDispatcher("tracked-dispatcher"), "actor-with-tracked-dispatcher")
- system.actorOf(Props[DelayableActor].withDispatcher("dispatcher-explicitly-excluded"), "actor-with-excluded-dispatcher")
+ system.actorOf(Props[ActorMetricsTestActor].withDispatcher("tracked-dispatcher"), "actor-with-tracked-dispatcher")
+ system.actorOf(Props[ActorMetricsTestActor].withDispatcher("dispatcher-explicitly-excluded"), "actor-with-excluded-dispatcher")
Kamon(Metrics).subscribe(DispatcherMetrics, "*", testActor, permanently = true)
expectMsgType[TickMetricSnapshot]
@@ -69,7 +72,7 @@ class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers
val (delayable, metricsListener) = delayableActor("worker-actor", "tracked-dispatcher")
for (_ ← 1 to 100) {
- delayable ! Discard
+ //delayable ! Discard
}
val dispatcherMetrics = expectDispatcherMetrics("tracked-dispatcher", metricsListener, 3 seconds)
@@ -92,7 +95,7 @@ class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers
trait DelayableActorFixture {
def delayableActor(name: String, dispatcher: String): (ActorRef, TestProbe) = {
- val actor = system.actorOf(Props[DelayableActor].withDispatcher(dispatcher), name)
+ val actor = system.actorOf(Props[ActorMetricsTestActor].withDispatcher(dispatcher), name)
val metricsListener = TestProbe()
Kamon(Metrics).subscribe(DispatcherMetrics, "*", metricsListener.ref, permanently = true)
diff --git a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
new file mode 100644
index 00000000..60923a2b
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
@@ -0,0 +1,134 @@
+package kamon.metric
+
+import akka.actor._
+import akka.testkit.{ TestProbe, ImplicitSender, TestKitBase }
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.metric.Subscriptions.TickMetricSnapshot
+import org.scalatest.{ Matchers, WordSpecLike }
+import scala.concurrent.duration._
+
+class SubscriptionsProtocolSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("subscriptions-protocol-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ |}
+ """.stripMargin))
+
+ val metricsExtension = Kamon(Metrics)(system)
+ import metricsExtension.{ register, subscribe, unsubscribe }
+
+ "the Subscriptions messaging protocol" should {
+ "allow subscribing for a single tick" in {
+ val subscriber = TestProbe()
+ register(TraceMetrics("one-shot"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "one-shot", subscriber.ref, permanently = false)
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+
+ tickSnapshot.metrics.size should be(1)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("one-shot"))
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ subscriber.expectNoMsg(1 second)
+ }
+
+ "allow subscribing permanently to a metric" in {
+ val subscriber = TestProbe()
+ register(TraceMetrics("permanent"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "permanent", subscriber.ref, permanently = true)
+
+ for (repetition ← 1 to 5) {
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+
+ tickSnapshot.metrics.size should be(1)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("permanent"))
+ subscriber.expectNoMsg(1 second)
+ }
+ }
+
+ "allow subscribing to metrics matching a glob pattern" in {
+ val subscriber = TestProbe()
+ register(TraceMetrics("include-one"), TraceMetrics.Factory)
+ register(TraceMetrics("exclude-two"), TraceMetrics.Factory)
+ register(TraceMetrics("include-three"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "include-*", subscriber.ref, permanently = true)
+
+ for (repetition ← 1 to 5) {
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+
+ tickSnapshot.metrics.size should be(2)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("include-one"))
+ tickSnapshot.metrics.keys should contain(TraceMetrics("include-three"))
+ subscriber.expectNoMsg(1 second)
+ }
+ }
+
+ "send a single TickMetricSnapshot to each subscriber, even if subscribed multiple times" in {
+ val subscriber = TestProbe()
+ register(TraceMetrics("include-one"), TraceMetrics.Factory)
+ register(TraceMetrics("exclude-two"), TraceMetrics.Factory)
+ register(TraceMetrics("include-three"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "include-one", subscriber.ref, permanently = true)
+ subscribe(TraceMetrics, "include-three", subscriber.ref, permanently = true)
+
+ for (repetition ← 1 to 5) {
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+
+ tickSnapshot.metrics.size should be(2)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("include-one"))
+ tickSnapshot.metrics.keys should contain(TraceMetrics("include-three"))
+ }
+ }
+
+ "allow un-subscribing a subscriber" in {
+ val subscriber = TestProbe()
+ register(TraceMetrics("one-shot"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "one-shot", subscriber.ref, permanently = true)
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+ tickSnapshot.metrics.size should be(1)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("one-shot"))
+
+ unsubscribe(subscriber.ref)
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ subscriber.expectNoMsg(1 second)
+ }
+
+ "watch all subscribers and un-subscribe them if they die" in {
+ val subscriber = TestProbe()
+ val forwarderSubscriber = system.actorOf(Props(new ForwarderSubscriber(subscriber.ref)))
+ watch(forwarderSubscriber)
+ register(TraceMetrics("one-shot"), TraceMetrics.Factory)
+ subscribe(TraceMetrics, "one-shot", forwarderSubscriber, permanently = true)
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
+ tickSnapshot.metrics.size should be(1)
+ tickSnapshot.metrics.keys should contain(TraceMetrics("one-shot"))
+
+ forwarderSubscriber ! PoisonPill
+ expectTerminated(forwarderSubscriber)
+
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ metricsExtension.subscriptions ! Subscriptions.FlushMetrics
+ subscriber.expectNoMsg(2 seconds)
+ }
+ }
+}
+
+class ForwarderSubscriber(target: ActorRef) extends Actor {
+ def receive = {
+ case anything ⇒ target.forward(anything)
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metrics/TickMetricSnapshotBufferSpec.scala b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
index d0a0c707..27eb64e2 100644
--- a/kamon-core/src/test/scala/kamon/metrics/TickMetricSnapshotBufferSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
@@ -14,16 +14,36 @@
* =========================================================================================
*/
-package kamon.metrics
+package kamon.metric
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.metric.instrument.Histogram
+import kamon.metric.instrument.Histogram.MutableRecord
import org.scalatest.{ Matchers, WordSpecLike }
-import akka.testkit.TestKit
+import akka.testkit.{ ImplicitSender, TestKitBase }
import akka.actor.ActorSystem
import scala.concurrent.duration._
-import kamon.metrics.Subscriptions.TickMetricSnapshot
-import kamon.metrics.MetricSnapshot.Measurement
-
-class TickMetricSnapshotBufferSpec extends TestKit(ActorSystem("tick-metric-snapshot-buffer")) with WordSpecLike with Matchers {
+import kamon.metric.Subscriptions.TickMetricSnapshot
+
+class TickMetricSnapshotBufferSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("trace-metrics-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 10
+ |
+ | filters = [
+ | {
+ | trace {
+ | includes = [ "*" ]
+ | excludes = [ "non-tracked-trace"]
+ | }
+ | }
+ | ]
+ |}
+ """.stripMargin))
"the TickMetricSnapshotBuffer" should {
"merge TickMetricSnapshots received until the flush timeout is reached and fix the from/to fields" in new SnapshotFixtures {
@@ -55,27 +75,38 @@ class TickMetricSnapshotBufferSpec extends TestKit(ActorSystem("tick-metric-snap
mergedSnapshot.to should equal(4000)
mergedSnapshot.metrics should not be ('empty)
- val testMetricSnapshot = mergedSnapshot.metrics(CustomMetric("test-metric")).metrics(CustomMetric.RecordedValues)
- testMetricSnapshot.min should equal(1)
- testMetricSnapshot.max should equal(10)
- testMetricSnapshot.numberOfMeasurements should equal(35)
- testMetricSnapshot.measurements should contain allOf (Measurement(1, 10), Measurement(4, 9), Measurement(10, 16))
+ val testMetricSnapshot = mergedSnapshot.metrics(testTraceIdentity).metrics(TraceMetrics.ElapsedTime).asInstanceOf[Histogram.Snapshot]
+ testMetricSnapshot.min should equal(10)
+ testMetricSnapshot.max should equal(300)
+ testMetricSnapshot.numberOfMeasurements should equal(6)
+ testMetricSnapshot.recordsIterator.toStream should contain allOf (
+ MutableRecord(10, 3),
+ MutableRecord(20, 1),
+ MutableRecord(30, 1),
+ MutableRecord(300, 1))
}
}
trait SnapshotFixtures {
+ val collectionContext = Kamon(Metrics).buildDefaultCollectionContext
+ val testTraceIdentity = TraceMetrics("buffer-spec-test-trace")
+ val traceRecorder = Kamon(Metrics).register(testTraceIdentity, TraceMetrics.Factory).get
+
val firstEmpty = TickMetricSnapshot(1000, 2000, Map.empty)
val secondEmpty = TickMetricSnapshot(2000, 3000, Map.empty)
val thirdEmpty = TickMetricSnapshot(3000, 4000, Map.empty)
- val firstNonEmpty = TickMetricSnapshot(1000, 2000,
- Map((CustomMetric("test-metric") -> SimpleGroupSnapshot(Map(CustomMetric.RecordedValues -> MetricSnapshot(InstrumentTypes.Histogram, 20, Scale.Unit, Vector(Measurement(1, 10), Measurement(10, 10))))))))
-
- val secondNonEmpty = TickMetricSnapshot(1000, 2000,
- Map((CustomMetric("test-metric") -> SimpleGroupSnapshot(Map(CustomMetric.RecordedValues -> MetricSnapshot(InstrumentTypes.Histogram, 15, Scale.Unit, Vector(Measurement(4, 9), Measurement(10, 6))))))))
-
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(20L)
+ traceRecorder.elapsedTime.record(30L)
+ val firstNonEmpty = TickMetricSnapshot(1000, 2000, Map(
+ (testTraceIdentity -> traceRecorder.collect(collectionContext))))
+
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(300L)
+ val secondNonEmpty = TickMetricSnapshot(1000, 2000, Map(
+ (testTraceIdentity -> traceRecorder.collect(collectionContext))))
}
-
- case class SimpleGroupSnapshot(metrics: Map[MetricIdentity, MetricSnapshotLike]) extends MetricGroupSnapshot
}
diff --git a/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
new file mode 100644
index 00000000..23977971
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
@@ -0,0 +1,96 @@
+package kamon.metric
+
+import akka.actor.ActorSystem
+import akka.testkit.{ ImplicitSender, TestKitBase }
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.metric.TraceMetrics.TraceMetricsSnapshot
+import kamon.trace.TraceContext.SegmentIdentity
+import kamon.trace.TraceRecorder
+import org.scalatest.{ Matchers, WordSpecLike }
+
+class TraceMetricsSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("trace-metrics-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 10
+ |
+ | filters = [
+ | {
+ | trace {
+ | includes = [ "*" ]
+ | excludes = [ "non-tracked-trace"]
+ | }
+ | }
+ | ]
+ | precision {
+ | default-histogram-precision {
+ | highest-trackable-value = 3600000000000
+ | significant-value-digits = 2
+ | }
+ |
+ | default-min-max-counter-precision {
+ | refresh-interval = 1 second
+ | highest-trackable-value = 999999999
+ | significant-value-digits = 2
+ | }
+ | }
+ |}
+ """.stripMargin))
+
+ "the TraceMetrics" should {
+ "record the elapsed time between a trace creation and finish" in {
+ for (repetitions ← 1 to 10) {
+ TraceRecorder.withNewTraceContext("record-elapsed-time") {
+ TraceRecorder.finish()
+ }
+ }
+
+ val snapshot = takeSnapshotOf("record-elapsed-time")
+ snapshot.elapsedTime.numberOfMeasurements should be(10)
+ snapshot.segments shouldBe empty
+ }
+
+ "record the elapsed time for segments that occur inside a given trace" in {
+ TraceRecorder.withNewTraceContext("trace-with-segments") {
+ val segmentHandle = TraceRecorder.startSegment(TraceMetricsTestSegment("test-segment"))
+ segmentHandle.get.finish()
+ TraceRecorder.finish()
+ }
+
+ val snapshot = takeSnapshotOf("trace-with-segments")
+ snapshot.elapsedTime.numberOfMeasurements should be(1)
+ snapshot.segments.size should be(1)
+ snapshot.segments(TraceMetricsTestSegment("test-segment")).numberOfMeasurements should be(1)
+ }
+
+ "record the elapsed time for segments that finish after their correspondent trace has finished" in {
+ val segmentHandle = TraceRecorder.withNewTraceContext("closing-segment-after-trace") {
+ val sh = TraceRecorder.startSegment(TraceMetricsTestSegment("test-segment"))
+ TraceRecorder.finish()
+ sh
+ }
+
+ val beforeFinishSegmentSnapshot = takeSnapshotOf("closing-segment-after-trace")
+ beforeFinishSegmentSnapshot.elapsedTime.numberOfMeasurements should be(1)
+ beforeFinishSegmentSnapshot.segments.size should be(0)
+
+ segmentHandle.get.finish()
+
+ val afterFinishSegmentSnapshot = takeSnapshotOf("closing-segment-after-trace")
+ afterFinishSegmentSnapshot.elapsedTime.numberOfMeasurements should be(0)
+ afterFinishSegmentSnapshot.segments.size should be(1)
+ afterFinishSegmentSnapshot.segments(TraceMetricsTestSegment("test-segment")).numberOfMeasurements should be(1)
+ }
+ }
+
+ case class TraceMetricsTestSegment(name: String) extends SegmentIdentity
+
+ def takeSnapshotOf(traceName: String): TraceMetricsSnapshot = {
+ val recorder = Kamon(Metrics).register(TraceMetrics(traceName), TraceMetrics.Factory)
+ val collectionContext = Kamon(Metrics).buildDefaultCollectionContext
+ recorder.get.collect(collectionContext)
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala
new file mode 100644
index 00000000..66ee49ea
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala
@@ -0,0 +1,304 @@
+package kamon.metric
+
+import akka.actor.ActorSystem
+import akka.testkit.{ ImplicitSender, TestKitBase }
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.metric.UserMetrics.{ UserGauge, UserMinMaxCounter, UserCounter, UserHistogram }
+import kamon.metric.instrument.Histogram
+import kamon.metric.instrument.Histogram.MutableRecord
+import org.scalatest.{ Matchers, WordSpecLike }
+import scala.concurrent.duration._
+
+class UserMetricsSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 10
+ |
+ | precision {
+ | default-histogram-precision {
+ | highest-trackable-value = 10000
+ | significant-value-digits = 2
+ | }
+ |
+ | default-min-max-counter-precision {
+ | refresh-interval = 1 hour
+ | highest-trackable-value = 1000
+ | significant-value-digits = 2
+ | }
+ |
+ | default-gauge-precision {
+ | refresh-interval = 1 hour
+ | highest-trackable-value = 999999999
+ | significant-value-digits = 2
+ | }
+ | }
+ |}
+ """.stripMargin))
+
+ "the UserMetrics extension" should {
+ "allow registering a fully configured Histogram and get the same Histogram if registering again" in {
+ val histogramA = Kamon(UserMetrics).registerHistogram("histogram-with-settings", Histogram.Precision.Normal, 10000L)
+ val histogramB = Kamon(UserMetrics).registerHistogram("histogram-with-settings", Histogram.Precision.Normal, 10000L)
+
+ histogramA shouldBe theSameInstanceAs(histogramB)
+ }
+
+ "return the original Histogram when registering a fully configured Histogram for second time but with different settings" in {
+ val histogramA = Kamon(UserMetrics).registerHistogram("histogram-with-settings", Histogram.Precision.Normal, 10000L)
+ val histogramB = Kamon(UserMetrics).registerHistogram("histogram-with-settings", Histogram.Precision.Fine, 50000L)
+
+ histogramA shouldBe theSameInstanceAs(histogramB)
+ }
+
+ "allow registering a Histogram that takes the default configuration from the kamon.metrics.precision settings" in {
+ Kamon(UserMetrics).registerHistogram("histogram-with-default-configuration")
+ }
+
+ "allow registering a Counter and get the same Counter if registering again" in {
+ val counterA = Kamon(UserMetrics).registerCounter("counter")
+ val counterB = Kamon(UserMetrics).registerCounter("counter")
+
+ counterA shouldBe theSameInstanceAs(counterB)
+ }
+
+ "allow registering a fully configured MinMaxCounter and get the same MinMaxCounter if registering again" in {
+ val minMaxCounterA = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-settings", Histogram.Precision.Normal, 1000L, 1 second)
+ val minMaxCounterB = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-settings", Histogram.Precision.Normal, 1000L, 1 second)
+
+ minMaxCounterA shouldBe theSameInstanceAs(minMaxCounterB)
+ }
+
+ "return the original MinMaxCounter when registering a fully configured MinMaxCounter for second time but with different settings" in {
+ val minMaxCounterA = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-settings", Histogram.Precision.Normal, 1000L, 1 second)
+ val minMaxCounterB = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-settings", Histogram.Precision.Fine, 5000L, 1 second)
+
+ minMaxCounterA shouldBe theSameInstanceAs(minMaxCounterB)
+ }
+
+ "allow registering a MinMaxCounter that takes the default configuration from the kamon.metrics.precision settings" in {
+ Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-default-configuration")
+ }
+
+ "allow registering a fully configured Gauge and get the same Gauge if registering again" in {
+ val gaugeA = Kamon(UserMetrics).registerGauge("gauge-with-settings", Histogram.Precision.Normal, 1000L, 1 second) {
+ () ⇒ 1L
+ }
+
+ val gaugeB = Kamon(UserMetrics).registerGauge("gauge-with-settings", Histogram.Precision.Normal, 1000L, 1 second) {
+ () ⇒ 1L
+ }
+
+ gaugeA shouldBe theSameInstanceAs(gaugeB)
+ }
+
+ "return the original Gauge when registering a fully configured Gauge for second time but with different settings" in {
+ val gaugeA = Kamon(UserMetrics).registerGauge("gauge-with-settings", Histogram.Precision.Normal, 1000L, 1 second) {
+ () ⇒ 1L
+ }
+
+ val gaugeB = Kamon(UserMetrics).registerGauge("gauge-with-settings", Histogram.Precision.Fine, 5000L, 1 second) {
+ () ⇒ 1L
+ }
+
+ gaugeA shouldBe theSameInstanceAs(gaugeB)
+ }
+
+ "allow registering a Gauge that takes the default configuration from the kamon.metrics.precision settings" in {
+ Kamon(UserMetrics).registerGauge("gauge-with-default-configuration") {
+ () ⇒ 2L
+ }
+ }
+
+ "allow unregistering metrics from the extension" in {
+ val userMetricsRecorder = Kamon(Metrics).register(UserMetrics, UserMetrics.Factory).get
+ val counter = Kamon(UserMetrics).registerCounter("counter-for-remove")
+ val histogram = Kamon(UserMetrics).registerHistogram("histogram-for-remove")
+ val minMaxCounter = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-for-remove")
+ val gauge = Kamon(UserMetrics).registerGauge("gauge-for-remove") { () ⇒ 2L }
+
+ userMetricsRecorder.counters.keys should contain("counter-for-remove")
+ userMetricsRecorder.histograms.keys should contain("histogram-for-remove")
+ userMetricsRecorder.minMaxCounters.keys should contain("min-max-counter-for-remove")
+ userMetricsRecorder.gauges.keys should contain("gauge-for-remove")
+
+ Kamon(UserMetrics).removeCounter("counter-for-remove")
+ Kamon(UserMetrics).removeHistogram("histogram-for-remove")
+ Kamon(UserMetrics).removeMinMaxCounter("min-max-counter-for-remove")
+ Kamon(UserMetrics).removeGauge("gauge-for-remove")
+
+ userMetricsRecorder.counters.keys should not contain ("counter-for-remove")
+ userMetricsRecorder.histograms.keys should not contain ("histogram-for-remove")
+ userMetricsRecorder.minMaxCounters.keys should not contain ("min-max-counter-for-remove")
+ userMetricsRecorder.gauges.keys should not contain ("gauge-for-remove")
+ }
+
+ "generate a snapshot containing all the registered user metrics and reset all instruments" in {
+ val context = Kamon(Metrics).buildDefaultCollectionContext
+ val userMetricsRecorder = Kamon(Metrics).register(UserMetrics, UserMetrics.Factory).get
+
+ val histogramWithSettings = Kamon(UserMetrics).registerHistogram("histogram-with-settings", Histogram.Precision.Normal, 10000L)
+ val histogramWithDefaultConfiguration = Kamon(UserMetrics).registerHistogram("histogram-with-default-configuration")
+ val counter = Kamon(UserMetrics).registerCounter("counter")
+ val minMaxCounterWithSettings = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-with-settings", Histogram.Precision.Normal, 1000L, 1 second)
+ val gauge = Kamon(UserMetrics).registerGauge("gauge-with-default-configuration") { () ⇒ 2L }
+
+ // lets put some values on those metrics
+ histogramWithSettings.record(10)
+ histogramWithSettings.record(20, 100)
+ histogramWithDefaultConfiguration.record(40)
+
+ counter.increment()
+ counter.increment(16)
+
+ minMaxCounterWithSettings.increment(43)
+ minMaxCounterWithSettings.decrement()
+
+ gauge.record(15)
+
+ val firstSnapshot = userMetricsRecorder.collect(context)
+
+ firstSnapshot.histograms.size should be(2)
+ firstSnapshot.histograms.keys should contain allOf (
+ UserHistogram("histogram-with-settings"),
+ UserHistogram("histogram-with-default-configuration"))
+
+ firstSnapshot.histograms(UserHistogram("histogram-with-settings")).min shouldBe (10)
+ firstSnapshot.histograms(UserHistogram("histogram-with-settings")).max shouldBe (20)
+ firstSnapshot.histograms(UserHistogram("histogram-with-settings")).numberOfMeasurements should be(101)
+ firstSnapshot.histograms(UserHistogram("histogram-with-settings")).recordsIterator.toStream should contain allOf (
+ MutableRecord(10, 1),
+ MutableRecord(20, 100))
+
+ firstSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).min shouldBe (40)
+ firstSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).max shouldBe (40)
+ firstSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).numberOfMeasurements should be(1)
+ firstSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).recordsIterator.toStream should contain only (
+ MutableRecord(40, 1))
+
+ firstSnapshot.counters(UserCounter("counter")).count should be(17)
+
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).min shouldBe (0)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).max shouldBe (43)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).numberOfMeasurements should be(3)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).recordsIterator.toStream should contain allOf (
+ MutableRecord(0, 1), // min
+ MutableRecord(42, 1), // current
+ MutableRecord(43, 1)) // max
+
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).min shouldBe (0)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).max shouldBe (0)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).numberOfMeasurements should be(3)
+ firstSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).recordsIterator.toStream should contain only (
+ MutableRecord(0, 3)) // min, max and current
+
+ firstSnapshot.gauges(UserGauge("gauge-with-default-configuration")).min shouldBe (15)
+ firstSnapshot.gauges(UserGauge("gauge-with-default-configuration")).max shouldBe (15)
+ firstSnapshot.gauges(UserGauge("gauge-with-default-configuration")).numberOfMeasurements should be(1)
+ firstSnapshot.gauges(UserGauge("gauge-with-default-configuration")).recordsIterator.toStream should contain only (
+ MutableRecord(15, 1)) // only the manually recorded value
+
+ val secondSnapshot = userMetricsRecorder.collect(context)
+
+ secondSnapshot.histograms.size should be(2)
+ secondSnapshot.histograms.keys should contain allOf (
+ UserHistogram("histogram-with-settings"),
+ UserHistogram("histogram-with-default-configuration"))
+
+ secondSnapshot.histograms(UserHistogram("histogram-with-settings")).min shouldBe (0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-settings")).max shouldBe (0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-settings")).numberOfMeasurements should be(0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-settings")).recordsIterator.toStream shouldBe empty
+
+ secondSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).min shouldBe (0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).max shouldBe (0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).numberOfMeasurements should be(0)
+ secondSnapshot.histograms(UserHistogram("histogram-with-default-configuration")).recordsIterator.toStream shouldBe empty
+
+ secondSnapshot.counters(UserCounter("counter")).count should be(0)
+
+ secondSnapshot.minMaxCounters.size should be(2)
+ secondSnapshot.minMaxCounters.keys should contain allOf (
+ UserMinMaxCounter("min-max-counter-with-settings"),
+ UserMinMaxCounter("min-max-counter-with-default-configuration"))
+
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).min shouldBe (42)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).max shouldBe (42)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).numberOfMeasurements should be(3)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-settings")).recordsIterator.toStream should contain only (
+ MutableRecord(42, 3)) // min, max and current
+
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).min shouldBe (0)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).max shouldBe (0)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).numberOfMeasurements should be(3)
+ secondSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-with-default-configuration")).recordsIterator.toStream should contain only (
+ MutableRecord(0, 3)) // min, max and current
+
+ secondSnapshot.gauges(UserGauge("gauge-with-default-configuration")).min shouldBe (0)
+ secondSnapshot.gauges(UserGauge("gauge-with-default-configuration")).max shouldBe (0)
+ secondSnapshot.gauges(UserGauge("gauge-with-default-configuration")).numberOfMeasurements should be(0)
+ secondSnapshot.gauges(UserGauge("gauge-with-default-configuration")).recordsIterator shouldBe empty
+
+ }
+
+ "generate a snapshot that can be merged with another" in {
+ val context = Kamon(Metrics).buildDefaultCollectionContext
+ val userMetricsRecorder = Kamon(Metrics).register(UserMetrics, UserMetrics.Factory).get
+
+ val histogram = Kamon(UserMetrics).registerHistogram("histogram-for-merge")
+ val counter = Kamon(UserMetrics).registerCounter("counter-for-merge")
+ val minMaxCounter = Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-for-merge")
+ val gauge = Kamon(UserMetrics).registerGauge("gauge-for-merge") { () ⇒ 10L }
+
+ histogram.record(100)
+ counter.increment(10)
+ minMaxCounter.increment(50)
+ minMaxCounter.decrement(10)
+ gauge.record(50)
+
+ val firstSnapshot = userMetricsRecorder.collect(context)
+
+ val extraCounter = Kamon(UserMetrics).registerCounter("extra-counter")
+ histogram.record(200)
+ extraCounter.increment(20)
+ minMaxCounter.increment(40)
+ minMaxCounter.decrement(50)
+ gauge.record(70)
+
+ val secondSnapshot = userMetricsRecorder.collect(context)
+ val mergedSnapshot = firstSnapshot.merge(secondSnapshot, context)
+
+ mergedSnapshot.histograms.keys should contain(UserHistogram("histogram-for-merge"))
+
+ mergedSnapshot.histograms(UserHistogram("histogram-for-merge")).min shouldBe (100)
+ mergedSnapshot.histograms(UserHistogram("histogram-for-merge")).max shouldBe (200)
+ mergedSnapshot.histograms(UserHistogram("histogram-for-merge")).numberOfMeasurements should be(2)
+ mergedSnapshot.histograms(UserHistogram("histogram-for-merge")).recordsIterator.toStream should contain allOf (
+ MutableRecord(100, 1),
+ MutableRecord(200, 1))
+
+ mergedSnapshot.counters(UserCounter("counter-for-merge")).count should be(10)
+ mergedSnapshot.counters(UserCounter("extra-counter")).count should be(20)
+
+ mergedSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-for-merge")).min shouldBe (0)
+ mergedSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-for-merge")).max shouldBe (80)
+ mergedSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-for-merge")).numberOfMeasurements should be(6)
+ mergedSnapshot.minMaxCounters(UserMinMaxCounter("min-max-counter-for-merge")).recordsIterator.toStream should contain allOf (
+ MutableRecord(0, 1), // min in first snapshot
+ MutableRecord(30, 2), // min and current in second snapshot
+ MutableRecord(40, 1), // current in first snapshot
+ MutableRecord(50, 1), // max in first snapshot
+ MutableRecord(80, 1)) // max in second snapshot
+
+ mergedSnapshot.gauges(UserGauge("gauge-for-merge")).min shouldBe (50)
+ mergedSnapshot.gauges(UserGauge("gauge-for-merge")).max shouldBe (70)
+ mergedSnapshot.gauges(UserGauge("gauge-for-merge")).numberOfMeasurements should be(2)
+ mergedSnapshot.gauges(UserGauge("gauge-for-merge")).recordsIterator.toStream should contain allOf (
+ MutableRecord(50, 1),
+ MutableRecord(70, 1))
+ }
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala
new file mode 100644
index 00000000..1a93e1f6
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/instrument/CounterSpec.scala
@@ -0,0 +1,55 @@
+package kamon.metric.instrument
+
+import java.nio.LongBuffer
+
+import kamon.metric.CollectionContext
+import org.scalatest.{ Matchers, WordSpec }
+
+class CounterSpec extends WordSpec with Matchers {
+
+ "a Counter" should {
+ "allow increment only operations" in new CounterFixture {
+ counter.increment()
+ counter.increment(10)
+
+ intercept[UnsupportedOperationException] {
+ counter.increment(-10)
+ }
+ }
+
+ "reset to zero when a snapshot is taken" in new CounterFixture {
+ counter.increment(100)
+ takeSnapshotFrom(counter).count should be(100)
+ takeSnapshotFrom(counter).count should be(0)
+ takeSnapshotFrom(counter).count should be(0)
+
+ counter.increment(50)
+ takeSnapshotFrom(counter).count should be(50)
+ takeSnapshotFrom(counter).count should be(0)
+ }
+
+ "produce a snapshot that can be merged with others" in new CounterFixture {
+ val counterA = Counter()
+ val counterB = Counter()
+ counterA.increment(100)
+ counterB.increment(200)
+
+ val counterASnapshot = takeSnapshotFrom(counterA)
+ val counterBSnapshot = takeSnapshotFrom(counterB)
+
+ counterASnapshot.merge(counterBSnapshot, collectionContext).count should be(300)
+ counterBSnapshot.merge(counterASnapshot, collectionContext).count should be(300)
+ }
+
+ }
+
+ trait CounterFixture {
+ val counter = Counter()
+
+ val collectionContext = new CollectionContext {
+ val buffer: LongBuffer = LongBuffer.allocate(1)
+ }
+
+ def takeSnapshotFrom(counter: Counter): Counter.Snapshot = counter.collect(collectionContext)
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala
new file mode 100644
index 00000000..9192d999
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala
@@ -0,0 +1,72 @@
+package kamon.metric.instrument
+
+import java.util.concurrent.atomic.AtomicLong
+
+import akka.actor.ActorSystem
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.metric.{ Metrics, Scale, CollectionContext }
+import org.scalatest.{ Matchers, WordSpecLike }
+import scala.concurrent.duration._
+
+class GaugeSpec extends WordSpecLike with Matchers {
+ implicit val system = ActorSystem("gauge-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | flush-interval = 1 hour
+ | default-collection-context-buffer-size = 10
+ | precision {
+ | default-gauge-precision {
+ | refresh-interval = 100 milliseconds
+ | highest-trackable-value = 999999999
+ | significant-value-digits = 2
+ | }
+ | }
+ |}
+ """.stripMargin))
+
+ "a Gauge" should {
+ "automatically record the current value using the configured refresh-interval" in {
+ val numberOfValuesRecorded = new AtomicLong(0)
+ val gauge = Gauge.fromDefaultConfig(system) { () ⇒ numberOfValuesRecorded.addAndGet(1) }
+
+ Thread.sleep(1.second.toMillis)
+ numberOfValuesRecorded.get() should be(10L +- 1L)
+ gauge.cleanup
+ }
+
+ "stop automatically recording after a call to cleanup" in {
+ val numberOfValuesRecorded = new AtomicLong(0)
+ val gauge = Gauge.fromDefaultConfig(system) { () ⇒ numberOfValuesRecorded.addAndGet(1) }
+
+ Thread.sleep(1.second.toMillis)
+ gauge.cleanup
+ numberOfValuesRecorded.get() should be(10L +- 1L)
+ Thread.sleep(1.second.toMillis)
+ numberOfValuesRecorded.get() should be(10L +- 1L)
+ }
+
+ "produce a Histogram snapshot including all the recorded values" in {
+ val numberOfValuesRecorded = new AtomicLong(0)
+ val gauge = Gauge.fromDefaultConfig(system) { () ⇒ numberOfValuesRecorded.addAndGet(1) }
+
+ Thread.sleep(1.second.toMillis)
+ gauge.cleanup
+ val snapshot = gauge.collect(Kamon(Metrics).buildDefaultCollectionContext)
+
+ snapshot.numberOfMeasurements should be(10L +- 1L)
+ snapshot.min should be(1)
+ snapshot.max should be(10L +- 1L)
+ }
+
+ "not record the current value when doing a collection" in {
+ val numberOfValuesRecorded = new AtomicLong(0)
+ val gauge = Gauge(Histogram.Precision.Normal, 10000L, Scale.Unit, 1 hour, system)(() ⇒ numberOfValuesRecorded.addAndGet(1))
+
+ val snapshot = gauge.collect(Kamon(Metrics).buildDefaultCollectionContext)
+
+ snapshot.numberOfMeasurements should be(0)
+ numberOfValuesRecorded.get() should be(0)
+ }
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala
new file mode 100644
index 00000000..cefdf0f4
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/instrument/HistogramSpec.scala
@@ -0,0 +1,130 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013 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 java.nio.LongBuffer
+
+import com.typesafe.config.ConfigFactory
+import kamon.metric.CollectionContext
+import org.scalatest.{ Matchers, WordSpec }
+
+import scala.util.Random
+
+class HistogramSpec extends WordSpec with Matchers {
+
+ val histogramConfig = ConfigFactory.parseString(
+ """
+ |
+ |highest-trackable-value = 100000
+ |significant-value-digits = 2
+ |
+ """.stripMargin)
+
+ "a Histogram" should {
+ "allow record values within the configured range" in new HistogramFixture {
+ histogram.record(1000)
+ histogram.record(5000, count = 100)
+ histogram.record(10000)
+ }
+
+ "fail when recording values higher than the highest trackable value" in new HistogramFixture {
+ intercept[IndexOutOfBoundsException] {
+ histogram.record(1000000)
+ }
+ }
+
+ "reset all recorded levels to zero after a snapshot collection" in new HistogramFixture {
+ histogram.record(100)
+ histogram.record(200)
+ histogram.record(300)
+
+ takeSnapshot().numberOfMeasurements should be(3)
+ takeSnapshot().numberOfMeasurements should be(0)
+ }
+
+ "produce a snapshot" which {
+ "supports min, max and numberOfMeasurements operations" in new HistogramFixture {
+ histogram.record(100)
+ histogram.record(200, count = 200)
+ histogram.record(300)
+ histogram.record(900)
+
+ val snapshot = takeSnapshot()
+
+ snapshot.min should equal(100L +- 1L)
+ snapshot.max should equal(900L +- 9L)
+ snapshot.numberOfMeasurements should be(203)
+ }
+
+ "can be merged with another snapshot" in new MultipleHistogramFixture {
+ val random = new Random(System.nanoTime())
+
+ for (repetitions ← 1 to 1000) {
+ // Put some values on A and Control
+ for (_ ← 1 to 1000) {
+ val newRecording = random.nextInt(100000)
+ controlHistogram.record(newRecording)
+ histogramA.record(newRecording)
+ }
+
+ // Put some values on B and Control
+ for (_ ← 1 to 2000) {
+ val newRecording = random.nextInt(100000)
+ controlHistogram.record(newRecording)
+ histogramB.record(newRecording)
+ }
+
+ val controlSnapshot = takeSnapshotFrom(controlHistogram)
+ val histogramASnapshot = takeSnapshotFrom(histogramA)
+ val histogramBSnapshot = takeSnapshotFrom(histogramB)
+
+ assertEquals(controlSnapshot, histogramASnapshot.merge(histogramBSnapshot, collectionContext))
+ assertEquals(controlSnapshot, histogramBSnapshot.merge(histogramASnapshot, collectionContext))
+ }
+ }
+ }
+ }
+
+ trait HistogramFixture {
+ val collectionContext = new CollectionContext {
+ val buffer: LongBuffer = LongBuffer.allocate(10000)
+ }
+
+ val histogram = Histogram.fromConfig(histogramConfig)
+
+ def takeSnapshot(): Histogram.Snapshot = histogram.collect(collectionContext)
+ }
+
+ trait MultipleHistogramFixture {
+ val collectionContext = new CollectionContext {
+ val buffer: LongBuffer = LongBuffer.allocate(10000)
+ }
+
+ val controlHistogram = Histogram.fromConfig(histogramConfig)
+ val histogramA = Histogram.fromConfig(histogramConfig)
+ val histogramB = Histogram.fromConfig(histogramConfig)
+
+ def takeSnapshotFrom(histogram: Histogram): Histogram.Snapshot = histogram.collect(collectionContext)
+
+ def assertEquals(left: Histogram.Snapshot, right: Histogram.Snapshot): Unit = {
+ left.numberOfMeasurements should equal(right.numberOfMeasurements)
+ left.min should equal(right.min)
+ left.max should equal(right.max)
+ left.recordsIterator.toStream should contain theSameElementsAs (right.recordsIterator.toStream)
+ }
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala
new file mode 100644
index 00000000..cb03664c
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala
@@ -0,0 +1,108 @@
+/* =========================================================================================
+ * 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.instrument
+
+import java.nio.LongBuffer
+
+import akka.actor.ActorSystem
+import com.typesafe.config.ConfigFactory
+import kamon.metric.CollectionContext
+import kamon.metric.instrument.Histogram.MutableRecord
+import org.scalatest.{ Matchers, WordSpecLike }
+
+class MinMaxCounterSpec extends WordSpecLike with Matchers {
+ val system = ActorSystem("min-max-counter-spec")
+ val minMaxCounterConfig = ConfigFactory.parseString(
+ """
+ |refresh-interval = 1 hour
+ |highest-trackable-value = 1000
+ |significant-value-digits = 2
+ """.stripMargin)
+
+ "the MinMaxCounter" should {
+ "track ascending tendencies" in new MinMaxCounterFixture {
+ mmCounter.increment()
+ mmCounter.increment(3)
+ mmCounter.increment()
+
+ val snapshot = collectCounterSnapshot()
+
+ snapshot.min should be(0)
+ snapshot.max should be(5)
+ snapshot.recordsIterator.toStream should contain allOf (
+ MutableRecord(0, 1), // min
+ MutableRecord(5, 2)) // max and current
+ }
+
+ "track descending tendencies" in new MinMaxCounterFixture {
+ mmCounter.increment(5)
+ mmCounter.decrement()
+ mmCounter.decrement(3)
+ mmCounter.decrement()
+
+ val snapshot = collectCounterSnapshot()
+
+ snapshot.min should be(0)
+ snapshot.max should be(5)
+ snapshot.recordsIterator.toStream should contain allOf (
+ MutableRecord(0, 2), // min and current
+ MutableRecord(5, 1)) // max
+ }
+
+ "reset the min and max to the current value after taking a snapshot" in new MinMaxCounterFixture {
+ mmCounter.increment(5)
+ mmCounter.decrement(3)
+
+ val firstSnapshot = collectCounterSnapshot()
+
+ firstSnapshot.min should be(0)
+ firstSnapshot.max should be(5)
+ firstSnapshot.recordsIterator.toStream should contain allOf (
+ MutableRecord(0, 1), // min
+ MutableRecord(2, 1), // current
+ MutableRecord(5, 1)) // max
+
+ val secondSnapshot = collectCounterSnapshot()
+
+ secondSnapshot.min should be(2)
+ secondSnapshot.max should be(2)
+ secondSnapshot.recordsIterator.toStream should contain(
+ MutableRecord(2, 3)) // min, max and current
+ }
+
+ "report zero as the min and current values if they current value fell bellow zero" in new MinMaxCounterFixture {
+ mmCounter.decrement(3)
+
+ val snapshot = collectCounterSnapshot()
+
+ snapshot.min should be(0)
+ snapshot.max should be(0)
+ snapshot.recordsIterator.toStream should contain(
+ MutableRecord(0, 3)) // min, max and current (even while current really is -3
+ }
+ }
+
+ trait MinMaxCounterFixture {
+ val collectionContext = new CollectionContext {
+ val buffer: LongBuffer = LongBuffer.allocate(64)
+ }
+
+ val mmCounter = MinMaxCounter.fromConfig(minMaxCounterConfig, system).asInstanceOf[PaddedMinMaxCounter]
+ mmCounter.cleanup // cancel the refresh schedule
+
+ def collectCounterSnapshot(): Histogram.Snapshot = mmCounter.collect(collectionContext)
+ }
+}
diff --git a/kamon-core/src/test/scala/kamon/metrics/ActorMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metrics/ActorMetricsSpec.scala
deleted file mode 100644
index 645ca96a..00000000
--- a/kamon-core/src/test/scala/kamon/metrics/ActorMetricsSpec.scala
+++ /dev/null
@@ -1,172 +0,0 @@
-/* =========================================================================================
- * Copyright © 2013 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 org.scalatest.{ WordSpecLike, Matchers }
-import akka.testkit.{ TestProbe, TestKitBase }
-import akka.actor.{ ActorRef, Actor, Props, ActorSystem }
-import com.typesafe.config.ConfigFactory
-import scala.concurrent.duration._
-import kamon.Kamon
-import kamon.metrics.Subscriptions.TickMetricSnapshot
-import kamon.metrics.ActorMetrics.ActorMetricSnapshot
-import kamon.metrics.MetricSnapshot.Measurement
-
-class ActorMetricsSpec extends TestKitBase with WordSpecLike with Matchers {
- implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString(
- """
- |kamon.metrics {
- | filters = [
- | {
- | actor {
- | includes = [ "user/tracked-*" ]
- | excludes = [ "user/tracked-explicitly-excluded"]
- | }
- | }
- | ]
- |}
- """.stripMargin))
-
- "the Kamon actor metrics" should {
- "respect the configured include and exclude filters" in new DelayableActorFixture {
- val tracked = system.actorOf(Props[DelayableActor], "tracked-actor")
- val nonTracked = system.actorOf(Props[DelayableActor], "non-tracked-actor")
- val trackedExplicitlyExcluded = system.actorOf(Props[DelayableActor], "tracked-explicitly-excluded")
-
- Kamon(Metrics).subscribe(ActorMetrics, "*", testActor, permanently = true)
- expectMsgType[TickMetricSnapshot]
-
- tracked ! Discard
- nonTracked ! Discard
- trackedExplicitlyExcluded ! Discard
-
- within(2 seconds) {
- val tickSnapshot = expectMsgType[TickMetricSnapshot]
- tickSnapshot.metrics.keys should contain(ActorMetrics("user/tracked-actor"))
- tickSnapshot.metrics.keys should not contain (ActorMetrics("user/non-tracked-actor"))
- tickSnapshot.metrics.keys should not contain (ActorMetrics("user/tracked-explicitly-excluded"))
- }
- }
-
- "record mailbox-size, processing-time and time-in-mailbox metrics under regular conditions" in new DelayableActorFixture {
- val (delayable, metricsListener) = delayableActor("tracked-normal-conditions")
-
- for (_ ← 1 to 10) {
- delayable ! Discard
- }
-
- val actorMetrics = expectActorMetrics("user/tracked-normal-conditions", metricsListener, 3 seconds)
- actorMetrics.mailboxSize.max should be <= 10L
- actorMetrics.processingTime.numberOfMeasurements should be(10L)
- actorMetrics.timeInMailbox.numberOfMeasurements should be(10L)
- }
-
- "keep a correct mailbox-size even if the actor is blocked processing a message" in new DelayableActorFixture {
- val (delayable, metricsListener) = delayableActor("tracked-mailbox-size-queueing-up")
-
- delayable ! Delay(2500 milliseconds)
- for (_ ← 1 to 9) {
- delayable ! Discard
- }
-
- // let the first snapshot pass
- metricsListener.expectMsgType[TickMetricSnapshot]
-
- // process the tick in which the actor is stalled.
- val stalledTickMetrics = expectActorMetrics("user/tracked-mailbox-size-queueing-up", metricsListener, 2 seconds)
- stalledTickMetrics.mailboxSize.numberOfMeasurements should equal(30)
- // only the automatic last-value recording should be taken, and includes the message being currently processed.
- stalledTickMetrics.mailboxSize.measurements should contain only (Measurement(10, 30))
- stalledTickMetrics.mailboxSize.min should equal(10)
- stalledTickMetrics.mailboxSize.max should equal(10)
- stalledTickMetrics.processingTime.numberOfMeasurements should be(0L)
- stalledTickMetrics.timeInMailbox.numberOfMeasurements should be(0L)
-
- // process the tick after the actor is unblocked.
- val afterStallTickMetrics = expectActorMetrics("user/tracked-mailbox-size-queueing-up", metricsListener, 2 seconds)
- afterStallTickMetrics.processingTime.numberOfMeasurements should be(10L)
- afterStallTickMetrics.timeInMailbox.numberOfMeasurements should be(10L)
- afterStallTickMetrics.processingTime.max should be(2500.milliseconds.toNanos +- 100.milliseconds.toNanos)
- afterStallTickMetrics.timeInMailbox.max should be(2500.milliseconds.toNanos +- 100.milliseconds.toNanos)
- }
-
- "track the number of errors" in new ErrorActorFixture {
- val (error, metricsListener) = failedActor("tracked-errors")
-
- for (_ ← 1 to 5) {
- error ! Error
- }
-
- val actorMetrics = expectActorMetrics("user/tracked-errors", metricsListener, 3 seconds)
- actorMetrics.errorCounter.numberOfMeasurements should be(5L)
- }
- }
-
- def expectActorMetrics(actorPath: String, listener: TestProbe, waitTime: FiniteDuration): ActorMetricSnapshot = {
- val tickSnapshot = within(waitTime) {
- listener.expectMsgType[TickMetricSnapshot]
- }
- val actorMetricsOption = tickSnapshot.metrics.get(ActorMetrics(actorPath))
- actorMetricsOption should not be empty
- actorMetricsOption.get.asInstanceOf[ActorMetricSnapshot]
- }
-
- trait DelayableActorFixture {
- def delayableActor(name: String): (ActorRef, TestProbe) = {
- val actor = system.actorOf(Props[DelayableActor], name)
- val metricsListener = TestProbe()
-
- Kamon(Metrics).subscribe(ActorMetrics, "user/" + name, metricsListener.ref, permanently = true)
- // Wait for one empty snapshot before proceeding to the test.
- metricsListener.expectMsgType[TickMetricSnapshot]
-
- (actor, metricsListener)
- }
- }
-
- trait ErrorActorFixture {
- def failedActor(name: String): (ActorRef, TestProbe) = {
- val actor = system.actorOf(Props[FailedActor], name)
- val metricsListener = TestProbe()
-
- Kamon(Metrics).subscribe(ActorMetrics, "user/" + name, metricsListener.ref, permanently = true)
- // Wait for one empty snapshot before proceeding to the test.
- metricsListener.expectMsgType[TickMetricSnapshot]
-
- (actor, metricsListener)
- }
- }
-}
-
-class DelayableActor extends Actor {
- def receive = {
- case Delay(time) ⇒ Thread.sleep(time.toMillis)
- case Discard ⇒
- }
-}
-
-class FailedActor extends Actor {
- def receive = {
- case Error ⇒ 1 / 0
- case Discard ⇒
- }
-}
-
-case object Discard
-
-case class Delay(time: FiniteDuration)
-
-case class Error()
diff --git a/kamon-core/src/test/scala/kamon/metrics/CustomMetricSpec.scala b/kamon-core/src/test/scala/kamon/metrics/CustomMetricSpec.scala
deleted file mode 100644
index 1e072f71..00000000
--- a/kamon-core/src/test/scala/kamon/metrics/CustomMetricSpec.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * =========================================================================================
- * Copyright © 2013 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.testkit.TestKitBase
-import org.scalatest.{ Matchers, WordSpecLike }
-import akka.actor.ActorSystem
-import scala.concurrent.duration._
-import com.typesafe.config.ConfigFactory
-import kamon.Kamon
-import kamon.metrics.Subscriptions.TickMetricSnapshot
-import kamon.metrics.MetricSnapshot.Measurement
-
-class CustomMetricSpec extends TestKitBase with WordSpecLike with Matchers {
- implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString(
- """
- |kamon.metrics {
- | filters = [
- | {
- | custom-metric {
- | includes = [ "test/*" ]
- | excludes = [ ]
- | }
- | }
- | ]
- |}
- """.stripMargin))
-
- "the Kamon custom metrics support" should {
- "allow registering a custom metric with the Metrics extension" in {
- val recorder = Kamon(Metrics).register(CustomMetric("test/sample-counter"), CustomMetric.histogram(100, 2, Scale.Unit))
-
- recorder should be('defined)
- }
-
- "allow subscriptions to custom metrics using the default subscription protocol" in {
- val recorder = Kamon(Metrics).register(CustomMetric("test/sample-counter"), CustomMetric.histogram(100, 2, Scale.Unit))
-
- recorder.map { r ⇒
- r.record(100)
- r.record(15)
- r.record(0)
- r.record(50)
- }
-
- Kamon(Metrics).subscribe(CustomMetric, "test/sample-counter", testActor)
-
- val recordedValues = within(5 seconds) {
- val snapshot = expectMsgType[TickMetricSnapshot]
- snapshot.metrics(CustomMetric("test/sample-counter")).metrics(CustomMetric.RecordedValues)
- }
-
- recordedValues.min should equal(0)
- recordedValues.max should equal(100)
- recordedValues.numberOfMeasurements should equal(4)
- recordedValues.measurements should contain allOf (
- Measurement(0, 1),
- Measurement(15, 1),
- Measurement(50, 1),
- Measurement(100, 1))
- }
- }
-
-}
diff --git a/kamon-core/src/test/scala/kamon/metrics/MetricSnapshotSpec.scala b/kamon-core/src/test/scala/kamon/metrics/MetricSnapshotSpec.scala
deleted file mode 100644
index 4d6ebc49..00000000
--- a/kamon-core/src/test/scala/kamon/metrics/MetricSnapshotSpec.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * =========================================================================================
- * Copyright © 2013 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 org.scalatest.{ Matchers, WordSpec }
-import kamon.metrics.MetricSnapshot.Measurement
-
-class MetricSnapshotSpec extends WordSpec with Matchers {
-
- "a metric snapshot" should {
- "support a max operation" in new SnapshotFixtures {
- snapshotA.max should be(17)
- snapshotB.max should be(10)
- snapshotC.max should be(1)
- }
-
- "support a min operation" in new SnapshotFixtures {
- snapshotA.min should be(1)
- snapshotB.min should be(2)
- snapshotC.min should be(1)
- }
-
- "be able to merge with other snapshot" in new SnapshotFixtures {
- val merged = snapshotA.merge(snapshotB).merge(snapshotC)
-
- merged.min should be(1)
- merged.max should be(17)
- merged.numberOfMeasurements should be(300)
- merged.measurements.map(_.value) should contain inOrderOnly (1, 2, 4, 5, 7, 10, 17)
- }
-
- "be able to merge with empty snapshots" in new SnapshotFixtures {
- snapshotA.merge(emptySnapshot) should be(snapshotA)
- emptySnapshot.merge(snapshotA).merge(emptySnapshot) should be(snapshotA)
- snapshotC.merge(emptySnapshot) should be(snapshotC)
- }
-
- }
-
- trait SnapshotFixtures {
- val emptySnapshot = MetricSnapshot(InstrumentTypes.Histogram, 0, Scale.Unit, Vector.empty)
-
- val snapshotA = MetricSnapshot(InstrumentTypes.Histogram, 100, Scale.Unit, Vector(
- Measurement(1, 3),
- Measurement(2, 15),
- Measurement(5, 68),
- Measurement(7, 13),
- Measurement(17, 1)))
-
- val snapshotB = MetricSnapshot(InstrumentTypes.Histogram, 100, Scale.Unit, Vector(
- Measurement(2, 6),
- Measurement(4, 48),
- Measurement(5, 39),
- Measurement(10, 7)))
-
- val snapshotC = MetricSnapshot(InstrumentTypes.Counter, 100, Scale.Unit, Vector(Measurement(1, 100)))
- }
-}
diff --git a/kamon-core/src/test/scala/kamon/metrics/instrument/MinMaxCounterSpec.scala b/kamon-core/src/test/scala/kamon/metrics/instrument/MinMaxCounterSpec.scala
deleted file mode 100644
index 14f1573f..00000000
--- a/kamon-core/src/test/scala/kamon/metrics/instrument/MinMaxCounterSpec.scala
+++ /dev/null
@@ -1,110 +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.instrument
-
-import org.scalatest.{ Matchers, WordSpecLike }
-import kamon.metrics.instruments.MinMaxCounter
-import kamon.metrics.instruments.MinMaxCounter.CounterMeasurement
-
-class MinMaxCounterSpec extends WordSpecLike with Matchers {
-
- "the MinMaxCounter" should {
- "increment" in {
- val counter = MinMaxCounter()
-
- counter.increment()
- counter.increment()
- counter.increment()
- counter.increment()
- counter.increment()
-
- val CounterMeasurement(_, _, current) = counter.collect()
-
- current should be(5)
- }
-
- "decrement" in {
- val counter = MinMaxCounter()
- counter.increment(5L)
-
- counter.decrement()
- counter.decrement()
- counter.decrement()
- counter.decrement()
- counter.decrement()
-
- val CounterMeasurement(_, _, current) = counter.collect()
-
- current should be(0)
- }
-
- "reset the min and max with the sum value when the collect method is called" in {
- val counter = MinMaxCounter()
-
- counter.increment(10)
- counter.increment(20)
- counter.increment(30)
- counter.increment(40)
- counter.increment(50)
-
- counter.collect() //only for check the last value after reset min max
-
- val CounterMeasurement(min, max, current) = counter.collect()
-
- min should be(current)
- max should be(current)
- current should be(150)
- }
- }
-
- "track the min value" in {
- val counter = MinMaxCounter()
-
- counter.increment(10)
- counter.increment(20)
- counter.increment(30)
- counter.increment(40)
- counter.increment(50)
-
- val CounterMeasurement(min, _, _) = counter.collect()
-
- min should be(0)
-
- counter.increment(50)
-
- val CounterMeasurement(minAfterCollectAndAddSomeValues, _, _) = counter.collect()
-
- minAfterCollectAndAddSomeValues should be(150)
- }
-
- "track the max value" in {
- val counter = MinMaxCounter()
- counter.increment(10)
- counter.increment(20)
- counter.increment(30)
- counter.increment(40)
- counter.increment(50)
-
- val CounterMeasurement(_, max, _) = counter.collect()
-
- max should be(150)
-
- counter.increment(200)
-
- val CounterMeasurement(_, maxAfterCollectAndAddSomeValues, _) = counter.collect()
-
- maxAfterCollectAndAddSomeValues should be(350)
- }
-}
diff --git a/kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala b/kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala
new file mode 100644
index 00000000..d073f68e
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala
@@ -0,0 +1,96 @@
+package kamon.trace
+
+import akka.actor.ActorSystem
+import akka.testkit.TestKitBase
+import com.typesafe.config.ConfigFactory
+import kamon.trace.TraceContext.SegmentIdentity
+import org.scalatest.{ Matchers, WordSpecLike }
+
+class TraceContextManipulationSpec extends TestKitBase with WordSpecLike with Matchers {
+ implicit def self = testActor
+ implicit lazy val system: ActorSystem = ActorSystem("trace-metrics-spec", ConfigFactory.parseString(
+ """
+ |kamon.metrics {
+ | tick-interval = 1 hour
+ | filters = [
+ | {
+ | trace {
+ | includes = [ "*" ]
+ | excludes = [ "non-tracked-trace"]
+ | }
+ | }
+ | ]
+ | precision {
+ | default-histogram-precision {
+ | highest-trackable-value = 3600000000000
+ | significant-value-digits = 2
+ | }
+ |
+ | default-min-max-counter-precision {
+ | refresh-interval = 1 second
+ | highest-trackable-value = 999999999
+ | significant-value-digits = 2
+ | }
+ | }
+ |}
+ """.stripMargin))
+
+ "the TraceRecorder api" should {
+ "allow starting a trace within a specified block of code, and only within that block of code" in {
+ val createdContext = TraceRecorder.withNewTraceContext("start-context") {
+ TraceRecorder.currentContext should not be empty
+ TraceRecorder.currentContext.get
+ }
+
+ TraceRecorder.currentContext shouldBe empty
+ createdContext.name shouldBe ("start-context")
+ }
+
+ "allow starting a trace within a specified block of code, providing a trace-token and only within that block of code" in {
+ val createdContext = TraceRecorder.withNewTraceContext("start-context-with-token", Some("token-1")) {
+ TraceRecorder.currentContext should not be empty
+ TraceRecorder.currentContext.get
+ }
+
+ TraceRecorder.currentContext shouldBe empty
+ createdContext.name shouldBe ("start-context-with-token")
+ createdContext.token should be("token-1")
+ }
+
+ "allow providing a TraceContext and make it available within a block of code" in {
+ val createdContext = TraceRecorder.withNewTraceContext("manually-provided-trace-context") { TraceRecorder.currentContext }
+
+ TraceRecorder.currentContext shouldBe empty
+ TraceRecorder.withTraceContext(createdContext) {
+ TraceRecorder.currentContext should be(createdContext)
+ }
+
+ TraceRecorder.currentContext shouldBe empty
+ }
+
+ "allow renaming a trace" in {
+ val createdContext = TraceRecorder.withNewTraceContext("trace-before-rename") {
+ TraceRecorder.rename("renamed-trace")
+ TraceRecorder.currentContext.get
+ }
+
+ TraceRecorder.currentContext shouldBe empty
+ createdContext.name shouldBe ("renamed-trace")
+ }
+
+ "allow creating a segment within a trace" in {
+ val createdContext = TraceRecorder.withNewTraceContext("trace-with-segments") {
+ val segmentHandle = TraceRecorder.startSegment(TraceManipulationTestSegment("segment-1"))
+
+ TraceRecorder.currentContext.get
+ }
+
+ TraceRecorder.currentContext shouldBe empty
+ createdContext.name shouldBe ("trace-with-segments")
+
+ }
+ }
+
+ case class TraceManipulationTestSegment(name: String) extends SegmentIdentity
+
+}