-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: update invalid metrics filter for prometheus-metrics 1.2.1
- Loading branch information
Showing
9 changed files
with
271 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/main/kotlin/click/dobel/shelly/exporter/ShellyExporterMetricsConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package click.dobel.shelly.exporter | ||
|
||
import click.dobel.shelly.exporter.metrics.ShellyMetrics | ||
import click.dobel.shelly.exporter.metrics.ValueFilteringPrometheusRegistry | ||
import io.micrometer.core.instrument.Meter | ||
import io.micrometer.core.instrument.config.MeterFilter | ||
import io.micrometer.core.instrument.config.MeterFilterReply | ||
import io.prometheus.metrics.model.registry.PrometheusRegistry | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
|
||
@Configuration | ||
class ShellyExporterMetricsConfiguration { | ||
|
||
companion object { | ||
const val SCRAPE_FAILURE_VALUE = Double.NaN | ||
} | ||
|
||
@Bean | ||
fun meterFilter(): MeterFilter? { | ||
return object : MeterFilter { | ||
override fun accept(id: Meter.Id): MeterFilterReply { | ||
return if (id.name.startsWith(ShellyMetrics.PREFIX)) { | ||
MeterFilterReply.ACCEPT | ||
} else { | ||
MeterFilterReply.DENY | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Bean | ||
fun prometheusRegistry(): PrometheusRegistry { | ||
return ValueFilteringPrometheusRegistry(SCRAPE_FAILURE_VALUE) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/kotlin/click/dobel/shelly/exporter/metrics/DoubleValidator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package click.dobel.shelly.exporter.metrics | ||
|
||
class DoubleValidator(private val invalidValue: Double) { | ||
private val blockInfinite = invalidValue.isInfinite() | ||
private val blockNaNs = invalidValue.isNaN() | ||
|
||
fun isValid(value: Double): Boolean { | ||
// special cases for NaN and Infinity: can't be compared using equality. | ||
return !(blockInfinite && value.isInfinite()) && | ||
!(blockNaNs && value.isNaN()) && | ||
(invalidValue != value) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 0 additions & 62 deletions
62
src/main/kotlin/click/dobel/shelly/exporter/metrics/ValueFilteringCollectorRegistry.kt
This file was deleted.
Oops, something went wrong.
85 changes: 85 additions & 0 deletions
85
src/main/kotlin/click/dobel/shelly/exporter/metrics/ValueFilteringPrometheusRegistry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package click.dobel.shelly.exporter.metrics | ||
|
||
import io.prometheus.metrics.model.registry.PrometheusRegistry | ||
import io.prometheus.metrics.model.registry.PrometheusScrapeRequest | ||
import io.prometheus.metrics.model.snapshots.CounterSnapshot | ||
import io.prometheus.metrics.model.snapshots.DataPointSnapshot | ||
import io.prometheus.metrics.model.snapshots.GaugeSnapshot | ||
import io.prometheus.metrics.model.snapshots.MetricMetadata | ||
import io.prometheus.metrics.model.snapshots.MetricSnapshots | ||
import mu.KLogging | ||
import java.util.function.Predicate | ||
|
||
class ValueFilteringPrometheusRegistry( | ||
invalidValue: Double, | ||
) : PrometheusRegistry() { | ||
|
||
private val doubleValidator = DoubleValidator(invalidValue) | ||
|
||
companion object : KLogging() | ||
|
||
private fun Double.isValid(metricName: String): Boolean { | ||
return doubleValidator.isValid(this) | ||
.also { valid -> | ||
if (!valid) { | ||
logger.debug { "Suppressing invalid reading $this of metric $metricName." } | ||
} | ||
} | ||
} | ||
|
||
private fun metricName(metadata: MetricMetadata, dataPointSnapshot: DataPointSnapshot): String { | ||
val labels = dataPointSnapshot.labels.joinToString(prefix = "{", postfix = "}") { "${it.name}='${it.value}'" } | ||
return "${metadata.name}${labels}" | ||
} | ||
|
||
override fun scrape(): MetricSnapshots { | ||
return super.scrape().removeInvalidDataPoints() | ||
} | ||
|
||
override fun scrape(includedNames: Predicate<String>?): MetricSnapshots { | ||
return super.scrape(includedNames).removeInvalidDataPoints() | ||
} | ||
|
||
override fun scrape(scrapeRequest: PrometheusScrapeRequest?): MetricSnapshots { | ||
return super.scrape(scrapeRequest).removeInvalidDataPoints() | ||
} | ||
|
||
override fun scrape(includedNames: Predicate<String>?, scrapeRequest: PrometheusScrapeRequest?): MetricSnapshots { | ||
return super.scrape(includedNames, scrapeRequest).removeInvalidDataPoints() | ||
} | ||
|
||
private fun MetricSnapshots.removeInvalidDataPoints(): MetricSnapshots { | ||
val results = MetricSnapshots.builder() | ||
|
||
this.forEach { metricSnapshot -> | ||
val filteredSnapshot = when (metricSnapshot) { | ||
is CounterSnapshot -> { | ||
CounterSnapshot( | ||
metricSnapshot.metadata, | ||
metricSnapshot.dataPoints.filter { dataPointSnapshot -> | ||
dataPointSnapshot.value.isValid(metricName(metricSnapshot.metadata, dataPointSnapshot)) | ||
} | ||
) | ||
} | ||
|
||
is GaugeSnapshot -> { | ||
GaugeSnapshot( | ||
metricSnapshot.metadata, | ||
metricSnapshot.dataPoints.filter { dataPointSnapshot -> | ||
dataPointSnapshot.value.isValid(metricName(metricSnapshot.metadata, dataPointSnapshot)) | ||
} | ||
) | ||
} | ||
|
||
else -> { | ||
metricSnapshot | ||
} | ||
} | ||
if (filteredSnapshot.dataPoints.isNotEmpty()) { | ||
results.metricSnapshot(filteredSnapshot) | ||
} | ||
} | ||
|
||
return results.build() | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
src/test/kotlin/click/dobel/shelly/exporter/metrics/DoubleValidatorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package click.dobel.shelly.exporter.metrics | ||
|
||
import io.kotest.core.spec.style.FreeSpec | ||
import io.kotest.matchers.shouldBe | ||
|
||
class DoubleValidatorTest : FreeSpec({ | ||
|
||
"DoubleValidator" - { | ||
"configured with NaN as invalid value" - { | ||
val validator = DoubleValidator(Double.NaN) | ||
|
||
"should not accept NaN" { validator.isValid(Double.NaN) shouldBe false } | ||
"should accept 0.0" { validator.isValid(0.0) shouldBe true } | ||
"should accept 5.0" { validator.isValid(5.0) shouldBe true } | ||
"should accept Infinity" { validator.isValid(Double.POSITIVE_INFINITY) shouldBe true } | ||
"should accept -Infinity" { validator.isValid(Double.NEGATIVE_INFINITY) shouldBe true } | ||
} | ||
|
||
"configured with 0.0 as invalid value" - { | ||
val validator = DoubleValidator(0.0) | ||
|
||
"should not accept 0.0" { validator.isValid(0.0) shouldBe false } | ||
"should accept 5.0" { validator.isValid(5.0) shouldBe true } | ||
"should accept NaN" { validator.isValid(Double.NaN) shouldBe true } | ||
"should accept Infinity" { validator.isValid(Double.POSITIVE_INFINITY) shouldBe true } | ||
"should accept -Infinity" { validator.isValid(Double.NEGATIVE_INFINITY) shouldBe true } | ||
} | ||
"configured with Infinity as invalid value" - { | ||
val validator = DoubleValidator(Double.NEGATIVE_INFINITY) | ||
|
||
"should not accept Infinity" { validator.isValid(Double.POSITIVE_INFINITY) shouldBe false } | ||
"should not accept -Infinity" { validator.isValid(Double.NEGATIVE_INFINITY) shouldBe false } | ||
"should accept 0.0" { validator.isValid(0.0) shouldBe true } | ||
"should accept 5.0" { validator.isValid(5.0) shouldBe true } | ||
"should accept NaN" { validator.isValid(Double.NaN) shouldBe true } | ||
} | ||
} | ||
}) |
39 changes: 0 additions & 39 deletions
39
src/test/kotlin/click/dobel/shelly/exporter/metrics/ValueFilteringCollectorRegistryTest.kt
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.