001package io.prometheus.metrics.core.exemplars;
002
003import io.prometheus.metrics.config.ExemplarsProperties;
004import io.prometheus.metrics.config.PrometheusProperties;
005
006import java.util.concurrent.TimeUnit;
007
008public class ExemplarSamplerConfig {
009
010    /**
011     * See {@link ExemplarsProperties#getMinRetentionPeriodSeconds()}
012     */
013    public static final int DEFAULT_MIN_RETENTION_PERIOD_SECONDS = 7;
014
015    /**
016     * See {@link ExemplarsProperties#getMaxRetentionPeriodSeconds()}
017     */
018    public static final int DEFAULT_MAX_RETENTION_PERIOD_SECONDS = 70;
019
020    /**
021     * See {@link ExemplarsProperties#getSampleIntervalMilliseconds()}
022     */
023    private static final int DEFAULT_SAMPLE_INTERVAL_MILLISECONDS = 90;
024
025    private final long minRetentionPeriodMillis;
026    private final long maxRetentionPeriodMillis;
027    private final long sampleIntervalMillis;
028    private final double[] histogramClassicUpperBounds; // null unless it's a classic histogram
029    private final int numberOfExemplars; // if histogramClassicUpperBounds != null, then numberOfExemplars == histogramClassicUpperBounds.length
030
031    /**
032     * Constructor for all metric types except classic histograms.
033     *
034     * @param properties        See {@link PrometheusProperties#getExemplarProperties()}.
035     * @param numberOfExemplars Counters have 1 Exemplar, native histograms and summaries have 4 Exemplars by default.
036     *                          For classic histogram use {@link #ExemplarSamplerConfig(ExemplarsProperties, double[])}.
037     */
038    public ExemplarSamplerConfig(ExemplarsProperties properties, int numberOfExemplars) {
039        this(properties, numberOfExemplars, null);
040    }
041
042    /**
043     * Constructor for classic histogram metrics.
044     *
045     * @param properties                  See {@link PrometheusProperties#getExemplarProperties()}.
046     * @param histogramClassicUpperBounds the ExemplarSampler will provide one Exemplar per histogram bucket.
047     *                                    Must be sorted, and must include the +Inf bucket.
048     */
049    public ExemplarSamplerConfig(ExemplarsProperties properties, double[] histogramClassicUpperBounds) {
050        this(properties, histogramClassicUpperBounds.length, histogramClassicUpperBounds);
051    }
052
053    private ExemplarSamplerConfig(ExemplarsProperties properties, int numberOfExemplars, double[] histogramClassicUpperBounds) {
054        this(
055                TimeUnit.SECONDS.toMillis(getOrDefault(properties.getMinRetentionPeriodSeconds(), DEFAULT_MIN_RETENTION_PERIOD_SECONDS)),
056                TimeUnit.SECONDS.toMillis(getOrDefault(properties.getMaxRetentionPeriodSeconds(), DEFAULT_MAX_RETENTION_PERIOD_SECONDS)),
057                getOrDefault(properties.getSampleIntervalMilliseconds(), DEFAULT_SAMPLE_INTERVAL_MILLISECONDS),
058                numberOfExemplars,
059                histogramClassicUpperBounds);
060    }
061
062    ExemplarSamplerConfig(long minRetentionPeriodMillis, long maxRetentionPeriodMillis, long sampleIntervalMillis, int numberOfExemplars, double[] histogramClassicUpperBounds) {
063        this.minRetentionPeriodMillis = minRetentionPeriodMillis;
064        this.maxRetentionPeriodMillis = maxRetentionPeriodMillis;
065        this.sampleIntervalMillis = sampleIntervalMillis;
066        this.numberOfExemplars = numberOfExemplars;
067        this.histogramClassicUpperBounds = histogramClassicUpperBounds;
068        validate();
069    }
070
071    private void validate() {
072        if (minRetentionPeriodMillis <= 0) {
073            throw new IllegalArgumentException(minRetentionPeriodMillis + ": minRetentionPeriod must be > 0.");
074        }
075        if (maxRetentionPeriodMillis <= 0) {
076            throw new IllegalArgumentException(maxRetentionPeriodMillis + ": maxRetentionPeriod must be > 0.");
077        }
078        if (histogramClassicUpperBounds != null) {
079            if (histogramClassicUpperBounds.length == 0 || histogramClassicUpperBounds[histogramClassicUpperBounds.length - 1] != Double.POSITIVE_INFINITY) {
080                throw new IllegalArgumentException("histogramClassicUpperBounds must contain the +Inf bucket.");
081            }
082            if (histogramClassicUpperBounds.length != numberOfExemplars) {
083                throw new IllegalArgumentException("histogramClassicUpperBounds.length must be equal to numberOfExemplars.");
084            }
085            double bound = histogramClassicUpperBounds[0];
086            for (int i = 1; i < histogramClassicUpperBounds.length; i++) {
087                if (bound >= histogramClassicUpperBounds[i]) {
088                    throw new IllegalArgumentException("histogramClassicUpperBounds must be sorted and must not contain duplicates.");
089                }
090            }
091        }
092        if (numberOfExemplars <= 0) {
093            throw new IllegalArgumentException(numberOfExemplars + ": numberOfExemplars must be > 0.");
094        }
095    }
096
097    private static <T> T getOrDefault(T result, T defaultValue) {
098        return result != null ? result : defaultValue;
099    }
100
101    /**
102     * May be {@code null}.
103     */
104    public double[] getHistogramClassicUpperBounds() {
105        return histogramClassicUpperBounds;
106    }
107
108    /**
109     * See {@link ExemplarsProperties#getMinRetentionPeriodSeconds()}
110     */
111    public long getMinRetentionPeriodMillis() {
112        return minRetentionPeriodMillis;
113    }
114
115    /**
116     * See {@link ExemplarsProperties#getMaxRetentionPeriodSeconds()}
117     */
118    public long getMaxRetentionPeriodMillis() {
119        return maxRetentionPeriodMillis;
120    }
121
122    /**
123     * See {@link ExemplarsProperties#getSampleIntervalMilliseconds()}
124     */
125    public long getSampleIntervalMillis() {
126        return sampleIntervalMillis;
127    }
128
129    /**
130     * Defaults: Counters have one Exemplar, native histograms and summaries have 4 Exemplars, classic histograms have one Exemplar per bucket.
131     */
132    public int getNumberOfExemplars() {
133        return numberOfExemplars;
134    }
135}