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