001package io.prometheus.client; 002 003import java.io.Closeable; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.List; 008import java.util.Map; 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 * @throws IOException 124 */ 125 @Override 126 public void close() throws IOException { 127 setDuration(); 128 } 129 } 130 131 /** 132 * The value of a single Gauge. 133 * <p> 134 * <em>Warning:</em> References to a Child become invalid after using 135 * {@link SimpleCollector#remove} or {@link SimpleCollector#clear}, 136 */ 137 public static class Child { 138 private final DoubleAdder value = new DoubleAdder(); 139 140 static TimeProvider timeProvider = new TimeProvider(); 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 (i.e. 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 * Get the value of the gauge. 217 */ 218 public double get() { 219 synchronized(this) { 220 return value.sum(); 221 } 222 } 223 } 224 225 // Convenience methods. 226 /** 227 * Increment the gauge with no labels by 1. 228 */ 229 public void inc() { 230 inc(1); 231 } 232 /** 233 * Increment the gauge with no labels by the given amount. 234 */ 235 public void inc(double amt) { 236 noLabelsChild.inc(amt); 237 } 238 /** 239 * Increment the gauge with no labels by 1. 240 */ 241 public void dec() { 242 dec(1); 243 } 244 /** 245 * Decrement the gauge with no labels by the given amount. 246 */ 247 public void dec(double amt) { 248 noLabelsChild.dec(amt); 249 } 250 /** 251 * Set the gauge with no labels to the given value. 252 */ 253 public void set(double val) { 254 noLabelsChild.set(val); 255 } 256 /** 257 * Set the gauge with no labels to the current unixtime. 258 */ 259 public void setToCurrentTime() { 260 noLabelsChild.setToCurrentTime(); 261 } 262 /** 263 * Start a timer to track a duration, for the gauge with no labels. 264 * <p> 265 * This is primarily useful for tracking the durations of major steps of batch jobs, 266 * which are then pushed to a PushGateway. 267 * For tracking other durations/latencies you should usually use a {@link Summary}. 268 * <p> 269 * Call {@link Timer#setDuration} at the end of what you want to measure the duration of. 270 */ 271 public Timer startTimer() { 272 return noLabelsChild.startTimer(); 273 } 274 275 /** 276 * Executes runnable code (i.e. a Java 8 Lambda) and observes a duration of how long it took to run. 277 * 278 * @param timeable Code that is being timed 279 * @return Measured duration in seconds for timeable to complete. 280 */ 281 public double setToTime(Runnable timeable){ 282 return noLabelsChild.setToTime(timeable); 283 } 284 285 /** 286 * Get the value of the gauge. 287 */ 288 public double get() { 289 return noLabelsChild.get(); 290 } 291 292 293 @Override 294 public List<MetricFamilySamples> collect() { 295 List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(children.size()); 296 for(Map.Entry<List<String>, Child> c: children.entrySet()) { 297 samples.add(new MetricFamilySamples.Sample(fullname, labelNames, c.getKey(), c.getValue().get())); 298 } 299 return familySamplesList(Type.GAUGE, samples); 300 } 301 302 @Override 303 public List<MetricFamilySamples> describe() { 304 return Collections.<MetricFamilySamples>singletonList(new GaugeMetricFamily(fullname, help, labelNames)); 305 } 306 307 static class TimeProvider { 308 long currentTimeMillis() { 309 return System.currentTimeMillis(); 310 } 311 long nanoTime() { 312 return System.nanoTime(); 313 } 314 } 315}