1+ package io .prometheus .metrics .instrumentation .dropwizard ;
2+
3+ import com .codahale .metrics .Timer ;
4+ import com .codahale .metrics .*;
5+ import io .prometheus .metrics .instrumentation .dropwizard5 .labels .CustomLabelMapper ;
6+ import io .prometheus .metrics .model .registry .MultiCollector ;
7+ import io .prometheus .metrics .model .registry .PrometheusRegistry ;
8+ import io .prometheus .metrics .model .snapshots .*;
9+
10+ import java .util .*;
11+ import java .util .concurrent .TimeUnit ;
12+ import java .util .logging .Level ;
13+ import java .util .logging .Logger ;
14+
15+ /**
16+ * Collect Dropwizard metrics from a MetricRegistry.
17+ */
18+ public class DropwizardExports implements MultiCollector {
19+ private static final Logger LOGGER = Logger .getLogger (DropwizardExports .class .getName ());
20+ private final MetricRegistry registry ;
21+ private final MetricFilter metricFilter ;
22+ private final Optional <CustomLabelMapper > labelMapper ;
23+
24+ /**
25+ * Creates a new DropwizardExports and {@link MetricFilter#ALL}.
26+ *
27+ * @param registry a metric registry to export in prometheus.
28+ */
29+ public DropwizardExports (MetricRegistry registry ) {
30+ super ();
31+ this .registry = registry ;
32+ this .metricFilter = MetricFilter .ALL ;
33+ this .labelMapper = Optional .empty ();
34+ }
35+
36+ /**
37+ * Creates a new DropwizardExports with a custom {@link MetricFilter}.
38+ *
39+ * @param registry a metric registry to export in prometheus.
40+ * @param metricFilter a custom metric filter.
41+ */
42+ public DropwizardExports (MetricRegistry registry , MetricFilter metricFilter ) {
43+ this .registry = registry ;
44+ this .metricFilter = metricFilter ;
45+ this .labelMapper = Optional .empty ();
46+ }
47+
48+ /**
49+ * @param registry a metric registry to export in prometheus.
50+ * @param metricFilter a custom metric filter.
51+ * @param labelMapper a labelMapper to use to map labels.
52+ */
53+ public DropwizardExports (MetricRegistry registry , MetricFilter metricFilter , CustomLabelMapper labelMapper ) {
54+ this .registry = registry ;
55+ this .metricFilter = metricFilter ;
56+ this .labelMapper = Optional .ofNullable (labelMapper );
57+ }
58+
59+ private static String getHelpMessage (String metricName , Metric metric ) {
60+ return String .format ("Generated from Dropwizard metric import (metric=%s, type=%s)" ,
61+ metricName , metric .getClass ().getName ());
62+ }
63+
64+ private MetricMetadata getMetricMetaData (String metricName , Metric metric ) {
65+ String name = labelMapper .isPresent () ? labelMapper .get ().getName (metricName ) : metricName ;
66+ return new MetricMetadata (PrometheusNaming .sanitizeMetricName (name ), getHelpMessage (metricName , metric ));
67+ }
68+
69+ /**
70+ * Export counter as Prometheus <a href="https://prometheus.io/docs/concepts/metric_types/#gauge">Gauge</a>.
71+ */
72+ MetricSnapshot fromCounter (String dropwizardName , Counter counter ) {
73+ MetricMetadata metadata = getMetricMetaData (dropwizardName , counter );
74+ CounterSnapshot .CounterDataPointSnapshot .Builder dataPointBuilder = CounterSnapshot .CounterDataPointSnapshot .builder ().value (Long .valueOf (counter .getCount ()).doubleValue ());
75+ labelMapper .map (mapper -> dataPointBuilder .labels (mapper .getLabels (dropwizardName , Collections .emptyList (), Collections .emptyList ())));
76+ return new CounterSnapshot (metadata , Collections .singletonList (dataPointBuilder .build ()));
77+ }
78+
79+ /**
80+ * Export gauge as a prometheus gauge.
81+ */
82+ MetricSnapshot fromGauge (String dropwizardName , Gauge gauge ) {
83+ Object obj = gauge .getValue ();
84+ double value ;
85+ if (obj instanceof Number ) {
86+ value = ((Number ) obj ).doubleValue ();
87+ } else if (obj instanceof Boolean ) {
88+ value = ((Boolean ) obj ) ? 1 : 0 ;
89+ } else {
90+ LOGGER .log (Level .FINE , String .format ("Invalid type for Gauge %s: %s" , PrometheusNaming .sanitizeMetricName (dropwizardName ),
91+ obj == null ? "null" : obj .getClass ().getName ()));
92+ return null ;
93+ }
94+ MetricMetadata metadata = getMetricMetaData (dropwizardName , gauge );
95+ GaugeSnapshot .GaugeDataPointSnapshot .Builder dataPointBuilder = GaugeSnapshot .GaugeDataPointSnapshot .builder ().value (value );
96+ labelMapper .map (mapper -> dataPointBuilder .labels (mapper .getLabels (dropwizardName , Collections .emptyList (), Collections .emptyList ())));
97+ return new GaugeSnapshot (metadata , Collections .singletonList (dataPointBuilder .build ()));
98+ }
99+
100+ /**
101+ * Export a histogram snapshot as a prometheus SUMMARY.
102+ *
103+ * @param dropwizardName metric name.
104+ * @param snapshot the histogram snapshot.
105+ * @param count the total sample count for this snapshot.
106+ * @param factor a factor to apply to histogram values.
107+ */
108+ MetricSnapshot fromSnapshotAndCount (String dropwizardName , Snapshot snapshot , long count , double factor , String helpMessage ) {
109+ Quantiles quantiles = Quantiles .builder ()
110+ .quantile (0.5 , snapshot .getMedian () * factor )
111+ .quantile (0.75 , snapshot .get75thPercentile () * factor )
112+ .quantile (0.95 , snapshot .get95thPercentile () * factor )
113+ .quantile (0.98 , snapshot .get98thPercentile () * factor )
114+ .quantile (0.99 , snapshot .get99thPercentile () * factor )
115+ .quantile (0.999 , snapshot .get999thPercentile () * factor )
116+ .build ();
117+
118+ MetricMetadata metadata = new MetricMetadata (PrometheusNaming .sanitizeMetricName (dropwizardName ), helpMessage );
119+ SummarySnapshot .SummaryDataPointSnapshot .Builder dataPointBuilder = SummarySnapshot .SummaryDataPointSnapshot .builder ().quantiles (quantiles ).count (count );
120+ labelMapper .map (mapper -> dataPointBuilder .labels (mapper .getLabels (dropwizardName , Collections .emptyList (), Collections .emptyList ())));
121+ return new SummarySnapshot (metadata , Collections .singletonList (dataPointBuilder .build ()));
122+ }
123+
124+ /**
125+ * Convert histogram snapshot.
126+ */
127+ MetricSnapshot fromHistogram (String dropwizardName , Histogram histogram ) {
128+ return fromSnapshotAndCount (dropwizardName , histogram .getSnapshot (), histogram .getCount (), 1.0 ,
129+ getHelpMessage (dropwizardName , histogram ));
130+ }
131+
132+ /**
133+ * Export Dropwizard Timer as a histogram. Use TIME_UNIT as time unit.
134+ */
135+ MetricSnapshot fromTimer (String dropwizardName , Timer timer ) {
136+ return fromSnapshotAndCount (dropwizardName , timer .getSnapshot (), timer .getCount (),
137+ 1.0D / TimeUnit .SECONDS .toNanos (1L ), getHelpMessage (dropwizardName , timer ));
138+ }
139+
140+ /**
141+ * Export a Meter as a prometheus COUNTER.
142+ */
143+ MetricSnapshot fromMeter (String dropwizardName , Meter meter ) {
144+ MetricMetadata metadata = getMetricMetaData (dropwizardName + "_total" , meter );
145+ CounterSnapshot .CounterDataPointSnapshot .Builder dataPointBuilder = CounterSnapshot .CounterDataPointSnapshot .builder ().value (meter .getCount ());
146+ labelMapper .map (mapper -> dataPointBuilder .labels (mapper .getLabels (dropwizardName , Collections .emptyList (), Collections .emptyList ())));
147+ return new CounterSnapshot (metadata , Collections .singletonList (dataPointBuilder .build ()));
148+ }
149+
150+ @ Override
151+ public MetricSnapshots collect () {
152+ MetricSnapshots .Builder metricSnapshots = MetricSnapshots .builder ();
153+
154+ for (SortedMap .Entry <String , Gauge > entry : registry .getGauges (metricFilter ).entrySet ()) {
155+ Optional .ofNullable (fromGauge (entry .getKey (), entry .getValue ())).map (metricSnapshots ::metricSnapshot );
156+ }
157+ for (SortedMap .Entry <String , Counter > entry : registry .getCounters (metricFilter ).entrySet ()) {
158+ metricSnapshots .metricSnapshot (fromCounter (entry .getKey (), entry .getValue ()));
159+ }
160+ for (SortedMap .Entry <String , Histogram > entry : registry .getHistograms (metricFilter ).entrySet ()) {
161+ metricSnapshots .metricSnapshot (fromHistogram (entry .getKey (), entry .getValue ()));
162+ }
163+ for (SortedMap .Entry <String , Timer > entry : registry .getTimers (metricFilter ).entrySet ()) {
164+ metricSnapshots .metricSnapshot (fromTimer (entry .getKey (), entry .getValue ()));
165+ }
166+ for (SortedMap .Entry <String , Meter > entry : registry .getMeters (metricFilter ).entrySet ()) {
167+ metricSnapshots .metricSnapshot (fromMeter (entry .getKey (), entry .getValue ()));
168+ }
169+ return metricSnapshots .build ();
170+ }
171+
172+ public static Builder builder () {
173+ return new Builder ();
174+ }
175+
176+ //Builder class for DropwizardExports
177+ public static class Builder {
178+ private MetricRegistry registry ;
179+ private MetricFilter metricFilter ;
180+ private CustomLabelMapper labelMapper ;
181+
182+ private Builder () {
183+ this .metricFilter = MetricFilter .ALL ;
184+ }
185+
186+ public Builder dropwizardRegistry (MetricRegistry registry ) {
187+ this .registry = registry ;
188+ return this ;
189+ }
190+
191+ public Builder metricFilter (MetricFilter metricFilter ) {
192+ this .metricFilter = metricFilter ;
193+ return this ;
194+ }
195+
196+ public Builder customLabelMapper (CustomLabelMapper labelMapper ) {
197+ this .labelMapper = labelMapper ;
198+ return this ;
199+ }
200+
201+ DropwizardExports build () {
202+ if (registry == null ) {
203+ throw new IllegalArgumentException ("MetricRegistry must be set" );
204+ }
205+ if (labelMapper == null ) {
206+ return new DropwizardExports (registry , metricFilter );
207+ } else {
208+ return new DropwizardExports (registry , metricFilter , labelMapper );
209+ }
210+ }
211+
212+ public void register () {
213+ register (PrometheusRegistry .defaultRegistry );
214+ }
215+
216+ public void register (PrometheusRegistry registry ) {
217+ DropwizardExports dropwizardExports = build ();
218+ registry .register (dropwizardExports );
219+ }
220+ }
221+
222+ }
0 commit comments