001package io.prometheus.client;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Map;
007
008/**
009 * Counter metric, to track counts of events or running totals.
010 * <p>
011 * Example of Counters include:
012 * <ul>
013 *  <li>Number of requests processed</li>
014 *  <li>Number of items that were inserted into a queue</li>
015 *  <li>Total amount of data a system has processed</li>
016 * </ul>
017 *
018 * Counters can only go up (and be reset), if your use case can go down you should use a {@link Gauge} instead.
019 * Use the <code>rate()</code> function in Prometheus to calculate the rate of increase of a Counter.
020 * By convention, the names of Counters are suffixed by <code>_total</code>.
021 *
022 * <p>
023 * An example Counter:
024 * <pre>
025 * {@code
026 *   class YourClass {
027 *     static final Counter requests = Counter.build()
028 *         .name("requests_total").help("Total requests.").register();
029 *     static final Counter failedRequests = Counter.build()
030 *         .name("requests_failed_total").help("Total failed requests.").register();
031 *
032 *     void processRequest() {
033 *        requests.inc();
034 *        try {
035 *          // Your code here.
036 *        } catch (Exception e) {
037 *          failedRequests.inc();
038 *          throw e;
039 *        }
040 *     }
041 *   }
042 * }
043 * </pre>
044 *
045 * <p>
046 * You can also use labels to track different types of metric:
047 * <pre>
048 * {@code
049 *   class YourClass {
050 *     static final Counter requests = Counter.build()
051 *         .name("requests_total").help("Total requests.")
052 *         .labelNames("method").register();
053 *
054 *     void processGetRequest() {
055 *        requests.labels("get").inc();
056 *        // Your code here.
057 *     }
058 *     void processPostRequest() {
059 *        requests.labels("post").inc();
060 *        // Your code here.
061 *     }
062 *   }
063 * }
064 * </pre>
065 * These can be aggregated and processed together much more easily in the Prometheus
066 * server than individual metrics for each labelset.
067 */
068public class Counter extends SimpleCollector<Counter.Child> implements Collector.Describable {
069
070  Counter(Builder b) {
071    super(b);
072  }
073
074  public static class Builder extends SimpleCollector.Builder<Builder, Counter> {
075    @Override
076    public Counter create() {
077      return new Counter(this);
078    }
079  }
080
081  /**
082   *  Return a Builder to allow configuration of a new Counter. Ensures required fields are provided.
083   *
084   *  @param name The name of the metric
085   *  @param help The help string of the metric
086   */
087  public static Builder build(String name, String help) {
088    return new Builder().name(name).help(help);
089  }
090
091  /**
092   *  Return a Builder to allow configuration of a new Counter.
093   */
094  public static Builder build() {
095    return new Builder();
096  }
097
098  @Override
099  protected Child newChild() {
100    return new Child();
101  }
102
103  /**
104   * The value of a single Counter.
105   * <p>
106   * <em>Warning:</em> References to a Child become invalid after using
107   * {@link SimpleCollector#remove} or {@link SimpleCollector#clear},
108   */
109  public static class Child {
110    private final DoubleAdder value = new DoubleAdder();
111    /**
112     * Increment the counter by 1.
113     */
114    public void inc() {
115      inc(1);
116    }
117    /**
118     * Increment the counter by the given amount.
119     * @throws IllegalArgumentException If amt is negative.
120     */
121    public void inc(double amt) {
122      if (amt < 0) {
123        throw new IllegalArgumentException("Amount to increment must be non-negative.");
124      }
125      value.add(amt);
126    }
127    /**
128     * Get the value of the counter.
129     */
130    public double get() {
131      return value.sum();
132    }
133  }
134
135  // Convenience methods.
136  /**
137   * Increment the counter with no labels by 1.
138   */
139  public void inc() {
140    inc(1);
141  }
142  /**
143   * Increment the counter with no labels by the given amount.
144   * @throws IllegalArgumentException If amt is negative.
145   */
146  public void inc(double amt) {
147    noLabelsChild.inc(amt);
148  }
149  
150  /**
151   * Get the value of the counter.
152   */
153  public double get() {
154    return noLabelsChild.get();
155  }
156
157  @Override
158  public List<MetricFamilySamples> collect() {
159    List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(children.size());
160    for(Map.Entry<List<String>, Child> c: children.entrySet()) {
161      samples.add(new MetricFamilySamples.Sample(fullname, labelNames, c.getKey(), c.getValue().get()));
162    }
163    return familySamplesList(Type.COUNTER, samples);
164  }
165
166  @Override
167  public List<MetricFamilySamples> describe() {
168    return Collections.<MetricFamilySamples>singletonList(new CounterMetricFamily(fullname, help, labelNames));
169  }
170}