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