001package io.prometheus.metrics.model.snapshots; 002 003import io.prometheus.metrics.config.EscapingScheme; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import javax.annotation.Nullable; 008 009/** Immutable snapshot of a Counter. */ 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. See 017 * {@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 this(metadata, dataPoints, false); 022 } 023 024 private CounterSnapshot( 025 MetricMetadata metadata, Collection<CounterDataPointSnapshot> dataPoints, boolean internal) { 026 super(metadata, dataPoints, internal); 027 } 028 029 @SuppressWarnings("unchecked") 030 @Override 031 public List<CounterDataPointSnapshot> getDataPoints() { 032 return (List<CounterDataPointSnapshot>) dataPoints; 033 } 034 035 @SuppressWarnings("unchecked") 036 @Override 037 MetricSnapshot escape( 038 EscapingScheme escapingScheme, List<? extends DataPointSnapshot> dataPointSnapshots) { 039 return new CounterSnapshot( 040 getMetadata().escape(escapingScheme), 041 (List<CounterDataPointSnapshot>) dataPointSnapshots, 042 true); 043 } 044 045 public static class CounterDataPointSnapshot extends DataPointSnapshot { 046 047 private final double value; 048 @Nullable private final Exemplar exemplar; 049 050 /** 051 * To create a new {@link CounterDataPointSnapshot}, you can either call the constructor 052 * directly or use the Builder with {@link CounterDataPointSnapshot#builder()}. 053 * 054 * @param value the counter value. Must not be negative. 055 * @param labels must not be null. Use {@link Labels#EMPTY} if there are no labels. 056 * @param exemplar may be null. 057 * @param createdTimestampMillis timestamp (as in {@link System#currentTimeMillis()}) when the 058 * time series (this specific set of labels) was created (or reset to zero). It's optional. 059 * Use {@code 0L} if there is no created timestamp. 060 */ 061 public CounterDataPointSnapshot( 062 double value, Labels labels, @Nullable Exemplar exemplar, long createdTimestampMillis) { 063 this(value, labels, exemplar, createdTimestampMillis, 0); 064 } 065 066 /** 067 * Constructor with an additional scrape timestamp. This is only useful in rare cases as the 068 * scrape timestamp is usually set by the Prometheus server during scraping. Exceptions include 069 * mirroring metrics with given timestamps from other metric sources. 070 */ 071 @SuppressWarnings("this-escape") 072 public CounterDataPointSnapshot( 073 double value, 074 Labels labels, 075 @Nullable Exemplar exemplar, 076 long createdTimestampMillis, 077 long scrapeTimestampMillis) { 078 this(value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis, false); 079 } 080 081 @SuppressWarnings("this-escape") 082 public CounterDataPointSnapshot( 083 double value, 084 Labels labels, 085 @Nullable Exemplar exemplar, 086 long createdTimestampMillis, 087 long scrapeTimestampMillis, 088 boolean internal) { 089 super(labels, createdTimestampMillis, scrapeTimestampMillis, internal); 090 this.value = value; 091 this.exemplar = exemplar; 092 if (!internal) { 093 validate(); 094 } 095 } 096 097 public double getValue() { 098 return value; 099 } 100 101 @Nullable 102 public Exemplar getExemplar() { 103 return exemplar; 104 } 105 106 protected void validate() { 107 if (value < 0.0) { 108 throw new IllegalArgumentException(value + ": counters cannot have a negative value"); 109 } 110 } 111 112 @Override 113 DataPointSnapshot escape(EscapingScheme escapingScheme) { 114 return new CounterSnapshot.CounterDataPointSnapshot( 115 value, 116 SnapshotEscaper.escapeLabels(getLabels(), escapingScheme), 117 SnapshotEscaper.escapeExemplar(exemplar, escapingScheme), 118 getCreatedTimestampMillis(), 119 getScrapeTimestampMillis(), 120 true); 121 } 122 123 public static Builder builder() { 124 return new Builder(); 125 } 126 127 public static class Builder extends DataPointSnapshot.Builder<Builder> { 128 129 @Nullable private Exemplar exemplar = null; 130 @Nullable private Double value = null; 131 private long createdTimestampMillis = 0L; 132 133 private Builder() {} 134 135 /** Counter value. This is required. The value must not be negative. */ 136 public Builder value(double value) { 137 this.value = value; 138 return this; 139 } 140 141 public Builder exemplar(@Nullable Exemplar exemplar) { 142 this.exemplar = exemplar; 143 return this; 144 } 145 146 public Builder createdTimestampMillis(long createdTimestampMillis) { 147 this.createdTimestampMillis = createdTimestampMillis; 148 return this; 149 } 150 151 public CounterDataPointSnapshot build() { 152 if (value == null) { 153 throw new IllegalArgumentException("Missing required field: value is null."); 154 } 155 return new CounterDataPointSnapshot( 156 value, labels, exemplar, createdTimestampMillis, scrapeTimestampMillis); 157 } 158 159 @Override 160 protected Builder self() { 161 return this; 162 } 163 } 164 } 165 166 public static Builder builder() { 167 return new Builder(); 168 } 169 170 public static class Builder extends MetricSnapshot.Builder<Builder> { 171 172 private final List<CounterDataPointSnapshot> dataPoints = new ArrayList<>(); 173 174 private Builder() {} 175 176 /** Add a data point. Can be called multiple times to add multiple data points. */ 177 public Builder dataPoint(CounterDataPointSnapshot dataPoint) { 178 dataPoints.add(dataPoint); 179 return this; 180 } 181 182 @Override 183 public CounterSnapshot build() { 184 return new CounterSnapshot(buildMetadata(), dataPoints); 185 } 186 187 @Override 188 protected Builder self() { 189 return this; 190 } 191 } 192}