io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils Maven / Gradle / Ivy
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.metrics.internal.aggregator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Utilities for interacting with explicit bucket histograms.
*
* This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class ExplicitBucketHistogramUtils {
private ExplicitBucketHistogramUtils() {}
public static final List DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES =
Collections.unmodifiableList(
Arrays.asList(
0d, 5d, 10d, 25d, 50d, 75d, 100d, 250d, 500d, 750d, 1_000d, 2_500d, 5_000d, 7_500d,
10_000d));
/** Converts bucket boundary "convenient" configuration into the "more efficient" array. */
public static double[] createBoundaryArray(List boundaries) {
validateBucketBoundaries(boundaries);
return boundaries.stream().mapToDouble(i -> i).toArray();
}
/**
* Finds the bucket index for a histogram.
*
* @param boundaries the array of bucket boundaries.
* @param value The current measurement value
* @return The bucket index where the value should be recorded.
*/
public static int findBucketIndex(double[] boundaries, double value) {
// Benchmark shows that linear search performs better than binary search with ordinary
// buckets.
for (int i = 0; i < boundaries.length; ++i) {
if (value <= boundaries[i]) {
return i;
}
}
return boundaries.length;
}
/**
* Validates errors in boundary configuration.
*
* @param boundaries The array of bucket boundaries.
* @throws IllegalArgumentException if boundaries are not specified correctly.
*/
public static void validateBucketBoundaries(List boundaries) {
for (double v : boundaries) {
if (Double.isNaN(v)) {
throw new IllegalArgumentException("invalid bucket boundary: NaN");
}
}
for (int i = 1; i < boundaries.size(); ++i) {
if (boundaries.get(i - 1) >= boundaries.get(i)) {
throw new IllegalArgumentException(
"Bucket boundaries must be in increasing order: "
+ boundaries.get(i - 1)
+ " >= "
+ boundaries.get(i));
}
}
if (!boundaries.isEmpty()) {
if (boundaries.get(0) == Double.NEGATIVE_INFINITY) {
throw new IllegalArgumentException("invalid bucket boundary: -Inf");
}
if (boundaries.get(boundaries.size() - 1) == Double.POSITIVE_INFINITY) {
throw new IllegalArgumentException("invalid bucket boundary: +Inf");
}
}
}
}