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}