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 void incWithExemplar(double amount, Labels labels) { 068 getNoLabels().incWithExemplar(amount, labels); 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public void set(double value) { 076 getNoLabels().set(value); 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 public void setWithExemplar(double value, Labels labels) { 084 getNoLabels().setWithExemplar(value, labels); 085 } 086 087 /** 088 * {@inheritDoc} 089 */ 090 @Override 091 public GaugeSnapshot collect() { 092 return (GaugeSnapshot) super.collect(); 093 } 094 095 @Override 096 protected GaugeSnapshot collect(List<Labels> labels, List<DataPoint> metricData) { 097 List<GaugeSnapshot.GaugeDataPointSnapshot> dataPointSnapshots = new ArrayList<>(labels.size()); 098 for (int i = 0; i < labels.size(); i++) { 099 dataPointSnapshots.add(metricData.get(i).collect(labels.get(i))); 100 } 101 return new GaugeSnapshot(getMetadata(), dataPointSnapshots); 102 } 103 104 @Override 105 protected DataPoint newDataPoint() { 106 if (isExemplarsEnabled()) { 107 return new DataPoint(new ExemplarSampler(exemplarSamplerConfig)); 108 } else { 109 return new DataPoint(null); 110 } 111 } 112 113 @Override 114 protected boolean isExemplarsEnabled() { 115 return exemplarsEnabled; 116 } 117 118 class DataPoint implements GaugeDataPoint { 119 120 private final ExemplarSampler exemplarSampler; // null if isExemplarsEnabled() is false 121 122 private DataPoint(ExemplarSampler exemplarSampler) { 123 this.exemplarSampler = exemplarSampler; 124 } 125 126 private final AtomicLong value = new AtomicLong(Double.doubleToRawLongBits(0)); 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public void inc(double amount) { 133 long next = value.updateAndGet(l -> Double.doubleToRawLongBits(Double.longBitsToDouble(l) + amount)); 134 if (isExemplarsEnabled()) { 135 exemplarSampler.observe(Double.longBitsToDouble(next)); 136 } 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 public void incWithExemplar(double amount, Labels labels) { 144 long next = value.updateAndGet(l -> Double.doubleToRawLongBits(Double.longBitsToDouble(l) + amount)); 145 if (isExemplarsEnabled()) { 146 exemplarSampler.observeWithExemplar(Double.longBitsToDouble(next), labels); 147 } 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public void set(double value) { 155 this.value.set(Double.doubleToRawLongBits(value)); 156 if (isExemplarsEnabled()) { 157 exemplarSampler.observe(value); 158 } 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public void setWithExemplar(double value, Labels labels) { 166 this.value.set(Double.doubleToRawLongBits(value)); 167 if (isExemplarsEnabled()) { 168 exemplarSampler.observeWithExemplar(value, labels); 169 } 170 } 171 172 private GaugeSnapshot.GaugeDataPointSnapshot collect(Labels labels) { 173 // Read the exemplar first. Otherwise, there is a race condition where you might 174 // see an Exemplar for a value that's not represented in getValue() yet. 175 // If there are multiple Exemplars (by default it's just one), use the oldest 176 // so that we don't violate min age. 177 Exemplar oldest = null; 178 if (isExemplarsEnabled()) { 179 for (Exemplar exemplar : exemplarSampler.collect()) { 180 if (oldest == null || exemplar.getTimestampMillis() < oldest.getTimestampMillis()) { 181 oldest = exemplar; 182 } 183 } 184 } 185 return new GaugeSnapshot.GaugeDataPointSnapshot(Double.longBitsToDouble(value.get()), labels, oldest); 186 } 187 } 188 189 public static Builder builder() { 190 return new Builder(PrometheusProperties.get()); 191 } 192 193 public static Builder builder(PrometheusProperties config) { 194 return new Builder(config); 195 } 196 197 public static class Builder extends StatefulMetric.Builder<Builder, Gauge> { 198 199 private Builder(PrometheusProperties config) { 200 super(Collections.emptyList(), config); 201 } 202 203 @Override 204 public Gauge build() { 205 return new Gauge(this, properties); 206 } 207 208 @Override 209 protected Builder self() { 210 return this; 211 } 212 } 213}