Skip to content

Commit

Permalink
Merge pull request #1011 from pnerg/distributions-as-gauges
Browse files Browse the repository at this point in the history
(prometheus) Added the possibility to generate gauge metrics from distribution based metrics
  • Loading branch information
SimunKaracic authored May 10, 2021
2 parents 1b04e8f + 7d42c95 commit e668e82
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 7 deletions.
13 changes: 12 additions & 1 deletion reporters/kamon-prometheus/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ kamon.prometheus {
}

summaries {
# Names of the histogram, timer and range sampler metrics that must be exported as summaries. All patterns are treared as Glob patterns.
# Names of the histogram, timer and range sampler metrics that must be exported as summaries. All patterns are treated as Glob patterns.
metrics = [
# example:
# "request*",
Expand All @@ -77,6 +77,17 @@ kamon.prometheus {
]
}

gauges {
# Names of the histogram, timer and range sampler metrics that shall be exported as a gauges as well.
# For each matching metric there will be three gauges representing the current seen distribution (min, max, avg)
# All patterns are treated as Glob patterns.
metrics = [
# example:
# "http.server*",
# "*akka*"
]
}

embedded-server {

# Hostname and port used by the embedded web server to publish the scraping enpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class PrometheusReporter(configPath: String = DefaultConfigPath, initialConfig:

scrapeDataBuilder.appendCounters(currentData.counters)
scrapeDataBuilder.appendGauges(currentData.gauges)
scrapeDataBuilder.appendDistributionMetricsAsGauges(snapshot.rangeSamplers ++ snapshot.histograms ++ snapshot.timers)
scrapeDataBuilder.appendHistograms(currentData.histograms)
scrapeDataBuilder.appendHistograms(currentData.timers)
scrapeDataBuilder.appendHistograms(currentData.rangeSamplers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ object PrometheusSettings {
informationBuckets: Seq[java.lang.Double],
customBuckets: Map[String, Seq[java.lang.Double]],
includeEnvironmentTags: Boolean,
summarySettings: SummarySettings
summarySettings: SummarySettings,
gaugeSettings: GaugeSettings
)

case class SummarySettings(
quantiles: Seq[java.lang.Double],
metricMatchers: Seq[Glob]
)

case class GaugeSettings(metricMatchers: Seq[Glob])

def readSettings(prometheusConfig: Config): Generic = {
Generic(
defaultBuckets = prometheusConfig.getDoubleList("buckets.default-buckets").asScala.toSeq,
Expand All @@ -49,6 +52,9 @@ object PrometheusSettings {
summarySettings = SummarySettings(
quantiles = prometheusConfig.getDoubleList("summaries.quantiles").asScala.toSeq,
metricMatchers = prometheusConfig.getStringList("summaries.metrics").asScala.map(Glob).toSeq
),
gaugeSettings = GaugeSettings(
metricMatchers = prometheusConfig.getStringList("gauges.metrics").asScala.map(Glob).toSeq
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ package kamon.prometheus
import java.lang.StringBuilder
import java.text.{DecimalFormat, DecimalFormatSymbols}
import java.util.Locale

import kamon.metric.{Distribution, MeasurementUnit, MetricSnapshot}
import kamon.metric.{Distribution, Instrument, MeasurementUnit, Metric, MetricSnapshot}
import kamon.metric.MeasurementUnit.{information, time}
import kamon.metric.MeasurementUnit.Dimension._
import kamon.tag.TagSet
Expand Down Expand Up @@ -50,6 +49,39 @@ class ScrapeDataBuilder(prometheusConfig: PrometheusSettings.Generic, environmen
this
}

def appendDistributionMetricsAsGauges(distributions: Seq[MetricSnapshot.Distributions]): ScrapeDataBuilder = {
def gaugeFilter(metric:MetricSnapshot.Distributions):Boolean = prometheusConfig.gaugeSettings.metricMatchers.exists(_.accept(metric.name))
def avg(snap:Instrument.Snapshot[Distribution]):Double = if(snap.value.count == 0) 0 else snap.value.sum/snap.value.count

distributions
.filter(gaugeFilter)
.map{ metric =>
val settings = Metric.Settings.ForValueInstrument(metric.settings.unit, metric.settings.autoUpdateInterval)
Seq(
MetricSnapshot.ofValues(
name = metric.name+".min",
description = metric.description,
settings = settings,
instruments = metric.instruments.map(snap => Instrument.Snapshot[Double](snap.tags, snap.value.min.toDouble))
),
MetricSnapshot.ofValues(
name = metric.name+".max",
description = metric.description,
settings = settings,
instruments = metric.instruments.map(snap => Instrument.Snapshot[Double](snap.tags, snap.value.max.toDouble))
),
MetricSnapshot.ofValues(
name = metric.name+".avg",
description = metric.description,
settings = settings,
instruments = metric.instruments.map(snap => Instrument.Snapshot[Double](snap.tags, avg(snap)))
)
)
}.flatten
.foreach(appendGaugeMetric)
this
}

private def appendCounterMetric(metric: MetricSnapshot.Values[Long]): Unit = {
val unit = metric.settings.unit
val normalizedMetricName = normalizeCounterMetricName(metric.name, unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package kamon.prometheus
import com.typesafe.config.ConfigFactory
import kamon.metric.MeasurementUnit.{information, none, time}
import kamon.metric.{MeasurementUnit, MetricSnapshot}
import kamon.prometheus.PrometheusSettings.SummarySettings
import kamon.prometheus.PrometheusSettings.{GaugeSettings, SummarySettings}
import kamon.tag.TagSet
import kamon.testkit.MetricSnapshotBuilder
import kamon.util.Filter.Glob
Expand Down Expand Up @@ -320,6 +320,43 @@ class ScrapeDataBuilderSpec extends WordSpec with Matchers {

}

"append gauges only for matching metrics" in {
val histogramWithHundredEntries = constantDistribution("firstMetric", none, 1, 100)
val histogramWithHundredEntriesTwo = constantDistribution("secondMetric", none, 1, 100)
val result = builder(withGauges = Seq("first*"))
.appendDistributionMetricsAsGauges(Seq(histogramWithHundredEntries, histogramWithHundredEntriesTwo))
.build()
println(result)
result should include {
"""|# TYPE firstMetric_min gauge
|firstMetric_min 1.0
|""".stripMargin
}
result should include {
"""|# TYPE firstMetric_max gauge
|firstMetric_max 100.0
|""".stripMargin
}
result should include {
"""|# TYPE firstMetric_avg gauge
|firstMetric_avg 50.0
|""".stripMargin
}

result should not include {
"""|# TYPE secondMetric_min gauge
|""".stripMargin
}
result should not include {
"""|# TYPE secondMetric_max gauge
|""".stripMargin
}
result should not include {
"""|# TYPE secondMetric_avg gauge
|""".stripMargin
}
}

"include global custom tags from the Kamon.environment.tags" in {
val counterOne = MetricSnapshotBuilder.counter("counter-one", "", TagSet.of("tag.with.dots", "value"), time.seconds, 10)
val gaugeOne = MetricSnapshotBuilder.gauge("gauge-one", "", TagSet.Empty, time.seconds, 20)
Expand Down Expand Up @@ -358,15 +395,17 @@ class ScrapeDataBuilderSpec extends WordSpec with Matchers {
private def builder(buckets: Seq[java.lang.Double] = Seq(5D, 7D, 8D, 9D, 10D, 11D, 12D),
customBuckets: Map[String, Seq[java.lang.Double]] = Map("histogram.custom-buckets" -> Seq(1D, 3D)),
environmentTags: TagSet = TagSet.Empty,
withSummary: Seq[String] = Seq.empty) = {
withSummary: Seq[String] = Seq.empty,
withGauges: Seq[String] = Seq.empty) = {
new ScrapeDataBuilder(
PrometheusSettings.Generic(
buckets,
buckets,
buckets,
customBuckets,
false,
SummarySettings(Seq(0.5, 0.75, 0.95, 0.99), withSummary.map(Glob))),
SummarySettings(Seq(0.5, 0.75, 0.95, 0.99), withSummary.map(Glob)),
GaugeSettings(withGauges.map(Glob))),
environmentTags
)
}
Expand Down

0 comments on commit e668e82

Please sign in to comment.