Skip to content

Commit 2bc9d91

Browse files
committed
#342 - Implemented Label Value Sanitizer
1 parent ca6145f commit 2bc9d91

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.prometheus.client;
2+
3+
public interface LabelValueSanitizer {
4+
String[] sanitize(String... labelValue);
5+
}

Diff for: simpleclient/src/main/java/io/prometheus/client/SimpleCollector.java

+45
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public abstract class SimpleCollector<Child> extends Collector {
5151
protected final String help;
5252
protected final String unit;
5353
protected final List<String> labelNames;
54+
protected final LabelValueSanitizer labelValueSanitizer;
5455

5556
protected final ConcurrentMap<List<String>, Child> children = new ConcurrentHashMap<List<String>, Child>();
5657
protected Child noLabelsChild;
@@ -64,6 +65,7 @@ public Child labels(String... labelValues) {
6465
if (labelValues.length != labelNames.size()) {
6566
throw new IllegalArgumentException("Incorrect number of labels.");
6667
}
68+
labelValues = labelValueSanitizer.sanitize(labelValues);
6769
for (String label: labelValues) {
6870
if (label == null) {
6971
throw new IllegalArgumentException("Label cannot be null.");
@@ -174,12 +176,42 @@ protected SimpleCollector(Builder b) {
174176
for (String n: labelNames) {
175177
checkMetricLabelName(n);
176178
}
179+
labelValueSanitizer = b.labelValueSanitizer;
177180

178181
if (!b.dontInitializeNoLabelsChild) {
179182
initializeNoLabelsChild();
180183
}
181184
}
182185

186+
/**
187+
* Default Sanitizer - Will not perform any manipulation on labels
188+
*/
189+
static LabelValueSanitizer NoOpLabelValueSanitizer() {
190+
return new LabelValueSanitizer() {
191+
@Override
192+
public String[] sanitize(String... labelValue) {
193+
return labelValue;
194+
}
195+
};
196+
}
197+
198+
/**
199+
* Transforms null labels to an empty string to guard against IllegalArgument runtime exceptions
200+
*/
201+
static LabelValueSanitizer TransformNullLabelsToEmptyString() {
202+
return new LabelValueSanitizer() {
203+
@Override
204+
public String[] sanitize(String... labelValue) {
205+
for (int i = 0; i < labelValue.length; i++) {
206+
if (labelValue[i] == null) {
207+
labelValue[i] = "";
208+
}
209+
}
210+
return labelValue;
211+
}
212+
};
213+
}
214+
183215
/**
184216
* Builders let you configure and then create collectors.
185217
*/
@@ -191,6 +223,7 @@ public abstract static class Builder<B extends Builder<B, C>, C extends SimpleCo
191223
String unit = "";
192224
String help = "";
193225
String[] labelNames = new String[]{};
226+
LabelValueSanitizer labelValueSanitizer = NoOpLabelValueSanitizer();
194227
// Some metrics require additional setup before the initialization can be done.
195228
boolean dontInitializeNoLabelsChild;
196229

@@ -239,6 +272,18 @@ public B labelNames(String... labelNames) {
239272
return (B)this;
240273
}
241274

275+
/**
276+
* Sanitize labels. Optional, defaults to no manipulation of labels.
277+
* Useful to scrub credentials from labels
278+
* or avoid Null values causing runtime exceptions
279+
* @param handler
280+
* @return new array of labels to use
281+
*/
282+
public B labelValueSanitizer(LabelValueSanitizer handler) {
283+
this.labelValueSanitizer = handler;
284+
return (B)this;
285+
}
286+
242287
/**
243288
* Return the constructed collector.
244289
* <p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.prometheus.client;
2+
3+
import org.junit.Before;
4+
import org.junit.Rule;
5+
import org.junit.Test;
6+
import org.junit.rules.ExpectedException;
7+
8+
import static org.junit.Assert.*;
9+
import static org.junit.rules.ExpectedException.none;
10+
11+
12+
public class SimpleCollectorWithLabelSanitizerTest {
13+
14+
CollectorRegistry registry;
15+
Gauge metric;
16+
17+
@Rule
18+
public final ExpectedException thrown = none();
19+
20+
@Before
21+
public void setUp() {
22+
registry = new CollectorRegistry();
23+
metric = Gauge.build().name("nonulllabels").help("help").labelNames("l").labelValueSanitizer(Gauge.TransformNullLabelsToEmptyString()).register(registry);
24+
}
25+
26+
private Double getValue(String labelValue) {
27+
return registry.getSampleValue("nonulllabels", new String[]{"l"}, new String[]{labelValue});
28+
}
29+
30+
@Test
31+
public void testNullLabelDoesntThrowWithLabelSanitizer() {
32+
metric.labels(new String[]{null}).inc();
33+
assertEquals(1.0, getValue("").doubleValue(), .001);
34+
}
35+
}

0 commit comments

Comments
 (0)