001package io.prometheus.client.dropwizard; 002 003import com.codahale.metrics.Counter; 004import com.codahale.metrics.Gauge; 005import com.codahale.metrics.Histogram; 006import com.codahale.metrics.Meter; 007import com.codahale.metrics.Metric; 008import com.codahale.metrics.MetricFilter; 009import com.codahale.metrics.MetricRegistry; 010import com.codahale.metrics.Snapshot; 011import com.codahale.metrics.Timer; 012import io.prometheus.client.dropwizard.samplebuilder.SampleBuilder; 013import io.prometheus.client.dropwizard.samplebuilder.DefaultSampleBuilder; 014 015import java.util.ArrayList; 016import java.util.Arrays; 017import java.util.HashMap; 018import java.util.List; 019import java.util.Map; 020import java.util.SortedMap; 021import java.util.concurrent.TimeUnit; 022import java.util.logging.Level; 023import java.util.logging.Logger; 024 025/** 026 * Collect Dropwizard metrics from a MetricRegistry. 027 */ 028public class DropwizardExports extends io.prometheus.client.Collector implements io.prometheus.client.Collector.Describable { 029 private static final Logger LOGGER = Logger.getLogger(DropwizardExports.class.getName()); 030 private MetricRegistry registry; 031 private MetricFilter metricFilter; 032 private SampleBuilder sampleBuilder; 033 034 /** 035 * Creates a new DropwizardExports with a {@link DefaultSampleBuilder} and {@link MetricFilter#ALL}. 036 * 037 * @param registry a metric registry to export in prometheus. 038 */ 039 public DropwizardExports(MetricRegistry registry) { 040 this.registry = registry; 041 this.metricFilter = MetricFilter.ALL; 042 this.sampleBuilder = new DefaultSampleBuilder(); 043 } 044 045 /** 046 * Creates a new DropwizardExports with a {@link DefaultSampleBuilder} and custom {@link MetricFilter}. 047 * 048 * @param registry a metric registry to export in prometheus. 049 * @param metricFilter a custom metric filter. 050 */ 051 public DropwizardExports(MetricRegistry registry, MetricFilter metricFilter) { 052 this.registry = registry; 053 this.metricFilter = metricFilter; 054 this.sampleBuilder = new DefaultSampleBuilder(); 055 } 056 057 /** 058 * @param registry a metric registry to export in prometheus. 059 * @param sampleBuilder sampleBuilder to use to create prometheus samples. 060 */ 061 public DropwizardExports(MetricRegistry registry, SampleBuilder sampleBuilder) { 062 this.registry = registry; 063 this.metricFilter = MetricFilter.ALL; 064 this.sampleBuilder = sampleBuilder; 065 } 066 067 /** 068 * @param registry a metric registry to export in prometheus. 069 * @param metricFilter a custom metric filter. 070 * @param sampleBuilder sampleBuilder to use to create prometheus samples. 071 */ 072 public DropwizardExports(MetricRegistry registry, MetricFilter metricFilter, SampleBuilder sampleBuilder) { 073 this.registry = registry; 074 this.metricFilter = metricFilter; 075 this.sampleBuilder = sampleBuilder; 076 } 077 078 private static String getHelpMessage(String metricName, Metric metric) { 079 return String.format("Generated from Dropwizard metric import (metric=%s, type=%s)", 080 metricName, metric.getClass().getName()); 081 } 082 083 /** 084 * Export counter as Prometheus <a href="https://prometheus.io/docs/concepts/metric_types/#gauge">Gauge</a>. 085 */ 086 MetricFamilySamples fromCounter(String dropwizardName, Counter counter) { 087 MetricFamilySamples.Sample sample = sampleBuilder.createSample(dropwizardName, "", new ArrayList<String>(), new ArrayList<String>(), 088 new Long(counter.getCount()).doubleValue()); 089 return new MetricFamilySamples(sample.name, Type.GAUGE, getHelpMessage(dropwizardName, counter), Arrays.asList(sample)); 090 } 091 092 /** 093 * Export gauge as a prometheus gauge. 094 */ 095 MetricFamilySamples fromGauge(String dropwizardName, Gauge gauge) { 096 Object obj = gauge.getValue(); 097 double value; 098 if (obj instanceof Number) { 099 value = ((Number) obj).doubleValue(); 100 } else if (obj instanceof Boolean) { 101 value = ((Boolean) obj) ? 1 : 0; 102 } else { 103 LOGGER.log(Level.FINE, String.format("Invalid type for Gauge %s: %s", sanitizeMetricName(dropwizardName), 104 obj == null ? "null" : obj.getClass().getName())); 105 return null; 106 } 107 MetricFamilySamples.Sample sample = sampleBuilder.createSample(dropwizardName, "", 108 new ArrayList<String>(), new ArrayList<String>(), value); 109 return new MetricFamilySamples(sample.name, Type.GAUGE, getHelpMessage(dropwizardName, gauge), Arrays.asList(sample)); 110 } 111 112 /** 113 * Export a histogram snapshot as a prometheus SUMMARY. 114 * 115 * @param dropwizardName metric name. 116 * @param snapshot the histogram snapshot. 117 * @param count the total sample count for this snapshot. 118 * @param factor a factor to apply to histogram values. 119 */ 120 MetricFamilySamples fromSnapshotAndCount(String dropwizardName, Snapshot snapshot, long count, double factor, String helpMessage) { 121 List<MetricFamilySamples.Sample> samples = Arrays.asList( 122 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.5"), snapshot.getMedian() * factor), 123 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.75"), snapshot.get75thPercentile() * factor), 124 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.95"), snapshot.get95thPercentile() * factor), 125 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.98"), snapshot.get98thPercentile() * factor), 126 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.99"), snapshot.get99thPercentile() * factor), 127 sampleBuilder.createSample(dropwizardName, "", Arrays.asList("quantile"), Arrays.asList("0.999"), snapshot.get999thPercentile() * factor), 128 sampleBuilder.createSample(dropwizardName, "_count", new ArrayList<String>(), new ArrayList<String>(), count) 129 ); 130 return new MetricFamilySamples(samples.get(0).name, Type.SUMMARY, helpMessage, samples); 131 } 132 133 /** 134 * Convert histogram snapshot. 135 */ 136 MetricFamilySamples fromHistogram(String dropwizardName, Histogram histogram) { 137 return fromSnapshotAndCount(dropwizardName, histogram.getSnapshot(), histogram.getCount(), 1.0, 138 getHelpMessage(dropwizardName, histogram)); 139 } 140 141 /** 142 * Export Dropwizard Timer as a histogram. Use TIME_UNIT as time unit. 143 */ 144 MetricFamilySamples fromTimer(String dropwizardName, Timer timer) { 145 return fromSnapshotAndCount(dropwizardName, timer.getSnapshot(), timer.getCount(), 146 1.0D / TimeUnit.SECONDS.toNanos(1L), getHelpMessage(dropwizardName, timer)); 147 } 148 149 /** 150 * Export a Meter as as prometheus COUNTER. 151 */ 152 MetricFamilySamples fromMeter(String dropwizardName, Meter meter) { 153 final MetricFamilySamples.Sample sample = sampleBuilder.createSample(dropwizardName, "_total", 154 new ArrayList<String>(), 155 new ArrayList<String>(), 156 meter.getCount()); 157 return new MetricFamilySamples(sample.name, Type.COUNTER, getHelpMessage(dropwizardName, meter), 158 Arrays.asList(sample)); 159 } 160 161 @Override 162 public List<MetricFamilySamples> collect() { 163 Map<String, MetricFamilySamples> mfSamplesMap = new HashMap<String, MetricFamilySamples>(); 164 165 for (SortedMap.Entry<String, Gauge> entry : registry.getGauges(metricFilter).entrySet()) { 166 addToMap(mfSamplesMap, fromGauge(entry.getKey(), entry.getValue())); 167 } 168 for (SortedMap.Entry<String, Counter> entry : registry.getCounters(metricFilter).entrySet()) { 169 addToMap(mfSamplesMap, fromCounter(entry.getKey(), entry.getValue())); 170 } 171 for (SortedMap.Entry<String, Histogram> entry : registry.getHistograms(metricFilter).entrySet()) { 172 addToMap(mfSamplesMap, fromHistogram(entry.getKey(), entry.getValue())); 173 } 174 for (SortedMap.Entry<String, Timer> entry : registry.getTimers(metricFilter).entrySet()) { 175 addToMap(mfSamplesMap, fromTimer(entry.getKey(), entry.getValue())); 176 } 177 for (SortedMap.Entry<String, Meter> entry : registry.getMeters(metricFilter).entrySet()) { 178 addToMap(mfSamplesMap, fromMeter(entry.getKey(), entry.getValue())); 179 } 180 return new ArrayList<MetricFamilySamples>(mfSamplesMap.values()); 181 } 182 183 private void addToMap(Map<String, MetricFamilySamples> mfSamplesMap, MetricFamilySamples newMfSamples) 184 { 185 if (newMfSamples != null) { 186 MetricFamilySamples currentMfSamples = mfSamplesMap.get(newMfSamples.name); 187 if (currentMfSamples == null) { 188 mfSamplesMap.put(newMfSamples.name, newMfSamples); 189 } else { 190 List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(currentMfSamples.samples); 191 samples.addAll(newMfSamples.samples); 192 mfSamplesMap.put(newMfSamples.name, new MetricFamilySamples(newMfSamples.name, currentMfSamples.type, currentMfSamples.help, samples)); 193 } 194 } 195 } 196 197 @Override 198 public List<MetricFamilySamples> describe() { 199 return new ArrayList<MetricFamilySamples>(); 200 } 201}