Skip to content

Commit 3e792c9

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 3e792c9

File tree

10 files changed

+244
-37
lines changed

10 files changed

+244
-37
lines changed

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

Lines changed: 78 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,78 @@ 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(ExporterHttpServerProperties exporterHttpServerProperties) {
147+
this.exporterHttpServerProperties = exporterHttpServerProperties;
148+
return this;
149+
}
150+
151+
public Builder pushgatewayProperties(ExporterPushgatewayProperties pushgatewayProperties) {
152+
this.pushgatewayProperties = pushgatewayProperties;
153+
return this;
154+
}
155+
156+
public Builder exporterOpenTelemetryProperties(ExporterOpenTelemetryProperties exporterOpenTelemetryProperties) {
157+
this.otelConfig = exporterOpenTelemetryProperties;
158+
return this;
159+
}
160+
161+
public PrometheusProperties build() {
162+
return new PrometheusProperties(
163+
defaultMetricsProperties,
164+
metricProperties,
165+
exemplarProperties,
166+
exporterProperties,
167+
exporterFilterProperties,
168+
exporterHttpServerProperties,
169+
pushgatewayProperties,
170+
otelConfig
171+
);
172+
}
173+
174+
}
175+
98176
}

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

Lines changed: 29 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,32 @@ 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())
49+
.isSameAs(defaults.getDefaultMetricProperties());
50+
assertThat(result.getExemplarProperties())
51+
.isSameAs(defaults.getExemplarProperties());
52+
assertThat(result.getExporterFilterProperties())
53+
.isSameAs(defaults.getExporterFilterProperties());
54+
assertThat(result.getExporterHttpServerProperties())
55+
.isSameAs(defaults.getExporterHttpServerProperties());
56+
assertThat(result.getExporterOpenTelemetryProperties())
57+
.isSameAs(defaults.getExporterOpenTelemetryProperties());
58+
assertThat(result.getExporterPushgatewayProperties())
59+
.isSameAs(defaults.getExporterPushgatewayProperties());
60+
}
61+
3362
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@
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 = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
4241
if (exemplarsEnabled) {
4342
exemplarSamplerConfig =
4443
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 1);
@@ -93,7 +92,7 @@ protected CounterSnapshot collect(List<Labels> labels, List<DataPoint> metricDat
9392

9493
@Override
9594
protected boolean isExemplarsEnabled() {
96-
return exemplarsEnabled;
95+
return exemplarSamplerConfig != null;
9796
}
9897

9998
@Override
@@ -112,7 +111,7 @@ static String stripTotalSuffix(String name) {
112111
return name;
113112
}
114113

115-
class DataPoint implements CounterDataPoint {
114+
static class DataPoint implements CounterDataPoint {
116115

117116
private final DoubleAdder doubleValue = new DoubleAdder();
118117
// LongAdder is 20% faster than DoubleAdder. So let's use the LongAdder for long observations,
@@ -168,6 +167,10 @@ public void incWithExemplar(double amount, Labels labels) {
168167
}
169168
}
170169

170+
private boolean isExemplarsEnabled() {
171+
return exemplarSampler != null;
172+
}
173+
171174
private void validateAndAdd(long amount) {
172175
if (amount < 0) {
173176
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,12 @@
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 = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
4948
if (exemplarsEnabled) {
5049
exemplarSamplerConfig =
5150
new ExemplarSamplerConfig(prometheusProperties.getExemplarProperties(), 1);
@@ -104,10 +103,10 @@ protected DataPoint newDataPoint() {
104103

105104
@Override
106105
protected boolean isExemplarsEnabled() {
107-
return exemplarsEnabled;
106+
return exemplarSamplerConfig != null;
108107
}
109108

110-
class DataPoint implements GaugeDataPoint {
109+
static class DataPoint implements GaugeDataPoint {
111110

112111
private final ExemplarSampler exemplarSampler; // null if isExemplarsEnabled() is false
113112

@@ -171,6 +170,11 @@ private GaugeSnapshot.GaugeDataPointSnapshot collect(Labels labels) {
171170
}
172171
return new GaugeSnapshot.GaugeDataPointSnapshot(get(), labels, oldest);
173172
}
173+
174+
private boolean isExemplarsEnabled() {
175+
return exemplarSampler != null;
176+
}
177+
174178
}
175179

176180
public static Builder builder() {

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

Lines changed: 10 additions & 7 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,7 @@ 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 = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
120119
nativeInitialSchema =
121120
getConfigProperty(
122121
properties,
@@ -159,10 +158,14 @@ private Histogram(Histogram.Builder builder, PrometheusProperties prometheusProp
159158
nativeResetDurationSeconds =
160159
getConfigProperty(properties, MetricsProperties::getHistogramNativeResetDurationSeconds);
161160
ExemplarsProperties exemplarsProperties = prometheusProperties.getExemplarProperties();
162-
exemplarSamplerConfig =
161+
if (exemplarsEnabled) {
162+
exemplarSamplerConfig =
163163
classicUpperBounds.length == 0
164-
? new ExemplarSamplerConfig(exemplarsProperties, 4)
165-
: new ExemplarSamplerConfig(exemplarsProperties, classicUpperBounds);
164+
? new ExemplarSamplerConfig(exemplarsProperties, 4)
165+
: new ExemplarSamplerConfig(exemplarsProperties, classicUpperBounds);
166+
} else {
167+
exemplarSamplerConfig = null;
168+
}
166169
}
167170

168171
@Override
@@ -177,7 +180,7 @@ public void observeWithExemplar(double amount, Labels labels) {
177180

178181
@Override
179182
protected boolean isExemplarsEnabled() {
180-
return exemplarsEnabled;
183+
return exemplarSamplerConfig != null;
181184
}
182185

183186
public class DataPoint implements DistributionDataPoint {
@@ -198,7 +201,7 @@ public class DataPoint implements DistributionDataPoint {
198201
private final ExemplarSampler exemplarSampler;
199202

200203
private DataPoint() {
201-
if (exemplarsEnabled) {
204+
if (isExemplarsEnabled()) {
202205
exemplarSampler = new ExemplarSampler(exemplarSamplerConfig);
203206
} else {
204207
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.
163+
* If a {@code MetricProperties} is configured for the metric name it has higher precedence
164+
* than the builder configuration. A special case is the setting {@link Builder#withoutExemplars()}
165+
* via the 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: 11 additions & 8 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 =
52+
boolean exemplarsEnabled = getConfigProperty(properties, MetricsProperties::getExemplarsEnabled);
53+
quantiles = Collections.unmodifiableList(makeQuantiles(properties));
54+
maxAgeSeconds = getConfigProperty(properties, MetricsProperties::getSummaryMaxAgeSeconds);
55+
ageBuckets =
5756
getConfigProperty(properties, MetricsProperties::getSummaryNumberOfAgeBuckets);
58-
this.exemplarSamplerConfig =
57+
if (exemplarsEnabled) {
58+
exemplarSamplerConfig =
5959
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)