diff options
Diffstat (limited to 'kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala')
-rw-r--r-- | kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala | 202 |
1 files changed, 202 insertions, 0 deletions
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..481f03c5 --- /dev/null +++ b/kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala @@ -0,0 +1,202 @@ +/* ========================================================================================= + * 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 kamon.metric.instrument.Histogram.MutableRecord +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.Subscriptions.TickMetricSnapshot +import kamon.metric.ActorMetrics.{ ActorMetricsRecorder, ActorMetricSnapshot } + +class ActorMetricsSpec extends TestKitBase with WordSpecLike with Matchers with ImplicitSender { + implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString( + """ + |kamon.metrics { + | 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 + } +} |