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