001package io.prometheus.metrics.instrumentation.dropwizard5.internal; 002 003import io.prometheus.metrics.instrumentation.dropwizard5.InvalidMetricHandler; 004import io.prometheus.metrics.instrumentation.dropwizard5.labels.CustomLabelMapper; 005import io.prometheus.metrics.model.registry.MultiCollector; 006import io.prometheus.metrics.model.snapshots.CounterSnapshot; 007import io.prometheus.metrics.model.snapshots.GaugeSnapshot; 008import io.prometheus.metrics.model.snapshots.MetricMetadata; 009import io.prometheus.metrics.model.snapshots.MetricSnapshot; 010import io.prometheus.metrics.model.snapshots.MetricSnapshots; 011import io.prometheus.metrics.model.snapshots.PrometheusNaming; 012import io.prometheus.metrics.model.snapshots.Quantiles; 013import io.prometheus.metrics.model.snapshots.SummarySnapshot; 014import java.util.Collections; 015import java.util.Map; 016import java.util.concurrent.TimeUnit; 017import java.util.function.BiFunction; 018import java.util.logging.Level; 019import java.util.logging.Logger; 020import javax.annotation.Nullable; 021 022/** 023 * Abstract base class for Dropwizard metrics exporters. Contains all the common logic for 024 * converting Dropwizard metrics to Prometheus metrics. Subclasses only need to implement {@link 025 * #collectMetricSnapshots()} to handle version-specific registry APIs. 026 * 027 * @param <R> The Dropwizard MetricRegistry type 028 * @param <F> The Dropwizard MetricFilter type 029 * @param <C> The Dropwizard Counter type 030 * @param <G> The Dropwizard Gauge type 031 * @param <H> The Dropwizard Histogram type 032 * @param <T> The Dropwizard Timer type 033 * @param <M> The Dropwizard Meter type 034 * @param <B> The Dropwizard Metric base type 035 * @param <S> The Dropwizard Snapshot type 036 */ 037public abstract class AbstractDropwizardExports<R, F, C, G, H, T, M, B, S> 038 implements MultiCollector { 039 040 private static final Logger logger = Logger.getLogger(AbstractDropwizardExports.class.getName()); 041 042 protected final R registry; 043 protected final F metricFilter; 044 @Nullable protected final CustomLabelMapper labelMapper; 045 protected final InvalidMetricHandler invalidMetricHandler; 046 047 protected AbstractDropwizardExports( 048 R registry, 049 F metricFilter, 050 @Nullable CustomLabelMapper labelMapper, 051 InvalidMetricHandler invalidMetricHandler) { 052 this.registry = registry; 053 this.metricFilter = metricFilter; 054 this.labelMapper = labelMapper; 055 this.invalidMetricHandler = invalidMetricHandler; 056 } 057 058 protected static String getHelpMessage(String metricName, Object metric) { 059 return String.format( 060 "Generated from Dropwizard metric import (metric=%s, type=%s)", 061 metricName, metric.getClass().getName()); 062 } 063 064 protected MetricMetadata getMetricMetaData(String metricName, B metric) { 065 String name = labelMapper != null ? labelMapper.getName(metricName) : metricName; 066 return new MetricMetadata( 067 PrometheusNaming.sanitizeMetricName(name), getHelpMessage(metricName, metric)); 068 } 069 070 /** 071 * Export counter as Prometheus <a 072 * href="https://prometheus.io/docs/concepts/metric_types/#gauge">Gauge</a>. 073 */ 074 @SuppressWarnings("unchecked") 075 protected MetricSnapshot fromCounter(String dropwizardName, C counter) { 076 long count = getCounterCount(counter); 077 MetricMetadata metadata = getMetricMetaData(dropwizardName, (B) counter); 078 CounterSnapshot.CounterDataPointSnapshot.Builder dataPointBuilder = 079 CounterSnapshot.CounterDataPointSnapshot.builder().value(Long.valueOf(count).doubleValue()); 080 if (labelMapper != null) { 081 dataPointBuilder.labels( 082 labelMapper.getLabels(dropwizardName, Collections.emptyList(), Collections.emptyList())); 083 } 084 return new CounterSnapshot(metadata, Collections.singletonList(dataPointBuilder.build())); 085 } 086 087 /** Export gauge as a prometheus gauge. */ 088 @SuppressWarnings("unchecked") 089 @Nullable 090 protected MetricSnapshot fromGauge(String dropwizardName, G gauge) { 091 Object obj = getGaugeValue(gauge); 092 double value; 093 if (obj instanceof Number) { 094 value = ((Number) obj).doubleValue(); 095 } else if (obj instanceof Boolean) { 096 value = ((Boolean) obj) ? 1 : 0; 097 } else { 098 logger.log( 099 Level.FINE, 100 String.format( 101 "Invalid type for Gauge %s: %s", 102 PrometheusNaming.sanitizeMetricName(dropwizardName), 103 obj == null ? "null" : obj.getClass().getName())); 104 return null; 105 } 106 MetricMetadata metadata = getMetricMetaData(dropwizardName, (B) gauge); 107 GaugeSnapshot.GaugeDataPointSnapshot.Builder dataPointBuilder = 108 GaugeSnapshot.GaugeDataPointSnapshot.builder().value(value); 109 if (labelMapper != null) { 110 dataPointBuilder.labels( 111 labelMapper.getLabels(dropwizardName, Collections.emptyList(), Collections.emptyList())); 112 } 113 return new GaugeSnapshot(metadata, Collections.singletonList(dataPointBuilder.build())); 114 } 115 116 /** 117 * Export a histogram snapshot as a prometheus SUMMARY. 118 * 119 * @param dropwizardName metric name. 120 * @param snapshot the histogram snapshot. 121 * @param count the total sample count for this snapshot. 122 * @param factor a factor to apply to histogram values. 123 */ 124 protected MetricSnapshot fromSnapshotAndCount( 125 String dropwizardName, S snapshot, long count, double factor, String helpMessage) { 126 Quantiles quantiles = 127 Quantiles.builder() 128 .quantile(0.5, getMedian(snapshot) * factor) 129 .quantile(0.75, get75thPercentile(snapshot) * factor) 130 .quantile(0.95, get95thPercentile(snapshot) * factor) 131 .quantile(0.98, get98thPercentile(snapshot) * factor) 132 .quantile(0.99, get99thPercentile(snapshot) * factor) 133 .quantile(0.999, get999thPercentile(snapshot) * factor) 134 .build(); 135 136 String name = labelMapper != null ? labelMapper.getName(dropwizardName) : dropwizardName; 137 MetricMetadata metadata = 138 new MetricMetadata(PrometheusNaming.sanitizeMetricName(name), helpMessage); 139 SummarySnapshot.SummaryDataPointSnapshot.Builder dataPointBuilder = 140 SummarySnapshot.SummaryDataPointSnapshot.builder().quantiles(quantiles).count(count); 141 if (labelMapper != null) { 142 dataPointBuilder.labels( 143 labelMapper.getLabels(dropwizardName, Collections.emptyList(), Collections.emptyList())); 144 } 145 return new SummarySnapshot(metadata, Collections.singletonList(dataPointBuilder.build())); 146 } 147 148 /** Convert histogram snapshot. */ 149 protected MetricSnapshot fromHistogram(String dropwizardName, H histogram) { 150 S snapshot = getHistogramSnapshot(histogram); 151 long count = getHistogramCount(histogram); 152 return fromSnapshotAndCount( 153 dropwizardName, snapshot, count, 1.0, getHelpMessage(dropwizardName, histogram)); 154 } 155 156 /** Export Dropwizard Timer as a histogram. Use TIME_UNIT as time unit. */ 157 protected MetricSnapshot fromTimer(String dropwizardName, T timer) { 158 S snapshot = getTimerSnapshot(timer); 159 long count = getTimerCount(timer); 160 return fromSnapshotAndCount( 161 dropwizardName, 162 snapshot, 163 count, 164 1.0D / TimeUnit.SECONDS.toNanos(1L), 165 getHelpMessage(dropwizardName, timer)); 166 } 167 168 /** Export a Meter as a prometheus COUNTER. */ 169 @SuppressWarnings("unchecked") 170 protected MetricSnapshot fromMeter(String dropwizardName, M meter) { 171 MetricMetadata metadata = getMetricMetaData(dropwizardName + "_total", (B) meter); 172 CounterSnapshot.CounterDataPointSnapshot.Builder dataPointBuilder = 173 CounterSnapshot.CounterDataPointSnapshot.builder().value(getMeterCount(meter)); 174 if (labelMapper != null) { 175 dataPointBuilder.labels( 176 labelMapper.getLabels(dropwizardName, Collections.emptyList(), Collections.emptyList())); 177 } 178 return new CounterSnapshot(metadata, Collections.singletonList(dataPointBuilder.build())); 179 } 180 181 @Override 182 public MetricSnapshots collect() { 183 return collectMetricSnapshots(); 184 } 185 186 protected <K, V> void collectMetricKind( 187 MetricSnapshots.Builder builder, 188 Map<K, V> metrics, 189 BiFunction<String, V, MetricSnapshot> toSnapshot, 190 java.util.function.Function<K, String> keyExtractor) { 191 for (Map.Entry<K, V> entry : metrics.entrySet()) { 192 String metricName = keyExtractor.apply(entry.getKey()); 193 try { 194 MetricSnapshot snapshot = toSnapshot.apply(metricName, entry.getValue()); 195 if (snapshot != null) { 196 builder.metricSnapshot(snapshot); 197 } 198 } catch (Exception e) { 199 if (!invalidMetricHandler.suppressException(metricName, e)) { 200 throw e; 201 } 202 } 203 } 204 } 205 206 // Abstract methods to be implemented by version-specific subclasses 207 208 /** Collect all metric snapshots from the registry. */ 209 protected abstract MetricSnapshots collectMetricSnapshots(); 210 211 protected abstract long getCounterCount(C counter); 212 213 protected abstract Object getGaugeValue(G gauge); 214 215 protected abstract S getHistogramSnapshot(H histogram); 216 217 protected abstract long getHistogramCount(H histogram); 218 219 protected abstract S getTimerSnapshot(T timer); 220 221 protected abstract long getTimerCount(T timer); 222 223 protected abstract long getMeterCount(M meter); 224 225 protected abstract double getMedian(S snapshot); 226 227 protected abstract double get75thPercentile(S snapshot); 228 229 protected abstract double get95thPercentile(S snapshot); 230 231 protected abstract double get98thPercentile(S snapshot); 232 233 protected abstract double get99thPercentile(S snapshot); 234 235 protected abstract double get999thPercentile(S snapshot); 236}