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}