package kamon.metric import akka.actor import akka.actor.{ ExtendedActorSystem, ExtensionIdProvider, ExtensionId } import kamon.Kamon import kamon.metric.instrument.{ Gauge, MinMaxCounter, Counter, Histogram } import scala.concurrent.duration.FiniteDuration class UserMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extension { import Metrics.AtomicGetOrElseUpdateForTriemap import UserMetrics._ lazy val metricsExtension = Kamon(Metrics)(system) val precisionConfig = system.settings.config.getConfig("kamon.metrics.precision") val defaultHistogramPrecisionConfig = precisionConfig.getConfig("default-histogram-precision") val defaultMinMaxCounterPrecisionConfig = precisionConfig.getConfig("default-min-max-counter-precision") val defaultGaugePrecisionConfig = precisionConfig.getConfig("default-gauge-precision") def registerHistogram(name: String, precision: Histogram.Precision, highestTrackableValue: Long): Histogram = { metricsExtension.storage.atomicGetOrElseUpdate(UserHistogram(name), { UserHistogramRecorder(Histogram(highestTrackableValue, precision, Scale.Unit)) }).asInstanceOf[UserHistogramRecorder].histogram } def registerHistogram(name: String): Histogram = { metricsExtension.storage.atomicGetOrElseUpdate(UserHistogram(name), { UserHistogramRecorder(Histogram.fromConfig(defaultHistogramPrecisionConfig)) }).asInstanceOf[UserHistogramRecorder].histogram } def registerCounter(name: String): Counter = { metricsExtension.storage.atomicGetOrElseUpdate(UserCounter(name), { UserCounterRecorder(Counter()) }).asInstanceOf[UserCounterRecorder].counter } def registerMinMaxCounter(name: String, precision: Histogram.Precision, highestTrackableValue: Long, refreshInterval: FiniteDuration): MinMaxCounter = { metricsExtension.storage.atomicGetOrElseUpdate(UserMinMaxCounter(name), { UserMinMaxCounterRecorder(MinMaxCounter(highestTrackableValue, precision, Scale.Unit, refreshInterval, system)) }).asInstanceOf[UserMinMaxCounterRecorder].minMaxCounter } def registerMinMaxCounter(name: String): MinMaxCounter = { metricsExtension.storage.atomicGetOrElseUpdate(UserMinMaxCounter(name), { UserMinMaxCounterRecorder(MinMaxCounter.fromConfig(defaultMinMaxCounterPrecisionConfig, system)) }).asInstanceOf[UserMinMaxCounterRecorder].minMaxCounter } def registerGauge(name: String)(currentValueCollector: Gauge.CurrentValueCollector): Gauge = { metricsExtension.storage.atomicGetOrElseUpdate(UserGauge(name), { UserGaugeRecorder(Gauge.fromConfig(defaultGaugePrecisionConfig, system)(currentValueCollector)) }).asInstanceOf[UserGaugeRecorder].gauge } def registerGauge(name: String, precision: Histogram.Precision, highestTrackableValue: Long, refreshInterval: FiniteDuration)(currentValueCollector: Gauge.CurrentValueCollector): Gauge = { metricsExtension.storage.atomicGetOrElseUpdate(UserGauge(name), { UserGaugeRecorder(Gauge(precision, highestTrackableValue, Scale.Unit, refreshInterval, system)(currentValueCollector)) }).asInstanceOf[UserGaugeRecorder].gauge } def removeHistogram(name: String): Unit = metricsExtension.unregister(UserHistogram(name)) def removeCounter(name: String): Unit = metricsExtension.unregister(UserCounter(name)) def removeMinMaxCounter(name: String): Unit = metricsExtension.unregister(UserMinMaxCounter(name)) def removeGauge(name: String): Unit = metricsExtension.unregister(UserGauge(name)) } object UserMetrics extends ExtensionId[UserMetricsExtension] with ExtensionIdProvider { def lookup(): ExtensionId[_ <: actor.Extension] = Metrics def createExtension(system: ExtendedActorSystem): UserMetricsExtension = new UserMetricsExtension(system) sealed trait UserMetricGroup // // Histograms // case class UserHistogram(name: String) extends MetricGroupIdentity with UserMetricGroup { val category = UserHistograms } case class UserHistogramRecorder(histogram: Histogram) extends MetricGroupRecorder { def collect(context: CollectionContext): MetricGroupSnapshot = UserHistogramSnapshot(histogram.collect(context)) def cleanup: Unit = histogram.cleanup } case class UserHistogramSnapshot(histogramSnapshot: Histogram.Snapshot) extends MetricGroupSnapshot { type GroupSnapshotType = UserHistogramSnapshot def merge(that: UserHistogramSnapshot, context: CollectionContext): UserHistogramSnapshot = UserHistogramSnapshot(that.histogramSnapshot.merge(histogramSnapshot, context)) def metrics: Map[MetricIdentity, MetricSnapshot] = Map((RecordedValues, histogramSnapshot)) } // // Counters // case class UserCounter(name: String) extends MetricGroupIdentity with UserMetricGroup { val category = UserCounters } case class UserCounterRecorder(counter: Counter) extends MetricGroupRecorder { def collect(context: CollectionContext): MetricGroupSnapshot = UserCounterSnapshot(counter.collect(context)) def cleanup: Unit = counter.cleanup } case class UserCounterSnapshot(counterSnapshot: Counter.Snapshot) extends MetricGroupSnapshot { type GroupSnapshotType = UserCounterSnapshot def merge(that: UserCounterSnapshot, context: CollectionContext): UserCounterSnapshot = UserCounterSnapshot(that.counterSnapshot.merge(counterSnapshot, context)) def metrics: Map[MetricIdentity, MetricSnapshot] = Map((Count, counterSnapshot)) } // // MinMaxCounters // case class UserMinMaxCounter(name: String) extends MetricGroupIdentity with UserMetricGroup { val category = UserMinMaxCounters } case class UserMinMaxCounterRecorder(minMaxCounter: MinMaxCounter) extends MetricGroupRecorder { def collect(context: CollectionContext): MetricGroupSnapshot = UserMinMaxCounterSnapshot(minMaxCounter.collect(context)) def cleanup: Unit = minMaxCounter.cleanup } case class UserMinMaxCounterSnapshot(minMaxCounterSnapshot: Histogram.Snapshot) extends MetricGroupSnapshot { type GroupSnapshotType = UserMinMaxCounterSnapshot def merge(that: UserMinMaxCounterSnapshot, context: CollectionContext): UserMinMaxCounterSnapshot = UserMinMaxCounterSnapshot(that.minMaxCounterSnapshot.merge(minMaxCounterSnapshot, context)) def metrics: Map[MetricIdentity, MetricSnapshot] = Map((RecordedValues, minMaxCounterSnapshot)) } // // Gauges // case class UserGauge(name: String) extends MetricGroupIdentity with UserMetricGroup { val category = UserGauges } case class UserGaugeRecorder(gauge: Gauge) extends MetricGroupRecorder { def collect(context: CollectionContext): MetricGroupSnapshot = UserGaugeSnapshot(gauge.collect(context)) def cleanup: Unit = gauge.cleanup } case class UserGaugeSnapshot(gaugeSnapshot: Histogram.Snapshot) extends MetricGroupSnapshot { type GroupSnapshotType = UserGaugeSnapshot def merge(that: UserGaugeSnapshot, context: CollectionContext): UserGaugeSnapshot = UserGaugeSnapshot(that.gaugeSnapshot.merge(gaugeSnapshot, context)) def metrics: Map[MetricIdentity, MetricSnapshot] = Map((RecordedValues, gaugeSnapshot)) } case object UserHistograms extends MetricGroupCategory { val name: String = "histogram" } case object UserCounters extends MetricGroupCategory { val name: String = "counter" } case object UserMinMaxCounters extends MetricGroupCategory { val name: String = "min-max-counter" } case object UserGauges extends MetricGroupCategory { val name: String = "gauge" } case object RecordedValues extends MetricIdentity { val name: String = "values" } case object Count extends MetricIdentity { val name: String = "count" } }