package kamon
package metric
import java.util.concurrent.TimeUnit
import com.typesafe.config.Config
import kamon.metric.InstrumentFactory.CustomInstrumentSettings
import kamon.util.MeasurementUnit
import scala.concurrent.duration._
private[kamon] class InstrumentFactory private (defaultHistogramDynamicRange: DynamicRange, defaultMMCounterDynamicRange: DynamicRange,
defaultMMCounterSampleInterval: Duration, customSettings: Map[String, CustomInstrumentSettings]) {
def buildHistogram(dynamicRange: Option[DynamicRange])(name: String, tags: Map[String, String], unit: MeasurementUnit): SnapshotableHistogram =
new HdrHistogram(name, tags, unit, instrumentDynamicRange(name, dynamicRange.getOrElse(defaultHistogramDynamicRange)))
def buildMinMaxCounter(dynamicRange: Option[DynamicRange], sampleInterval: Option[Duration])
(name: String, tags: Map[String, String], unit: MeasurementUnit): SnapshotableMinMaxCounter =
new PaddedMinMaxCounter(
name,
tags,
buildHistogram(dynamicRange.orElse(Some(defaultMMCounterDynamicRange)))(name, tags, unit),
instrumentSampleInterval(name, sampleInterval.getOrElse(defaultMMCounterSampleInterval)))
def buildGauge(name: String, tags: Map[String, String], unit: MeasurementUnit): SnapshotableGauge =
new AtomicLongGauge(name, tags, unit)
def buildCounter(name: String, tags: Map[String, String], unit: MeasurementUnit): SnapshotableCounter =
new LongAdderCounter(name, tags, unit)
private def instrumentDynamicRange(instrumentName: String, dynamicRange: DynamicRange): DynamicRange =
customSettings.get(instrumentName).fold(dynamicRange) { cs =>
overrideDynamicRange(dynamicRange, cs)
}
private def instrumentSampleInterval(instrumentName: String, sampleInterval: Duration): Duration =
customSettings.get(instrumentName).fold(sampleInterval) { cs =>
cs.sampleInterval.getOrElse(sampleInterval)
}
private def overrideDynamicRange(defaultDynamicRange: DynamicRange, customSettings: CustomInstrumentSettings): DynamicRange =
DynamicRange(
customSettings.lowestDiscernibleValue.getOrElse(defaultDynamicRange.lowestDiscernibleValue),
customSettings.highestTrackableValue.getOrElse(defaultDynamicRange.highestTrackableValue),
customSettings.significantValueDigits.getOrElse(defaultDynamicRange.significantValueDigits)
)
}
object InstrumentFactory {
def fromConfig(config: Config): InstrumentFactory = {
val factoryConfig = config.getConfig("kamon.metric.instrument-factory")
val histogramDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.histogram"))
val mmCounterDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.min-max-counter"))
val mmCounterSampleInterval = factoryConfig.getDuration("default-settings.min-max-counter.sample-interval", TimeUnit.MILLISECONDS)
val customSettings = factoryConfig.getConfig("custom-settings")
.configurations
.filter(nonEmptySection)
.map(readCustomInstrumentSettings)
new InstrumentFactory(histogramDynamicRange, mmCounterDynamicRange, mmCounterSampleInterval.millis, customSettings)
}
private def nonEmptySection(entry: (String, Config)): Boolean = entry match {
case (_, config) => config.firstLevelKeys.nonEmpty
}
private def readCustomInstrumentSettings(entry: (String, Config)): (String, CustomInstrumentSettings) = {
val (metricName, metricConfig) = entry
val customSettings = CustomInstrumentSettings(
if (metricConfig.hasPath("lowest-discernible-value")) Some(metricConfig.getLong("lowest-discernible-value")) else None,
if (metricConfig.hasPath("highest-trackable-value")) Some(metricConfig.getLong("highest-trackable-value")) else None,
if (metricConfig.hasPath("significant-value-digits")) Some(metricConfig.getInt("significant-value-digits")) else None,
if (metricConfig.hasPath("sample-interval")) Some(metricConfig.getDuration("sample-interval", TimeUnit.MILLISECONDS).millis) else None
)
(metricName -> customSettings)
}
private def readDynamicRange(config: Config): DynamicRange =
DynamicRange(
lowestDiscernibleValue = config.getLong("lowest-discernible-value"),
highestTrackableValue = config.getLong("highest-trackable-value"),
significantValueDigits = config.getInt("significant-value-digits")
)
private case class CustomInstrumentSettings(
lowestDiscernibleValue: Option[Long],
highestTrackableValue: Option[Long],
significantValueDigits: Option[Int],
sampleInterval: Option[Duration]
)
}