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