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