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