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