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