001package io.prometheus.client; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Map; 006 007/** 008 * Summary metric, to track the size of events. 009 * <p> 010 * Example of uses for Summaries include: 011 * <ul> 012 * <li>Response latency</li> 013 * <li>Request size</li> 014 * </ul> 015 * 016 * <p> 017 * Example Summaries: 018 * <pre> 019 * {@code 020 * class YourClass { 021 * static final Summary receivedBytes = Summary.build() 022 * .name("requests_size_bytes").help("Request size in bytes.").register(); 023 * static final Summary requestLatency = Summary.build() 024 * .name("requests_latency_seconds").help("Request latency in seconds.").register(); 025 * 026 * void processRequest(Request req) { 027 * Summary.Timer requestTimer = requestLatency.startTimer(); 028 * try { 029 * // Your code here. 030 * } finally { 031 * receivedBytes.observe(req.size()); 032 * requestTimer.observeDuration(); 033 * } 034 * } 035 * } 036 * } 037 * </pre> 038 * This would allow you to track request rate, average latency and average request size. 039 */ 040public class Summary extends SimpleCollector<Summary.Child> { 041 042 Summary(Builder b) { 043 super(b); 044 } 045 046 public static class Builder extends SimpleCollector.Builder<Builder, Summary> { 047 @Override 048 public Summary create() { 049 return new Summary(this); 050 } 051 } 052 053 /** 054 * Return a Builder to allow configuration of a new Summary. 055 */ 056 public static Builder build() { 057 return new Builder(); 058 } 059 060 @Override 061 protected Child newChild() { 062 return new Child(); 063 } 064 065 /** 066 * Represents an event being timed. 067 */ 068 public static class Timer { 069 Child child; 070 long start; 071 private Timer(Child child) { 072 this.child = child; 073 start = Child.timeProvider.nanoTime(); 074 } 075 /** 076 * Observe the amount of time in seconds since {@link Child#startTimer} was called. 077 * @return Measured duration in seconds since {@link Child#startTimer} was called. 078 */ 079 public double observeDuration() { 080 double elapsed = (Child.timeProvider.nanoTime() - start) / NANOSECONDS_PER_SECOND; 081 child.observe(elapsed); 082 return elapsed; 083 } 084 } 085 086 /** 087 * The value of a single Summary. 088 * <p> 089 * <em>Warning:</em> References to a Child become invalid after using 090 * {@link SimpleCollector#remove} or {@link SimpleCollector#clear}. 091 */ 092 public static class Child { 093 public static class Value { 094 private double count; 095 private double sum; 096 } 097 098 // Having these seperate leaves us open to races, 099 // however Prometheus as whole has other races 100 // that mean adding atomicity here wouldn't be useful. 101 // This should be reevaluated in the future. 102 private DoubleAdder count = new DoubleAdder(); 103 private DoubleAdder sum = new DoubleAdder(); 104 105 static TimeProvider timeProvider = new TimeProvider(); 106 /** 107 * Observe the given amount. 108 */ 109 public void observe(double amt) { 110 count.add(1); 111 sum.add(amt); 112 } 113 /** 114 * Start a timer to track a duration. 115 * <p> 116 * Call {@link Timer#observeDuration} at the end of what you want to measure the duration of. 117 */ 118 public Timer startTimer() { 119 return new Timer(this); 120 } 121 /** 122 * Get the value of the Summary. 123 * <p> 124 * <em>Warning:</em> The definition of {@link Value} is subject to change. 125 */ 126 public Value get() { 127 Value v = new Value(); 128 v.count = count.sum(); 129 v.sum = sum.sum(); 130 return v; 131 } 132 } 133 134 // Convenience methods. 135 /** 136 * Observe the given amount on the summary with no labels. 137 */ 138 public void observe(double amt) { 139 noLabelsChild.observe(amt); 140 } 141 /** 142 * Start a timer to track a duration on the summary with no labels. 143 * <p> 144 * Call {@link Timer#observeDuration} at the end of what you want to measure the duration of. 145 */ 146 public Timer startTimer() { 147 return noLabelsChild.startTimer(); 148 } 149 150 @Override 151 public List<MetricFamilySamples> collect() { 152 List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(); 153 for(Map.Entry<List<String>, Child> c: children.entrySet()) { 154 Child.Value v = c.getValue().get(); 155 samples.add(new MetricFamilySamples.Sample(fullname + "_count", labelNames, c.getKey(), v.count)); 156 samples.add(new MetricFamilySamples.Sample(fullname + "_sum", labelNames, c.getKey(), v.sum)); 157 } 158 159 MetricFamilySamples mfs = new MetricFamilySamples(fullname, Type.SUMMARY, help, samples); 160 List<MetricFamilySamples> mfsList = new ArrayList<MetricFamilySamples>(); 161 mfsList.add(mfs); 162 return mfsList; 163 } 164 165 static class TimeProvider { 166 long nanoTime() { 167 return System.nanoTime(); 168 } 169 } 170}