aboutsummaryrefslogblamecommitdiff
path: root/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala
blob: e072d3ef263cd25732e8e800c4080c983922fa0b (plain) (tree)
1
2
3
4
5
6
7
8
9

                    
                                        


                                                   


                                                                           



                                                      

                                                                           


                                                                                                      
                               
                                                    
       






























































































                                                                                                                                                 





                                                                            
 



                                                                                                   





                                                                          



                                                                                                        

     




                                                                                            



















                                                                                                                                                            

                                                                   
 
                                               


                                                              



                                                                                                                                                                      


                               



                                                                                                                                                                                  

                             
                                                                                                             
 



                                                                                                                                                                                



                                        



                                                                                                                                                                                            

                                                    



                                                                                                                                                                          

                                                                 

                                                                    
 
                                                


                                                              





















                                                                                                                                                                                             

                                                    



                                                                                                                                                                    
 
                                           


                                                              




                                                                                     











                                                                                               

                                                                                    







                                                                            



                                                                    
 
                                                                        
 



                                                                                                                                                             


                              

                                                                                                                  
 



                                                                                                                                                                       





                                                                   



                                                                                                                                                     

                             

                                           


     
package kamon.metric

import akka.actor.{ Props, ActorSystem }
import akka.testkit.{ ImplicitSender, TestKitBase }
import com.typesafe.config.ConfigFactory
import kamon.Kamon
import kamon.metric.Subscriptions.TickMetricSnapshot
import kamon.metric.UserMetrics._
import kamon.metric.instrument.{ Histogram, Counter, MinMaxCounter, Gauge }
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 un-registering user metrics" in {
      val metricsExtension = Kamon(Metrics)
      Kamon(UserMetrics).registerCounter("counter-for-remove")
      Kamon(UserMetrics).registerHistogram("histogram-for-remove")
      Kamon(UserMetrics).registerMinMaxCounter("min-max-counter-for-remove")
      Kamon(UserMetrics).registerGauge("gauge-for-remove") { ()  2L }

      metricsExtension.storage.keys should contain(UserCounter("counter-for-remove"))
      metricsExtension.storage.keys should contain(UserHistogram("histogram-for-remove"))
      metricsExtension.storage.keys should contain(UserMinMaxCounter("min-max-counter-for-remove"))
      metricsExtension.storage.keys should contain(UserGauge("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")

      metricsExtension.storage.keys should not contain (UserCounter("counter-for-remove"))
      metricsExtension.storage.keys should not contain (UserHistogram("histogram-for-remove"))
      metricsExtension.storage.keys should not contain (UserMinMaxCounter("min-max-counter-for-remove"))
      metricsExtension.storage.keys should not contain (UserGauge("gauge-for-remove"))
    }

    "include all the registered metrics in the a tick snapshot and reset all recorders" in {
      Kamon(Metrics).subscribe(UserHistograms, "*", testActor, permanently = true)
      Kamon(Metrics).subscribe(UserCounters, "*", testActor, permanently = true)
      Kamon(Metrics).subscribe(UserMinMaxCounters, "*", testActor, permanently = true)
      Kamon(Metrics).subscribe(UserGauges, "*", testActor, permanently = true)

      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)

      Kamon(Metrics).subscriptions ! Subscriptions.FlushMetrics
      val firstSnapshot = expectMsgType[TickMetricSnapshot].metrics

      firstSnapshot.keys should contain allOf (
        UserHistogram("histogram-with-settings"),
        UserHistogram("histogram-with-default-configuration"))

      firstSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (10)
      firstSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (20)
      firstSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(101)
      firstSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain allOf (
        MutableRecord(10, 1),
        MutableRecord(20, 100))

      firstSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (40)
      firstSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (40)
      firstSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(1)
      firstSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain only (
        MutableRecord(40, 1))

      firstSnapshot(UserCounter("counter")).metrics(Count).asInstanceOf[Counter.Snapshot].count should be(17)

      firstSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (43)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(3)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain allOf (
        MutableRecord(0, 1), // min
        MutableRecord(42, 1), // current
        MutableRecord(43, 1)) // max

      firstSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (0)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(3)
      firstSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain only (
        MutableRecord(0, 3)) // min, max and current

      firstSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (15)
      firstSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (15)
      firstSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(1)
      firstSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain only (
        MutableRecord(15, 1)) // only the manually recorded value

      Kamon(Metrics).subscriptions ! Subscriptions.FlushMetrics
      val secondSnapshot = expectMsgType[TickMetricSnapshot].metrics

      secondSnapshot.keys should contain allOf (
        UserHistogram("histogram-with-settings"),
        UserHistogram("histogram-with-default-configuration"))

      secondSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      secondSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (0)
      secondSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(0)
      secondSnapshot(UserHistogram("histogram-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream shouldBe empty

      secondSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      secondSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (0)
      secondSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(0)
      secondSnapshot(UserHistogram("histogram-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream shouldBe empty

      secondSnapshot(UserCounter("counter")).metrics(Count).asInstanceOf[Counter.Snapshot].count should be(0)

      secondSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (42)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (42)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(3)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-settings")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain only (
        MutableRecord(42, 3)) // max

      secondSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (0)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(3)
      secondSnapshot(UserMinMaxCounter("min-max-counter-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain only (
        MutableRecord(0, 3)) // min, max and current

      secondSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      secondSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (0)
      secondSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(0)
      secondSnapshot(UserGauge("gauge-with-default-configuration")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream shouldBe empty

      Kamon(Metrics).unsubscribe(testActor)
    }

    "generate a snapshot that can be merged with another" in {
      val buffer = system.actorOf(TickMetricSnapshotBuffer.props(1 hours, testActor))
      Kamon(Metrics).subscribe(UserHistograms, "*", buffer, permanently = true)
      Kamon(Metrics).subscribe(UserCounters, "*", buffer, permanently = true)
      Kamon(Metrics).subscribe(UserMinMaxCounters, "*", buffer, permanently = true)
      Kamon(Metrics).subscribe(UserGauges, "*", buffer, permanently = true)

      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)

      Kamon(Metrics).subscriptions ! Subscriptions.FlushMetrics
      Thread.sleep(2000) // Make sure that the snapshots are taken before proceeding

      val extraCounter = Kamon(UserMetrics).registerCounter("extra-counter")
      histogram.record(200)
      extraCounter.increment(20)
      minMaxCounter.increment(40)
      minMaxCounter.decrement(50)
      gauge.record(70)

      Kamon(Metrics).subscriptions ! Subscriptions.FlushMetrics
      Thread.sleep(2000) // Make sure that the metrics are buffered.
      buffer ! TickMetricSnapshotBuffer.FlushBuffer
      val snapshot = expectMsgType[TickMetricSnapshot].metrics

      snapshot.keys should contain(UserHistogram("histogram-for-merge"))

      snapshot(UserHistogram("histogram-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (100)
      snapshot(UserHistogram("histogram-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (200)
      snapshot(UserHistogram("histogram-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(2)
      snapshot(UserHistogram("histogram-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain allOf (
        MutableRecord(100, 1),
        MutableRecord(200, 1))

      snapshot(UserCounter("counter-for-merge")).metrics(Count).asInstanceOf[Counter.Snapshot].count should be(10)
      snapshot(UserCounter("extra-counter")).metrics(Count).asInstanceOf[Counter.Snapshot].count should be(20)

      snapshot(UserMinMaxCounter("min-max-counter-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (0)
      snapshot(UserMinMaxCounter("min-max-counter-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (80)
      snapshot(UserMinMaxCounter("min-max-counter-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(6)
      snapshot(UserMinMaxCounter("min-max-counter-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].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

      snapshot(UserGauge("gauge-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].min shouldBe (50)
      snapshot(UserGauge("gauge-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].max shouldBe (70)
      snapshot(UserGauge("gauge-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].numberOfMeasurements should be(2)
      snapshot(UserGauge("gauge-for-merge")).metrics(RecordedValues).asInstanceOf[Histogram.Snapshot].recordsIterator.toStream should contain allOf (
        MutableRecord(50, 1),
        MutableRecord(70, 1))

      Kamon(Metrics).unsubscribe(testActor)
    }
  }
}