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     * Increment the gauge by 1.
141     */
142    public void inc() {
143      inc(1);
144    }
145    /**
146     * Increment the gauge by the given amount.
147     */
148    public void inc(double amt) {
149      value.add(amt);
150    }
151    /**
152     * Decrement the gauge by 1.
153     */
154    public void dec() {
155      dec(1);
156    }
157    /**
158     * Decrement the gauge by the given amount.
159     */
160    public void dec(double amt) {
161      value.add(-amt);
162    }
163    /**
164     * Set the gauge to the given value.
165     */
166    public void set(double val) {
167      synchronized(this) {
168        value.reset();
169        // If get() were called here it'd see an invalid value, so use a lock.
170        // inc()/dec() don't need locks, as all the possible outcomes
171        // are still possible if set() were atomic so no new races are introduced.
172        value.add(val);
173      }
174    }
175    /**
176     * Set the gauge to the current unixtime.
177     */
178    public void setToCurrentTime() {
179      set(timeProvider.currentTimeMillis() / MILLISECONDS_PER_SECOND);
180    }
181    /**
182     * Start a timer to track a duration.
183     * <p>
184     * Call {@link Timer#setDuration} at the end of what you want to measure the duration of.
185     * <p>
186     * This is primarily useful for tracking the durations of major steps of batch jobs,
187     * which are then pushed to a PushGateway.
188     * For tracking other durations/latencies you should usually use a {@link Summary}.
189     */
190    public Timer startTimer() {
191      return new Timer(this);
192    }
193
194    /**
195     * Executes runnable code (i.e. a Java 8 Lambda) and observes a duration of how long it took to run.
196     *
197     * @param timeable Code that is being timed
198     * @return Measured duration in seconds for timeable to complete.
199     */
200    public double setToTime(Runnable timeable){
201      Timer timer = startTimer();
202
203      double elapsed;
204      try {
205        timeable.run();
206      } finally {
207        elapsed = timer.setDuration();
208      }
209
210      return elapsed;
211    }
212
213    /**
214     * Get the value of the gauge.
215     */
216    public double get() {
217      synchronized(this) {
218        return value.sum();
219      }
220    }
221  }
222
223  // Convenience methods.
224  /**
225   * Increment the gauge with no labels by 1.
226   */
227  public void inc() {
228    inc(1);
229  }
230  /**
231   * Increment the gauge with no labels by the given amount.
232   */
233  public void inc(double amt) {
234    noLabelsChild.inc(amt);
235  }
236  /**
237   * Increment the gauge with no labels by 1.
238   */
239  public void dec() {
240    dec(1);
241  }
242  /**
243   * Decrement the gauge with no labels by the given amount.
244   */
245  public void dec(double amt) {
246    noLabelsChild.dec(amt);
247  }
248  /**
249   * Set the gauge with no labels to the given value.
250   */
251  public void set(double val) {
252    noLabelsChild.set(val);
253  }
254  /**
255   * Set the gauge with no labels to the current unixtime.
256   */
257  public void setToCurrentTime() {
258    noLabelsChild.setToCurrentTime();
259  }
260  /**
261   * Start a timer to track a duration, for the gauge with no labels.
262   * <p>
263   * This is primarily useful for tracking the durations of major steps of batch jobs,
264   * which are then pushed to a PushGateway.
265   * For tracking other durations/latencies you should usually use a {@link Summary}.
266   * <p>
267   * Call {@link Timer#setDuration} at the end of what you want to measure the duration of.
268   */
269  public Timer startTimer() {
270    return noLabelsChild.startTimer();
271  }
272
273  /**
274   * Executes runnable code (i.e. a Java 8 Lambda) and observes a duration of how long it took to run.
275   *
276   * @param timeable Code that is being timed
277   * @return Measured duration in seconds for timeable to complete.
278   */
279  public double setToTime(Runnable timeable){
280    return noLabelsChild.setToTime(timeable);
281  }
282  
283  /**
284   * Get the value of the gauge.
285   */
286  public double get() {
287    return noLabelsChild.get();
288  }
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}