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