Skip to content

Commit 48b81c3

Browse files
committed
Make sure when Builder.withoutExemplars is used, that this cannot be overridden via properties. Introduce builder for PrometheusProperties for testing. Minimal housekeeping, removing the redundant boolean flag.
Signed-off-by: Jens Wilke <signed-off@cruftex.net>
1 parent 68b28f4 commit 48b81c3

File tree

10 files changed

+224
-44
lines changed

10 files changed

+224
-44
lines changed

prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusProperties.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public static PrometheusProperties get() throws PrometheusPropertiesException {
3636
return instance;
3737
}
3838

39+
public static Builder builder() {
40+
return new Builder();
41+
}
42+
3943
public PrometheusProperties(
4044
MetricsProperties defaultMetricsProperties,
4145
Map<String, MetricsProperties> metricProperties,
@@ -95,4 +99,77 @@ public ExporterPushgatewayProperties getExporterPushgatewayProperties() {
9599
public ExporterOpenTelemetryProperties getExporterOpenTelemetryProperties() {
96100
return exporterOpenTelemetryProperties;
97101
}
102+
103+
public static class Builder {
104+
private MetricsProperties defaultMetricsProperties;
105+
private Map<String, MetricsProperties> metricProperties = new HashMap<>();
106+
private ExemplarsProperties exemplarProperties;
107+
private ExporterProperties exporterProperties;
108+
private ExporterFilterProperties exporterFilterProperties;
109+
private ExporterHttpServerProperties exporterHttpServerProperties;
110+
private ExporterPushgatewayProperties pushgatewayProperties;
111+
private ExporterOpenTelemetryProperties otelConfig;
112+
113+
private Builder() {}
114+
115+
public Builder defaultMetricsProperties(MetricsProperties defaultMetricsProperties) {
116+
this.defaultMetricsProperties = defaultMetricsProperties;
117+
return this;
118+
}
119+
120+
public Builder metricProperties(Map<String, MetricsProperties> metricProperties) {
121+
this.metricProperties = metricProperties;
122+
return this;
123+
}
124+
125+
/** Convenience for adding a single named MetricsProperties */
126+
public Builder putMetricProperty(String name, MetricsProperties props) {
127+
this.metricProperties.put(name, props);
128+
return this;
129+
}
130+
131+
public Builder exemplarProperties(ExemplarsProperties exemplarProperties) {
132+
this.exemplarProperties = exemplarProperties;
133+
return this;
134+
}
135+
136+
public Builder exporterProperties(ExporterProperties exporterProperties) {
137+
this.exporterProperties = exporterProperties;
138+
return this;
139+
}
140+
141+
public Builder exporterFilterProperties(ExporterFilterProperties exporterFilterProperties) {
142+
this.exporterFilterProperties = exporterFilterProperties;
143+
return this;
144+
}
145+
146+
public Builder exporterHttpServerProperties(
147+
ExporterHttpServerProperties exporterHttpServerProperties) {
148+
this.exporterHttpServerProperties = exporterHttpServerProperties;
149+
return this;
150+
}
151+
152+
public Builder pushgatewayProperties(ExporterPushgatewayProperties pushgatewayProperties) {
153+
this.pushgatewayProperties = pushgatewayProperties;
154+
return this;
155+
}
156+
157+
public Builder exporterOpenTelemetryProperties(
158+
ExporterOpenTelemetryProperties exporterOpenTelemetryProperties) {
159+
this.otelConfig = exporterOpenTelemetryProperties;
160+
return this;
161+
}
162+
163+
public PrometheusProperties build() {
164+
return new PrometheusProperties(
165+
defaultMetricsProperties,
166+
metricProperties,
167+
exemplarProperties,
168+
exporterProperties,
169+
exporterFilterProperties,
170+
exporterHttpServerProperties,
171+
pushgatewayProperties,
172+
otelConfig);
173+
}
174+
}
98175
}

prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/PrometheusPropertiesTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.io.IOException;
66
import java.io.InputStream;
7+
import java.util.HashMap;
78
import java.util.Properties;
89
import org.junit.jupiter.api.Test;
910

@@ -30,4 +31,29 @@ public void testEmptyUpperBounds() throws IOException {
3031
MetricsProperties.load("io.prometheus.metrics", properties);
3132
assertThat(properties).isEmpty();
3233
}
34+
35+
@Test
36+
public void testBuilder() {
37+
PrometheusProperties defaults = PrometheusPropertiesLoader.load(new HashMap<>());
38+
PrometheusProperties.Builder builder = PrometheusProperties.builder();
39+
builder.defaultMetricsProperties(defaults.getDefaultMetricProperties());
40+
builder.exemplarProperties(defaults.getExemplarProperties());
41+
builder.defaultMetricsProperties(defaults.getDefaultMetricProperties());
42+
builder.exporterFilterProperties(defaults.getExporterFilterProperties());
43+
builder.exporterHttpServerProperties(defaults.getExporterHttpServerProperties());
44+
builder.exporterOpenTelemetryProperties(defaults.getExporterOpenTelemetryProperties());
45+
builder.pushgatewayProperties(defaults.getExporterPushgatewayProperties());
46+
PrometheusProperties result = builder.build();
47+
assertThat(result.getDefaultMetricProperties()).isSameAs(defaults.getDefaultMetricProperties());
48+
assertThat(result.getDefaultMetricProperties()).isSameAs(defaults.getDefaultMetricProperties());
49+
assertThat(result.getExemplarProperties()).isSameAs(defaults.getExemplarProperties());
50+
assertThat(result.getExporterFilterProperties())
51+
.isSameAs(defaults.getExporterFilterProperties());
52+
assertThat(result.getExporterHttpServerProperties())
53+
.isSameAs(defaults.getExporterHttpServerProperties());
54+
assertThat(result.getExporterOpenTelemetryProperties())
55+
.isSameAs(defaults.getExporterOpenTelemetryProperties());
56+
assertThat(result.getExporterPushgatewayProperties())
57+
.isSameAs(defaults.getExporterPushgatewayProperties());
58+
}
3359
}

prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Counter.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232
public class Counter extends StatefulMetric<CounterDataPoint, Counter.DataPoint>
3333
implements CounterDataPoint {
3434

35-
private final boolean exemplarsEnabled;
3635
private final ExemplarSamplerConfig exemplarSamplerConfig;
3736

3837
private Counter(Builder builder, PrometheusProperties prometheusProperties) {
3938
super(builder);
4039
MetricsProperties[] properties = getMetricProperties(builder, prometheusProperties);
41-
exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
40+
boolean exemplarsEnabled =
41+
getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
4242
if (exemplarsEnabled) {
4343
exemplarSamplerConfig =
4444
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 1);
@@ -93,7 +93,7 @@ protected CounterSnapshot collect(List<Labels> labels, List<DataPoint> metricDat
9393

9494
@Override
9595
protected boolean isExemplarsEnabled() {
96-
return exemplarsEnabled;
96+
return exemplarSamplerConfig != null;
9797
}
9898

9999
@Override
@@ -112,7 +112,7 @@ static String stripTotalSuffix(String name) {
112112
return name;
113113
}
114114

115-
class DataPoint implements CounterDataPoint {
115+
static class DataPoint implements CounterDataPoint {
116116

117117
private final DoubleAdder doubleValue = new DoubleAdder();
118118
// LongAdder is 20% faster than DoubleAdder. So let's use the LongAdder for long observations,
@@ -168,6 +168,10 @@ public void incWithExemplar(double amount, Labels labels) {
168168
}
169169
}
170170

171+
private boolean isExemplarsEnabled() {
172+
return exemplarSampler != null;
173+
}
174+
171175
private void validateAndAdd(long amount) {
172176
if (amount < 0) {
173177
throw new IllegalArgumentException(

prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Gauge.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@
3939
public class Gauge extends StatefulMetric<GaugeDataPoint, Gauge.DataPoint>
4040
implements GaugeDataPoint {
4141

42-
private final boolean exemplarsEnabled;
4342
private final ExemplarSamplerConfig exemplarSamplerConfig;
4443

4544
private Gauge(Builder builder, PrometheusProperties prometheusProperties) {
4645
super(builder);
4746
MetricsProperties[] properties = getMetricProperties(builder, prometheusProperties);
48-
exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
47+
boolean exemplarsEnabled =
48+
getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
4949
if (exemplarsEnabled) {
5050
exemplarSamplerConfig =
5151
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 1);
@@ -104,10 +104,10 @@ protected DataPoint newDataPoint() {
104104

105105
@Override
106106
protected boolean isExemplarsEnabled() {
107-
return exemplarsEnabled;
107+
return exemplarSamplerConfig != null;
108108
}
109109

110-
class DataPoint implements GaugeDataPoint {
110+
static class DataPoint implements GaugeDataPoint {
111111

112112
private final ExemplarSampler exemplarSampler; // null if isExemplarsEnabled() is false
113113

@@ -171,6 +171,10 @@ private GaugeSnapshot.GaugeDataPointSnapshot collect(Labels labels) {
171171
}
172172
return new GaugeSnapshot.GaugeDataPointSnapshot(get(), labels, oldest);
173173
}
174+
175+
private boolean isExemplarsEnabled() {
176+
return exemplarSampler != null;
177+
}
174178
}
175179

176180
public static Builder builder() {

prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Histogram.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public class Histogram extends StatefulMetric<DistributionDataPoint, Histogram.D
6868
// NATIVE_BOUNDS is used to look up the native bucket index depending on the current schema.
6969
private static final double[][] NATIVE_BOUNDS;
7070

71-
private final boolean exemplarsEnabled;
7271
private final ExemplarSamplerConfig exemplarSamplerConfig;
7372

7473
// Upper bounds for the classic histogram buckets. Contains at least +Inf.
@@ -116,7 +115,8 @@ public class Histogram extends StatefulMetric<DistributionDataPoint, Histogram.D
116115
private Histogram(Histogram.Builder builder, PrometheusProperties prometheusProperties) {
117116
super(builder);
118117
MetricsProperties[] properties = getMetricProperties(builder, prometheusProperties);
119-
exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
118+
boolean exemplarsEnabled =
119+
getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
120120
nativeInitialSchema =
121121
getConfigProperty(
122122
properties,
@@ -159,10 +159,14 @@ private Histogram(Histogram.Builder builder, PrometheusProperties prometheusProp
159159
nativeResetDurationSeconds =
160160
getConfigProperty(properties, MetricsProperties::getHistogramNativeResetDurationSeconds);
161161
ExemplarsProperties exemplarsProperties = prometheusProperties.getExemplarProperties();
162-
exemplarSamplerConfig =
163-
classicUpperBounds.length == 0
164-
? new ExemplarSamplerConfig(exemplarsProperties, 4)
165-
: new ExemplarSamplerConfig(exemplarsProperties, classicUpperBounds);
162+
if (exemplarsEnabled) {
163+
exemplarSamplerConfig =
164+
classicUpperBounds.length == 0
165+
? new ExemplarSamplerConfig(exemplarsProperties, 4)
166+
: new ExemplarSamplerConfig(exemplarsProperties, classicUpperBounds);
167+
} else {
168+
exemplarSamplerConfig = null;
169+
}
166170
}
167171

168172
@Override
@@ -177,7 +181,7 @@ public void observeWithExemplar(double amount, Labels labels) {
177181

178182
@Override
179183
protected boolean isExemplarsEnabled() {
180-
return exemplarsEnabled;
184+
return exemplarSamplerConfig != null;
181185
}
182186

183187
public class DataPoint implements DistributionDataPoint {
@@ -198,7 +202,7 @@ public class DataPoint implements DistributionDataPoint {
198202
private final ExemplarSampler exemplarSampler;
199203

200204
private DataPoint() {
201-
if (exemplarsEnabled) {
205+
if (isExemplarsEnabled()) {
202206
exemplarSampler = new ExemplarSampler(exemplarSamplerConfig);
203207
} else {
204208
exemplarSampler = null;

prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/StatefulMetric.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.Collections;
1111
import java.util.List;
1212
import java.util.Map;
13+
import java.util.Objects;
1314
import java.util.concurrent.ConcurrentHashMap;
1415
import java.util.function.Function;
1516

@@ -157,23 +158,26 @@ protected T getNoLabels() {
157158
return noLabels;
158159
}
159160

161+
/**
162+
* Metric properties in effect by order of precedence with the highest precedence first. If a
163+
* {@code MetricProperties} is configured for the metric name it has higher precedence than the
164+
* builder configuration. A special case is the setting {@link Builder#withoutExemplars()} via the
165+
* builder, which cannot be overridden by any configuration.
166+
*/
160167
protected MetricsProperties[] getMetricProperties(
161168
Builder<?, ?> builder, PrometheusProperties prometheusProperties) {
169+
List<MetricsProperties> properties = new ArrayList<>();
170+
if (Objects.equals(builder.exemplarsEnabled, false)) {
171+
properties.add(MetricsProperties.builder().exemplarsEnabled(false).build());
172+
}
162173
String metricName = getMetadata().getName();
163174
if (prometheusProperties.getMetricProperties(metricName) != null) {
164-
return new MetricsProperties[] {
165-
prometheusProperties.getMetricProperties(metricName), // highest precedence
166-
builder.toProperties(), // second-highest precedence
167-
prometheusProperties.getDefaultMetricProperties(), // third-highest precedence
168-
builder.getDefaultProperties() // fallback
169-
};
170-
} else {
171-
return new MetricsProperties[] {
172-
builder.toProperties(), // highest precedence
173-
prometheusProperties.getDefaultMetricProperties(), // second-highest precedence
174-
builder.getDefaultProperties() // fallback
175-
};
175+
properties.add(prometheusProperties.getMetricProperties(metricName));
176176
}
177+
properties.add(builder.toProperties());
178+
properties.add(prometheusProperties.getDefaultMetricProperties());
179+
properties.add(builder.getDefaultProperties()); // fallback
180+
return properties.toArray(new MetricsProperties[0]);
177181
}
178182

179183
protected <P> P getConfigProperty(

prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Summary.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,22 @@ public class Summary extends StatefulMetric<DistributionDataPoint, Summary.DataP
4444
private final List<CKMSQuantiles.Quantile> quantiles; // May be empty, but cannot be null.
4545
private final long maxAgeSeconds;
4646
private final int ageBuckets;
47-
private final boolean exemplarsEnabled;
4847
private final ExemplarSamplerConfig exemplarSamplerConfig;
4948

5049
private Summary(Builder builder, PrometheusProperties prometheusProperties) {
5150
super(builder);
5251
MetricsProperties[] properties = getMetricProperties(builder, prometheusProperties);
53-
this.exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
54-
this.quantiles = Collections.unmodifiableList(makeQuantiles(properties));
55-
this.maxAgeSeconds = getConfigProperty(properties, MetricsProperties::getSummaryMaxAgeSeconds);
56-
this.ageBuckets =
57-
getConfigProperty(properties, MetricsProperties::getSummaryNumberOfAgeBuckets);
58-
this.exemplarSamplerConfig =
59-
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 4);
52+
boolean exemplarsEnabled =
53+
getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
54+
quantiles = Collections.unmodifiableList(makeQuantiles(properties));
55+
maxAgeSeconds = getConfigProperty(properties, MetricsProperties::getSummaryMaxAgeSeconds);
56+
ageBuckets = getConfigProperty(properties, MetricsProperties::getSummaryNumberOfAgeBuckets);
57+
if (exemplarsEnabled) {
58+
exemplarSamplerConfig =
59+
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 4);
60+
} else {
61+
exemplarSamplerConfig = null;
62+
}
6063
}
6164

6265
private List<CKMSQuantiles.Quantile> makeQuantiles(MetricsProperties[] properties) {
@@ -79,7 +82,7 @@ private List<CKMSQuantiles.Quantile> makeQuantiles(MetricsProperties[] propertie
7982

8083
@Override
8184
protected boolean isExemplarsEnabled() {
82-
return exemplarsEnabled;
85+
return exemplarSamplerConfig != null;
8386
}
8487

8588
@Override
@@ -134,7 +137,7 @@ private DataPoint() {
134137
} else {
135138
quantileValues = null;
136139
}
137-
if (exemplarsEnabled) {
140+
if (isExemplarsEnabled()) {
138141
exemplarSampler = new ExemplarSampler(exemplarSamplerConfig);
139142
} else {
140143
exemplarSampler = null;

0 commit comments

Comments
 (0)