DistributionSummary with 0.0 as minimum expected value or SLO

101 Views Asked by At

When I'm attempting to create a DistributionSummary instance with a minimum expected value or Service Level Objective (SLO) of 0.0, I encounter the exceptions below.

However, when I use distributionSummary.record(0.0), 0.0 is accepted without throwing an exception. What's the reason for this (seems to be) inconsistent behavior?

Here we're primarily utilizing this DistributionSummary for integer values, without fractional parts. Using, for example, 0.001 for minimumExpectedValue and SLO works but seems unconventional.

Sample code:

final PrometheusMeterRegistry registry =
        new PrometheusMeterRegistry(PrometheusConfig.DEFAULT).throwExceptionOnRegistrationFailure();

final DistributionSummary distributionSummary = DistributionSummary.builder("my_metric")
        .minimumExpectedValue(0.0)
        .maximumExpectedValue(100.0)
        .publishPercentileHistogram()
        .serviceLevelObjectives(0.0, 1, 2, 5, 10, 20, 50, 100)
        .baseUnit("listeners")
        .register(registry);

distributionSummary.record(0.0);

System.out.println(registry.scrape());

.minimumExpectedValue(0.0) throws this:

io.micrometer.core.instrument.config.InvalidConfigurationException: Invalid distribution configuration: minimumExpectedValue (0.0) must be greater than 0.
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.rejectConfig(DistributionStatisticConfig.java:503)
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.validate(DistributionStatisticConfig.java:478)
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.build(DistributionStatisticConfig.java:460)
    at io.micrometer.core.instrument.DistributionSummary$Builder.register(DistributionSummary.java:418)
    at io.micrometer.core.instrument.DistributionSummary$Builder.register(DistributionSummary.java:413)
    ....

.serviceLevelObjectives(0, ...) throws this:

io.micrometer.core.instrument.config.InvalidConfigurationException: Invalid distribution configuration: serviceLevelObjectiveBoundaries must contain only the values greater than 0. Found 0.0
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.rejectConfig(DistributionStatisticConfig.java:503)
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.validate(DistributionStatisticConfig.java:495)
    at io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder.build(DistributionStatisticConfig.java:460)
    at io.micrometer.core.instrument.DistributionSummary$Builder.register(DistributionSummary.java:418)
    at io.micrometer.core.instrument.DistributionSummary$Builder.register(DistributionSummary.java:413)
    ...

Used dependencies:

  • micrometer-core:1.12.3
  • micrometer-registry-prometheus:1.11.3
2

There are 2 best solutions below

2
VonC On

From what I can see, the Micrometer's DistributionSummary is designed to measure the distribution of events, such as request sizes or method execution times. Both the minimum expected value and SLOs are used to configure how this distribution is analyzed and reported.

The configuration phase enforces that both the minimum expected value and SLOs are greater than 0.0.
I suspect it reflects the intention to measure and analyze positive distributions, where a 0.0 might represent either an absence of measurement or an edge case not intended for typical statistical analysis.

Recording a value of 0.0 is allowed because it is considered a valid measurement, representing the lower bound of what can be recorded.
That capability makes sure use cases requiring the tracking of zero values (e.g., no resource usage, immediate response times) are supported.

This distinction exists because:

  • the recording of values (including 0) is a runtime operation reflecting actual measurements,
  • whereas the configuration of minimum expected values and SLOs defines the statistical model and expectations against which those measurements are evaluated.

As you have noted, setting the minimum expected value or SLOs to a small, positive value (like 0.001) is a workaround. While it might feel unconventional, it aligns with the framework's constraints and still allows you to capture the range of values you are interested in.

final PrometheusMeterRegistry registry =
        new PrometheusMeterRegistry(PrometheusConfig.DEFAULT).throwExceptionOnRegistrationFailure();

final DistributionSummary distributionSummary = DistributionSummary.builder("my_metric")
        .minimumExpectedValue(0.001) // Adjusted from 0.0 to 0.001
        .maximumExpectedValue(100.0)
        .publishPercentileHistogram()
        .serviceLevelObjectives(0.001, 1, 2, 5, 10, 20, 50, 100) // Adjusted 0.0 to 0.001
        .baseUnit("listeners")
        .register(registry);

distributionSummary.record(0.0); // Still recording 0.0 as a valid value

System.out.println(registry.scrape());

If the above adjustment does not meet your needs (perhaps because treating 0.0 as a special case is critical to your application's metrics analysis) then you might need to explore other options.

  • either use other metrics collection and analysis systems (InfluxDB, Graphite, StatsD, Datadog, ...).

  • or implement custom metrics handling within your application. That could involve using basic Micrometer metrics types (like counters and gauges) in a custom way to track and analyze 0.0 values explicitly. For example, you could use a separate counter to track occurrences of 0.0 and other metrics to capture the rest of the distribution.

0
Jonatan Ivanov On

As far as I remember, in some histogram implementations a zero bucket might cause a divide-by-zero issue but I'm not 100% sure.

minimumExpectedValue creates an SLO bucket with that value which is also validated and prevented because of the above.

It's a little bit connected that right now you cannot record negative values using a DistributionSummary so a zero bucket (how many values are less than or equal to zero) is essentially how many zero values you recorded (which is basically a zero counter). You can do two things:

  1. Set your SLO bucket/minimumExpectedValue to a very small number, e.g.: .serviceLevelObjectives(1e-100)
  2. Utilize a separate Counter to count zero values

Please feel free to comment, on this issue: https://github.com/micrometer-metrics/micrometer/issues/2550