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, Summary> { 041 042 Summary(Builder b) { 043 super(b); 044 } 045 046 public static class Builder extends SimpleCollector.Builder<Builder> { 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 */ 078 public void observeDuration() { 079 child.observe((Child.timeProvider.nanoTime() - start) / NANOSECONDS_PER_SECOND); 080 } 081 } 082 083 /** 084 * The value of a single Summary. 085 * <p> 086 * <em>Warning:</em> References to a Child become invalid after using 087 * {@link SimpleCollector#remove} or {@link SimpleCollector#clear}. 088 */ 089 public static class Child { 090 public static class Value { 091 private double count; 092 private double sum; 093 } 094 095 // Having these seperate leaves us open to races, 096 // however Prometheus as whole has other races 097 // that mean adding atomicity here wouldn't be useful. 098 // This should be reevaluated in the future. 099 private DoubleAdder count = new DoubleAdder(); 100 private DoubleAdder sum = new DoubleAdder(); 101 102 static TimeProvider timeProvider = new TimeProvider(); 103 /** 104 * Observe the given amount. 105 */ 106 public void observe(double amt) { 107 count.add(1); 108 sum.add(amt); 109 } 110 /** 111 * Start a timer to track a duration. 112 * <p> 113 * Call {@link Timer#observeDuration} at the end of what you want to measure the duration of. 114 */ 115 public Timer startTimer() { 116 return new Timer(this); 117 } 118 /** 119 * Get the value of the Summary. 120 * <p> 121 * <em>Warning:</em> The definition of {@link Value} is subject to change. 122 */ 123 public Value get() { 124 Value v = new Value(); 125 v.count = count.sum(); 126 v.sum = sum.sum(); 127 return v; 128 } 129 } 130 131 // Convenience methods. 132 /** 133 * Observe the given amount on the summary with no labels. 134 */ 135 public void observe(double amt) { 136 noLabelsChild.observe(amt); 137 } 138 /** 139 * Start a timer to track a duration on the summary with no labels. 140 * <p> 141 * Call {@link Timer#observeDuration} at the end of what you want to measure the duration of. 142 */ 143 public Timer startTimer() { 144 return noLabelsChild.startTimer(); 145 } 146 147 @Override 148 public List<MetricFamilySamples> collect() { 149 List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(); 150 for(Map.Entry<List<String>, Child> c: children.entrySet()) { 151 Child.Value v = c.getValue().get(); 152 samples.add(new MetricFamilySamples.Sample(fullname + "_count", labelNames, c.getKey(), v.count)); 153 samples.add(new MetricFamilySamples.Sample(fullname + "_sum", labelNames, c.getKey(), v.sum)); 154 } 155 156 MetricFamilySamples mfs = new MetricFamilySamples(fullname, Type.SUMMARY, help, samples); 157 List<MetricFamilySamples> mfsList = new ArrayList<MetricFamilySamples>(); 158 mfsList.add(mfs); 159 return mfsList; 160 } 161 162 static class TimeProvider { 163 long nanoTime() { 164 return System.nanoTime(); 165 } 166 } 167}