001package io.prometheus.metrics.model.snapshots;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.List;
006
007/** Immutable snapshot of a Counter. */
008public class CounterSnapshot extends MetricSnapshot {
009
010  /**
011   * To create a new {@link CounterSnapshot}, you can either call the constructor directly or use
012   * the builder with {@link CounterSnapshot#builder()}.
013   *
014   * @param metadata the metric name in metadata must not include the {@code _total} suffix. See
015   *     {@link MetricMetadata} for more naming conventions.
016   * @param dataPoints the constructor will create a sorted copy of the collection.
017   */
018  public CounterSnapshot(MetricMetadata metadata, Collection<CounterDataPointSnapshot> dataPoints) {
019    super(metadata, dataPoints);
020  }
021
022  @SuppressWarnings("unchecked")
023  @Override
024  public List<CounterDataPointSnapshot> getDataPoints() {
025    return (List<CounterDataPointSnapshot>) dataPoints;
026  }
027
028  public static class CounterDataPointSnapshot extends DataPointSnapshot {
029
030    private final double value;
031    private final Exemplar exemplar; // may be null
032
033    /**
034     * To create a new {@link CounterDataPointSnapshot}, you can either call the constructor
035     * directly or use the Builder with {@link CounterDataPointSnapshot#builder()}.
036     *
037     * @param value the counter value. Must not be negative.
038     * @param labels must not be null. Use {@link Labels#EMPTY} if there are no labels.
039     * @param exemplar may be null.
040     * @param createdTimestampMillis timestamp (as in {@link System#currentTimeMillis()}) when the
041     *     time series (this specific set of labels) was created (or reset to zero). It's optional.
042     *     Use {@code 0L} if there is no created timestamp.
043     */
044    public CounterDataPointSnapshot(
045        double value, Labels labels, Exemplar exemplar, long createdTimestampMillis) {
046      this(value, labels, exemplar, createdTimestampMillis, 0);
047    }
048
049    /**
050     * Constructor with an additional scrape timestamp. This is only useful in rare cases as the
051     * scrape timestamp is usually set by the Prometheus server during scraping. Exceptions include
052     * mirroring metrics with given timestamps from other metric sources.
053     */
054    @SuppressWarnings("this-escape")
055    public CounterDataPointSnapshot(
056        double value,
057        Labels labels,
058        Exemplar exemplar,
059        long createdTimestampMillis,
060        long scrapeTimestampMillis) {
061      super(labels, createdTimestampMillis, scrapeTimestampMillis);
062      this.value = value;
063      this.exemplar = exemplar;
064      validate();
065    }
066
067    public double getValue() {
068      return value;
069    }
070
071    /** May be {@code null}. */
072    public Exemplar getExemplar() {
073      return exemplar;
074    }
075
076    protected void validate() {
077      if (value < 0.0) {
078        throw new IllegalArgumentException(value + ": counters cannot have a negative value");
079      }
080    }
081
082    public static Builder builder() {
083      return new Builder();
084    }
085
086    public static class Builder extends DataPointSnapshot.Builder<Builder> {
087
088      private Exemplar exemplar = null;
089      private Double value = null;
090      private long createdTimestampMillis = 0L;
091
092      private Builder() {}
093
094      /** Counter value. This is required. The value must not be negative. */
095      public Builder value(double value) {
096        this.value = value;
097        return this;
098      }
099
100      public Builder exemplar(Exemplar exemplar) {
101        this.exemplar = exemplar;
102        return this;
103      }
104
105      public Builder createdTimestampMillis(long createdTimestampMillis) {
106        this.createdTimestampMillis = createdTimestampMillis;
107        return this;
108      }
109
110      public CounterDataPointSnapshot build() {
111        if (value == null) {
112          throw new IllegalArgumentException("Missing required field: value is null.");
113        }
114        return new CounterDataPointSnapshot(
115            value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis);
116      }
117
118      @Override
119      protected Builder self() {
120        return this;
121      }
122    }
123  }
124
125  public static Builder builder() {
126    return new Builder();
127  }
128
129  public static class Builder extends MetricSnapshot.Builder<Builder> {
130
131    private final List<CounterDataPointSnapshot> dataPoints = new ArrayList<>();
132
133    private Builder() {}
134
135    /** Add a data point. Can be called multiple times to add multiple data points. */
136    public Builder dataPoint(CounterDataPointSnapshot dataPoint) {
137      dataPoints.add(dataPoint);
138      return this;
139    }
140
141    @Override
142    public CounterSnapshot build() {
143      return new CounterSnapshot(buildMetadata(), dataPoints);
144    }
145
146    @Override
147    protected Builder self() {
148      return this;
149    }
150  }
151}