001package io.prometheus.metrics.core.metrics; 002 003import io.prometheus.metrics.config.MetricsProperties; 004import io.prometheus.metrics.config.PrometheusProperties; 005import io.prometheus.metrics.core.datapoints.GaugeDataPoint; 006import io.prometheus.metrics.core.exemplars.ExemplarSampler; 007import io.prometheus.metrics.core.exemplars.ExemplarSamplerConfig; 008import io.prometheus.metrics.model.snapshots.Exemplar; 009import io.prometheus.metrics.model.snapshots.GaugeSnapshot; 010import io.prometheus.metrics.model.snapshots.Labels; 011 012import java.util.ArrayList; 013import java.util.Collections; 014import java.util.List; 015import java.util.concurrent.atomic.AtomicLong; 016 017/** 018 * Gauge metric. 019 * <p> 020 * Example usage: 021 * <pre>{@code 022 * Gauge currentActiveUsers = Gauge.builder() 023 * .name("current_active_users") 024 * .help("Number of users that are currently active") 025 * .labelNames("region") 026 * .register(); 027 * 028 * public void login(String region) { 029 * currentActiveUsers.labelValues(region).inc(); 030 * // perform login 031 * } 032 * 033 * public void logout(String region) { 034 * currentActiveUsers.labelValues(region).dec(); 035 * // perform logout 036 * } 037 * }</pre> 038 */ 039public class Gauge extends StatefulMetric<GaugeDataPoint, Gauge.DataPoint> implements GaugeDataPoint { 040 041 private final boolean exemplarsEnabled; 042 private final ExemplarSamplerConfig exemplarSamplerConfig; 043 044 private Gauge(Builder builder, PrometheusProperties prometheusProperties) { 045 super(builder); 046 MetricsProperties[] properties = getMetricProperties(builder, prometheusProperties); 047 exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled); 048 if (exemplarsEnabled) { 049 exemplarSamplerConfig = new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 1); 050 } else { 051 exemplarSamplerConfig = null; 052 } 053 } 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 public void inc(double amount) { 060 getNoLabels().inc(amount); 061 } 062 063 /** 064 * {@inheritDoc} 065 */ 066 @Override 067 public double get() { 068 return getNoLabels().get(); 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public void incWithExemplar(double amount, Labels labels) { 076 getNoLabels().incWithExemplar(amount, labels); 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 public void set(double value) { 084 getNoLabels().set(value); 085 } 086 087 /** 088 * {@inheritDoc} 089 */ 090 @Override 091 public void setWithExemplar(double value, Labels labels) { 092 getNoLabels().setWithExemplar(value, labels); 093 } 094 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 public GaugeSnapshot collect() { 100 return (GaugeSnapshot) super.collect(); 101 } 102 103 @Override 104 protected GaugeSnapshot collect(List<Labels> labels, List<DataPoint> metricData) { 105 List<GaugeSnapshot.GaugeDataPointSnapshot> dataPointSnapshots = new ArrayList<>(labels.size()); 106 for (int i = 0; i < labels.size(); i++) { 107 dataPointSnapshots.add(metricData.get(i).collect(labels.get(i))); 108 } 109 return new GaugeSnapshot(getMetadata(), dataPointSnapshots); 110 } 111 112 @Override 113 protected DataPoint newDataPoint() { 114 if (isExemplarsEnabled()) { 115 return new DataPoint(new ExemplarSampler(exemplarSamplerConfig)); 116 } else { 117 return new DataPoint(null); 118 } 119 } 120 121 @Override 122 protected boolean isExemplarsEnabled() { 123 return exemplarsEnabled; 124 } 125 126 class DataPoint implements GaugeDataPoint { 127 128 private final ExemplarSampler exemplarSampler; // null if isExemplarsEnabled() is false 129 130 private DataPoint(ExemplarSampler exemplarSampler) { 131 this.exemplarSampler = exemplarSampler; 132 } 133 134 private final AtomicLong value = new AtomicLong(Double.doubleToRawLongBits(0)); 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public void inc(double amount) { 141 long next = value.updateAndGet(l -> Double.doubleToRawLongBits(Double.longBitsToDouble(l) + amount)); 142 if (isExemplarsEnabled()) { 143 exemplarSampler.observe(Double.longBitsToDouble(next)); 144 } 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public void incWithExemplar(double amount, Labels labels) { 152 long next = value.updateAndGet(l -> Double.doubleToRawLongBits(Double.longBitsToDouble(l) + amount)); 153 if (isExemplarsEnabled()) { 154 exemplarSampler.observeWithExemplar(Double.longBitsToDouble(next), labels); 155 } 156 } 157 158 /** 159 * {@inheritDoc} 160 */ 161 @Override 162 public void set(double value) { 163 this.value.set(Double.doubleToRawLongBits(value)); 164 if (isExemplarsEnabled()) { 165 exemplarSampler.observe(value); 166 } 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override 173 public double get() { 174 return Double.longBitsToDouble(value.get()); 175 } 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override 181 public void setWithExemplar(double value, Labels labels) { 182 this.value.set(Double.doubleToRawLongBits(value)); 183 if (isExemplarsEnabled()) { 184 exemplarSampler.observeWithExemplar(value, labels); 185 } 186 } 187 188 private GaugeSnapshot.GaugeDataPointSnapshot collect(Labels labels) { 189 // Read the exemplar first. Otherwise, there is a race condition where you might 190 // see an Exemplar for a value that's not represented in getValue() yet. 191 // If there are multiple Exemplars (by default it's just one), use the oldest 192 // so that we don't violate min age. 193 Exemplar oldest = null; 194 if (isExemplarsEnabled()) { 195 for (Exemplar exemplar : exemplarSampler.collect()) { 196 if (oldest == null || exemplar.getTimestampMillis() < oldest.getTimestampMillis()) { 197 oldest = exemplar; 198 } 199 } 200 } 201 return new GaugeSnapshot.GaugeDataPointSnapshot(get(), labels, oldest); 202 } 203 } 204 205 public static Builder builder() { 206 return new Builder(PrometheusProperties.get()); 207 } 208 209 public static Builder builder(PrometheusProperties config) { 210 return new Builder(config); 211 } 212 213 public static class Builder extends StatefulMetric.Builder<Builder, Gauge> { 214 215 private Builder(PrometheusProperties config) { 216 super(Collections.emptyList(), config); 217 } 218 219 @Override 220 public Gauge build() { 221 return new Gauge(this, properties); 222 } 223 224 @Override 225 protected Builder self() { 226 return this; 227 } 228 } 229}