001package io.prometheus.client;
002
003import java.io.Closeable;
004import java.util.ArrayList;
005import java.util.Collections;
006import java.util.List;
007import java.util.Map;
008
009/**
010 * Gauge metric, to report instantaneous values.
011 * <p>
012 * Examples of Gauges include:
013 * <ul>
014 *  <li>Inprogress requests</li>
015 *  <li>Number of items in a queue</li>
016 *  <li>Free memory</li>
017 *  <li>Total memory</li>
018 *  <li>Temperature</li>
019 * </ul>
020 *
021 * Gauges can go both up and down.
022 * <p>
023 * An example Gauge:
024 * <pre>
025 * {@code
026 *   class YourClass {
027 *     static final Gauge inprogressRequests = Gauge.build()
028 *         .name("inprogress_requests").help("Inprogress requests.").register();
029 *
030 *     void processRequest() {
031 *        inprogressRequest.inc();
032 *        // Your code here.
033 *        inprogressRequest.dec();
034 *     }
035 *   }
036 * }
037 * </pre>
038 *
039 * <p>
040 * You can also use labels to track different types of metric:
041 * <pre>
042 * {@code
043 *   class YourClass {
044 *     static final Gauge inprogressRequests = Gauge.build()
045 *         .name("inprogress_requests").help("Inprogress requests.")
046 *         .labelNames("method").register();
047 *
048 *     void processGetRequest() {
049 *        inprogressRequests.labels("get").inc();
050 *        // Your code here.
051 *        inprogressRequests.labels("get").dec();
052 *     }
053 *     void processPostRequest() {
054 *        inprogressRequests.labels("post").inc();
055 *        // Your code here.
056 *        inprogressRequests.labels("post").dec();
057 *     }
058 *   }
059 * }
060 * </pre>
061 * <p>
062 * These can be aggregated and processed together much more easily in the Prometheus
063 * server than individual metrics for each labelset.
064 */
065public class Gauge extends SimpleCollector<Gauge.Child> implements Collector.Describable {
066
067  Gauge(Builder b) {
068    super(b);
069  }
070
071  public static class Builder extends SimpleCollector.Builder<Builder, Gauge> {
072    @Override
073    public Gauge create() {
074      return new Gauge(this);
075    }
076  }
077
078  /**
079   *  Return a Builder to allow configuration of a new Gauge. Ensures required fields are provided.
080   *
081   *  @param name The name of the metric
082   *  @param help The help string of the metric
083   */
084  public static Builder build(String name, String help) {
085    return new Builder().name(name).help(help);
086  }
087
088  /**
089   *  Return a Builder to allow configuration of a new Gauge.
090   */
091  public static Builder build() {
092    return new Builder();
093  }
094
095  @Override
096  protected Child newChild() {
097    return new Child();
098  }
099
100   /**
101    * Represents an event being timed.
102    */
103   public static class Timer implements Closeable {
104     private final Child child;
105     private final long start;
106     private Timer(Child child) {
107       this.child = child;
108       start = Child.timeProvider.nanoTime();
109     }
110     /**
111      * Set the amount of time in seconds since {@link Child#startTimer} was called.
112      * @return Measured duration in seconds since {@link Child#startTimer} was called.
113      */
114     public double setDuration() {
115       double elapsed = (Child.timeProvider.nanoTime() - start) / NANOSECONDS_PER_SECOND;
116       child.set(elapsed);
117       return elapsed;
118     }
119
120     /**
121      * Equivalent to calling {@link #setDuration()}.
122      */
123     @Override
124     public void close() {
125       setDuration();
126     }
127   }
128
129  /**
130   * The value of a single Gauge.
131   * <p>
132   * <em>Warning:</em> References to a Child become invalid after using
133   * {@link SimpleCollector#remove} or {@link SimpleCollector#clear},
134   */
135  public static class Child {
136    private final DoubleAdder value = new DoubleAdder();
137
138    static TimeProvider timeProvider = new TimeProvider();
139
140    /**
141     * Increment the gauge by 1.
142     */
143    public void inc() {
144      inc(1);
145    }
146    /**
147     * Increment the gauge by the given amount.
148     */
149    public void inc(double amt) {
150      value.add(amt);
151    }
152    /**
153     * Decrement the gauge by 1.
154     */
155    public void dec() {
156      dec(1);
157    }
158    /**
159     * Decrement the gauge by the given amount.
160     */
161    public void dec(double amt) {
162      value.add(-amt);
163    }
164    /**
165     * Set the gauge to the given value.
166     */
167    public void set(double val) {
168      synchronized(this) {
169        value.reset();
170        // If get() were called here it'd see an invalid value, so use a lock.
171        // inc()/dec() don't need locks, as all the possible outcomes
172        // are still possible if set() were atomic so no new races are introduced.
173        value.add(val);
174      }
175    }
176    /**
177     * Set the gauge to the current unixtime.
178     */
179    public void setToCurrentTime() {
180      set(timeProvider.currentTimeMillis() / MILLISECONDS_PER_SECOND);
181    }
182    /**
183     * Start a timer to track a duration.
184     * <p>
185     * Call {@link Timer#setDuration} at the end of what you want to measure the duration of.
186     * <p>
187     * This is primarily useful for tracking the durations of major steps of batch jobs,
188     * which are then pushed to a PushGateway.
189     * For tracking other durations/latencies you should usually use a {@link Summary}.
190     */
191    public Timer startTimer() {
192      return new Timer(this);
193    }
194
195    /**
196     * Executes runnable code (i.e. a Java 8 Lambda) and observes a duration of how long it took to run.
197     *
198     * @param timeable Code that is being timed
199     * @return Measured duration in seconds for timeable to complete.
200     */
201    public double setToTime(Runnable timeable){
202      Timer timer = startTimer();
203
204      double elapsed;
205      try {
206        timeable.run();
207      } finally {
208        elapsed = timer.setDuration();
209      }
210
211      return elapsed;
212    }
213
214    /**
215     * Get the value of the gauge.
216     */
217    public double get() {
218      synchronized(this) {
219        return value.sum();
220      }
221    }
222  }
223
224  // Convenience methods.
225  /**
226   * Increment the gauge with no labels by 1.
227   */
228  public void inc() {
229    inc(1);
230  }
231  /**
232   * Increment the gauge with no labels by the given amount.
233   */
234  public void inc(double amt) {
235    noLabelsChild.inc(amt);
236  }
237  /**
238   * Increment the gauge with no labels by 1.
239   */
240  public void dec() {
241    dec(1);
242  }
243  /**
244   * Decrement the gauge with no labels by the given amount.
245   */
246  public void dec(double amt) {
247    noLabelsChild.dec(amt);
248  }
249  /**
250   * Set the gauge with no labels to the given value.
251   */
252  public void set(double val) {
253    noLabelsChild.set(val);
254  }
255  /**
256   * Set the gauge with no labels to the current unixtime.
257   */
258  public void setToCurrentTime() {
259    noLabelsChild.setToCurrentTime();
260  }
261  /**
262   * Start a timer to track a duration, for the gauge with no labels.
263   * <p>
264   * This is primarily useful for tracking the durations of major steps of batch jobs,
265   * which are then pushed to a PushGateway.
266   * For tracking other durations/latencies you should usually use a {@link Summary}.
267   * <p>
268   * Call {@link Timer#setDuration} at the end of what you want to measure the duration of.
269   */
270  public Timer startTimer() {
271    return noLabelsChild.startTimer();
272  }
273
274  /**
275   * Executes runnable code (i.e. a Java 8 Lambda) and observes a duration of how long it took to run.
276   *
277   * @param timeable Code that is being timed
278   * @return Measured duration in seconds for timeable to complete.
279   */
280  public double setToTime(Runnable timeable){
281    return noLabelsChild.setToTime(timeable);
282  }
283  
284  /**
285   * Get the value of the gauge.
286   */
287  public double get() {
288    return noLabelsChild.get();
289  }
290
291  @Override
292  public List<MetricFamilySamples> collect() {
293    List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(children.size());
294    for(Map.Entry<List<String>, Child> c: children.entrySet()) {
295      samples.add(new MetricFamilySamples.Sample(fullname, labelNames, c.getKey(), c.getValue().get()));
296    }
297    return familySamplesList(Type.GAUGE, samples);
298  }
299
300  @Override
301  public List<MetricFamilySamples> describe() {
302    return Collections.<MetricFamilySamples>singletonList(new GaugeMetricFamily(fullname, help, labelNames));
303  }
304
305  static class TimeProvider {
306    long currentTimeMillis() {
307      return System.currentTimeMillis();
308    }
309    long nanoTime() {
310      return System.nanoTime();
311    }
312  }
313}