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