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 Promtheus 
065 * server than individual metrics for each labelset.
066 */
067public class Counter extends SimpleCollector<Counter.Child, Counter> {
068
069  Counter(Builder b) {
070    super(b);
071  }
072
073  public static class Builder extends SimpleCollector.Builder<Builder> {
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.
082   */
083  public static Builder build() {
084    return new Builder();
085  }
086
087  @Override
088  protected Child newChild() {
089    return new Child();
090  }
091
092  /**
093   * The value of a single Counter.
094   * <p>
095   * <em>Warning:</em> References to a Child become invalid after using
096   * {@link SimpleCollector#remove} or {@link SimpleCollector#clear},
097   */
098  public static class Child {
099    private DoubleAdder value = new DoubleAdder();
100    /**
101     * Increment the counter by 1.
102     */
103    public void inc() {
104      inc(1);
105    }
106    /**
107     * Increment the counter by the given amount.
108     * @throws IllegalArgumentException If amt is negative.
109     */
110    public void inc(double amt) {
111      if (amt < 0) {
112        throw new IllegalArgumentException("Amount to increment must be non-negative.");
113      }
114      value.add(amt);
115    }
116    /**
117     * Get the value of the counter.
118     */
119    public double get() {
120      return value.sum();
121    }
122  }
123
124  // Convenience methods.
125  /**
126   * Increment the counter with no labels by 1.
127   */
128  public void inc() {
129    inc(1);
130  }
131  /**
132   * Increment the counter with no labels by the given amount.
133   * @throws IllegalArgumentException If amt is negative.
134   */
135  public void inc(double amt) {
136    noLabelsChild.inc(amt);
137  }
138
139  @Override
140  public List<MetricFamilySamples> collect() {
141    List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
142    for(Map.Entry<List<String>, Child> c: children.entrySet()) {
143      samples.add(new MetricFamilySamples.Sample(fullname, labelNames, c.getKey(), c.getValue().get()));
144    }
145    MetricFamilySamples mfs = new MetricFamilySamples(fullname, Type.COUNTER, help, samples);
146
147    List<MetricFamilySamples> mfsList = new ArrayList<MetricFamilySamples>();
148    mfsList.add(mfs);
149    return mfsList;
150  }
151}