Skip to content

Commit a1dc304

Browse files
committed
Allow subclasses of DropwizardExports to deal with validation errors
In the current form, if e.g. a Counter contains invalid data the entire `collect` calls fails with InvalidArgumentException. In large applications, it may be preferable to gracefully degrade the available metrics and report the failure via e.g. logging. This change allows just that - by changing the access modifiers to the `fromXYZ` methods subclasses can now handle the errors. Finally, to allow for skipping of invalid metrics, we follow the example in `fromGauge` to interpret the meaning of a returned null to be that the metric should be skipped.
1 parent c3f43d8 commit a1dc304

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

prometheus-metrics-instrumentation-dropwizard/src/main/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExports.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private MetricMetadata getMetricMetaData(String metricName, Metric metric) {
8585
* Export counter as Prometheus <a
8686
* href="https://prometheus.io/docs/concepts/metric_types/#gauge">Gauge</a>.
8787
*/
88-
MetricSnapshot fromCounter(String dropwizardName, Counter counter) {
88+
protected MetricSnapshot fromCounter(String dropwizardName, Counter counter) {
8989
MetricMetadata metadata = getMetricMetaData(dropwizardName, counter);
9090
CounterSnapshot.CounterDataPointSnapshot.Builder dataPointBuilder =
9191
CounterSnapshot.CounterDataPointSnapshot.builder()
@@ -99,7 +99,7 @@ MetricSnapshot fromCounter(String dropwizardName, Counter counter) {
9999
}
100100

101101
/** Export gauge as a prometheus gauge. */
102-
MetricSnapshot fromGauge(String dropwizardName, Gauge<?> gauge) {
102+
protected MetricSnapshot fromGauge(String dropwizardName, Gauge<?> gauge) {
103103
Object obj = gauge.getValue();
104104
double value;
105105
if (obj instanceof Number) {
@@ -134,7 +134,7 @@ MetricSnapshot fromGauge(String dropwizardName, Gauge<?> gauge) {
134134
* @param count the total sample count for this snapshot.
135135
* @param factor a factor to apply to histogram values.
136136
*/
137-
MetricSnapshot fromSnapshotAndCount(
137+
protected MetricSnapshot fromSnapshotAndCount(
138138
String dropwizardName, Snapshot snapshot, long count, double factor, String helpMessage) {
139139
Quantiles quantiles =
140140
Quantiles.builder()
@@ -159,7 +159,7 @@ MetricSnapshot fromSnapshotAndCount(
159159
}
160160

161161
/** Convert histogram snapshot. */
162-
MetricSnapshot fromHistogram(String dropwizardName, Histogram histogram) {
162+
protected MetricSnapshot fromHistogram(String dropwizardName, Histogram histogram) {
163163
return fromSnapshotAndCount(
164164
dropwizardName,
165165
histogram.getSnapshot(),
@@ -169,7 +169,7 @@ MetricSnapshot fromHistogram(String dropwizardName, Histogram histogram) {
169169
}
170170

171171
/** Export Dropwizard Timer as a histogram. Use TIME_UNIT as time unit. */
172-
MetricSnapshot fromTimer(String dropwizardName, Timer timer) {
172+
protected MetricSnapshot fromTimer(String dropwizardName, Timer timer) {
173173
return fromSnapshotAndCount(
174174
dropwizardName,
175175
timer.getSnapshot(),
@@ -179,7 +179,7 @@ MetricSnapshot fromTimer(String dropwizardName, Timer timer) {
179179
}
180180

181181
/** Export a Meter as a prometheus COUNTER. */
182-
MetricSnapshot fromMeter(String dropwizardName, Meter meter) {
182+
protected MetricSnapshot fromMeter(String dropwizardName, Meter meter) {
183183
MetricMetadata metadata = getMetricMetaData(dropwizardName + "_total", meter);
184184
CounterSnapshot.CounterDataPointSnapshot.Builder dataPointBuilder =
185185
CounterSnapshot.CounterDataPointSnapshot.builder().value(meter.getCount());
@@ -207,17 +207,40 @@ public MetricSnapshots collect() {
207207

208208
registry
209209
.getCounters(metricFilter)
210-
.forEach((name, counter) -> metricSnapshots.metricSnapshot(fromCounter(name, counter)));
210+
.forEach(
211+
(name, counter) -> {
212+
MetricSnapshot snapshot = fromCounter(name, counter);
213+
if (snapshot != null) {
214+
metricSnapshots.metricSnapshot(snapshot);
215+
}
216+
});
211217
registry
212218
.getHistograms(metricFilter)
213219
.forEach(
214-
(name, histogram) -> metricSnapshots.metricSnapshot(fromHistogram(name, histogram)));
220+
(name, histogram) -> {
221+
MetricSnapshot snapshot = fromHistogram(name, histogram);
222+
if (snapshot != null) {
223+
metricSnapshots.metricSnapshot(snapshot);
224+
}
225+
});
215226
registry
216227
.getTimers(metricFilter)
217-
.forEach((name, timer) -> metricSnapshots.metricSnapshot(fromTimer(name, timer)));
228+
.forEach(
229+
(name, timer) -> {
230+
MetricSnapshot snapshot = fromTimer(name, timer);
231+
if (snapshot != null) {
232+
metricSnapshots.metricSnapshot(snapshot);
233+
}
234+
});
218235
registry
219236
.getMeters(metricFilter)
220-
.forEach((name, meter) -> metricSnapshots.metricSnapshot(fromMeter(name, meter)));
237+
.forEach(
238+
(name, meter) -> {
239+
MetricSnapshot snapshot = fromMeter(name, meter);
240+
if (snapshot != null) {
241+
metricSnapshots.metricSnapshot(snapshot);
242+
}
243+
});
221244

222245
return metricSnapshots.build();
223246
}

prometheus-metrics-instrumentation-dropwizard/src/test/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExportsTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.codahale.metrics.*;
88
import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
99
import io.prometheus.metrics.model.registry.PrometheusRegistry;
10+
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
1011
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
1112
import java.io.ByteArrayOutputStream;
1213
import java.io.IOException;
@@ -278,6 +279,52 @@ public void testThatMetricHelpUsesOriginalDropwizardName() {
278279
assertThat(convertToOpenMetricsFormat()).isEqualTo(expected);
279280
}
280281

282+
@Test
283+
void responseWhenRegistryIsEmpty() {
284+
var registry = new PrometheusRegistry();
285+
registry.register(new IgnoreInvalidCounter(metricRegistry));
286+
assertThat(convertToOpenMetricsFormat(registry))
287+
.isEqualTo(
288+
"""
289+
# EOF
290+
""");
291+
}
292+
293+
@Test
294+
void collectInvalidMetricFails() {
295+
metricRegistry.counter("my.application.namedCounter1").inc(-10);
296+
assertThatThrownBy(() -> convertToOpenMetricsFormat())
297+
.isInstanceOf(IllegalArgumentException.class);
298+
}
299+
300+
@Test
301+
void allowSubclassesToIgnoreInvalidMetric() {
302+
var registry = new PrometheusRegistry();
303+
registry.register(new IgnoreInvalidCounter(metricRegistry));
304+
metricRegistry.counter("my.application.namedCounter1").inc(-10);
305+
assertThat(convertToOpenMetricsFormat(registry))
306+
.isEqualTo(
307+
"""
308+
# EOF
309+
""");
310+
}
311+
312+
private static class IgnoreInvalidCounter extends DropwizardExports {
313+
314+
public IgnoreInvalidCounter(MetricRegistry registry) {
315+
super(registry);
316+
}
317+
318+
@Override
319+
protected MetricSnapshot fromCounter(String dropwizardName, Counter counter) {
320+
try {
321+
return super.fromCounter(dropwizardName, counter);
322+
} catch (IllegalArgumentException exc) {
323+
return null;
324+
}
325+
}
326+
}
327+
281328
private static class ExampleDoubleGauge implements Gauge<Double> {
282329
@Override
283330
public Double getValue() {

0 commit comments

Comments
 (0)