Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package io.prometheus.metrics.config;

import javax.annotation.Nullable;

/** Properties starting with io.prometheus.open_metrics2 */
public class OpenMetrics2Properties {

private static final String PREFIX = "io.prometheus.open_metrics2";
private static final String CONTENT_NEGOTIATION = "content_negotiation";
private static final String DISABLE_SUFFIX_APPENDING = "disable_suffix_appending";
private static final String COMPOSITE_VALUES = "composite_values";
private static final String EXEMPLAR_COMPLIANCE = "exemplar_compliance";
private static final String NATIVE_HISTOGRAMS = "native_histograms";

@Nullable private final Boolean contentNegotiation;
@Nullable private final Boolean disableSuffixAppending;
@Nullable private final Boolean compositeValues;
@Nullable private final Boolean exemplarCompliance;
@Nullable private final Boolean nativeHistograms;

private OpenMetrics2Properties(
@Nullable Boolean contentNegotiation,
@Nullable Boolean disableSuffixAppending,
@Nullable Boolean compositeValues,
@Nullable Boolean exemplarCompliance,
@Nullable Boolean nativeHistograms) {
this.contentNegotiation = contentNegotiation;
this.disableSuffixAppending = disableSuffixAppending;
this.compositeValues = compositeValues;
this.exemplarCompliance = exemplarCompliance;
this.nativeHistograms = nativeHistograms;
}

/** Gate OM2 features behind content negotiation. Default is {@code false}. */
public boolean getContentNegotiation() {
return contentNegotiation != null && contentNegotiation;
}

/** Suppress {@code _total} and unit suffixes. Default is {@code false}. */
public boolean getDisableSuffixAppending() {
return disableSuffixAppending != null && disableSuffixAppending;
}

/** Single-line histogram/summary with {@code st@}. Default is {@code false}. */
public boolean getCompositeValues() {
return compositeValues != null && compositeValues;
}

/** Mandatory timestamps, no 128-char limit for exemplars. Default is {@code false}. */
public boolean getExemplarCompliance() {
return exemplarCompliance != null && exemplarCompliance;
}

/** Exponential buckets support for native histograms. Default is {@code false}. */
public boolean getNativeHistograms() {
return nativeHistograms != null && nativeHistograms;
}

/**
* Note that this will remove entries from {@code propertySource}. This is because we want to know
* if there are unused properties remaining after all properties have been loaded.
*/
static OpenMetrics2Properties load(PropertySource propertySource)
throws PrometheusPropertiesException {
Boolean contentNegotiation = Util.loadBoolean(PREFIX, CONTENT_NEGOTIATION, propertySource);
Boolean disableSuffixAppending =
Util.loadBoolean(PREFIX, DISABLE_SUFFIX_APPENDING, propertySource);
Boolean compositeValues = Util.loadBoolean(PREFIX, COMPOSITE_VALUES, propertySource);
Boolean exemplarCompliance = Util.loadBoolean(PREFIX, EXEMPLAR_COMPLIANCE, propertySource);
Boolean nativeHistograms = Util.loadBoolean(PREFIX, NATIVE_HISTOGRAMS, propertySource);
return new OpenMetrics2Properties(
contentNegotiation,
disableSuffixAppending,
compositeValues,
exemplarCompliance,
nativeHistograms);
}

public static Builder builder() {
return new Builder();
}

public static class Builder {

@Nullable private Boolean contentNegotiation;
@Nullable private Boolean disableSuffixAppending;
@Nullable private Boolean compositeValues;
@Nullable private Boolean exemplarCompliance;
@Nullable private Boolean nativeHistograms;

private Builder() {}

/** See {@link #getContentNegotiation()} */
public Builder contentNegotiation(boolean contentNegotiation) {
this.contentNegotiation = contentNegotiation;
return this;
}

/** See {@link #getDisableSuffixAppending()} */
public Builder disableSuffixAppending(boolean disableSuffixAppending) {
this.disableSuffixAppending = disableSuffixAppending;
return this;
}

/** See {@link #getCompositeValues()} */
public Builder compositeValues(boolean compositeValues) {
this.compositeValues = compositeValues;
return this;
}

/** See {@link #getExemplarCompliance()} */
public Builder exemplarCompliance(boolean exemplarCompliance) {
this.exemplarCompliance = exemplarCompliance;
return this;
}

/** See {@link #getNativeHistograms()} */
public Builder nativeHistograms(boolean nativeHistograms) {
this.nativeHistograms = nativeHistograms;
return this;
}

/** Enable all OpenMetrics 2.0 features */
public Builder enableAll() {
this.contentNegotiation = true;
this.disableSuffixAppending = true;
this.compositeValues = true;
this.exemplarCompliance = true;
this.nativeHistograms = true;
return this;
}

public OpenMetrics2Properties build() {
return new OpenMetrics2Properties(
contentNegotiation,
disableSuffixAppending,
compositeValues,
exemplarCompliance,
nativeHistograms);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.annotation.Nullable;

/**
Expand All @@ -21,6 +22,7 @@ public class PrometheusProperties {
private final ExporterHttpServerProperties exporterHttpServerProperties;
private final ExporterOpenTelemetryProperties exporterOpenTelemetryProperties;
private final ExporterPushgatewayProperties exporterPushgatewayProperties;
private final OpenMetrics2Properties openMetrics2Properties;

/**
* Map that stores metric-specific properties keyed by metric name in exposition format
Expand Down Expand Up @@ -111,7 +113,8 @@ public static Builder builder() {
ExporterFilterProperties exporterFilterProperties,
ExporterHttpServerProperties httpServerConfig,
ExporterPushgatewayProperties pushgatewayProperties,
ExporterOpenTelemetryProperties otelConfig) {
ExporterOpenTelemetryProperties otelConfig,
OpenMetrics2Properties openMetrics2Properties) {
this.defaultMetricsProperties = defaultMetricsProperties;
this.metricProperties = metricProperties;
this.exemplarProperties = exemplarProperties;
Expand All @@ -120,6 +123,7 @@ public static Builder builder() {
this.exporterHttpServerProperties = httpServerConfig;
this.exporterPushgatewayProperties = pushgatewayProperties;
this.exporterOpenTelemetryProperties = otelConfig;
this.openMetrics2Properties = openMetrics2Properties;
}

/**
Expand Down Expand Up @@ -167,6 +171,10 @@ public ExporterOpenTelemetryProperties getExporterOpenTelemetryProperties() {
return exporterOpenTelemetryProperties;
}

public OpenMetrics2Properties getOpenMetrics2Properties() {
return openMetrics2Properties;
}

public static class Builder {
private MetricsProperties defaultMetricsProperties = MetricsProperties.builder().build();
private final MetricPropertiesMap metricProperties = new MetricPropertiesMap();
Expand All @@ -180,6 +188,8 @@ public static class Builder {
ExporterPushgatewayProperties.builder().build();
private ExporterOpenTelemetryProperties otelConfig =
ExporterOpenTelemetryProperties.builder().build();
private OpenMetrics2Properties openMetrics2Properties =
OpenMetrics2Properties.builder().build();

private Builder() {}

Expand Down Expand Up @@ -231,6 +241,18 @@ public Builder exporterOpenTelemetryProperties(
return this;
}

public Builder enableOpenMetrics2(Consumer<OpenMetrics2Properties.Builder> configurator) {
OpenMetrics2Properties.Builder openMetrics2Builder = OpenMetrics2Properties.builder();
configurator.accept(openMetrics2Builder);
this.openMetrics2Properties = openMetrics2Builder.build();
return this;
}

public Builder openMetrics2Properties(OpenMetrics2Properties openMetrics2Properties) {
this.openMetrics2Properties = openMetrics2Properties;
return this;
}

public PrometheusProperties build() {
return new PrometheusProperties(
defaultMetricsProperties,
Expand All @@ -240,7 +262,8 @@ public PrometheusProperties build() {
exporterFilterProperties,
exporterHttpServerProperties,
pushgatewayProperties,
otelConfig);
otelConfig,
openMetrics2Properties);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static PrometheusProperties load(Map<Object, Object> externalProperties)
ExporterPushgatewayProperties.load(propertySource);
ExporterOpenTelemetryProperties exporterOpenTelemetryProperties =
ExporterOpenTelemetryProperties.load(propertySource);
OpenMetrics2Properties openMetrics2Properties = OpenMetrics2Properties.load(propertySource);
validateAllPropertiesProcessed(propertySource);
return new PrometheusProperties(
defaultMetricsProperties,
Expand All @@ -49,7 +50,8 @@ public static PrometheusProperties load(Map<Object, Object> externalProperties)
exporterFilterProperties,
exporterHttpServerProperties,
exporterPushgatewayProperties,
exporterOpenTelemetryProperties);
exporterOpenTelemetryProperties,
openMetrics2Properties);
}

// This will remove entries from propertySource when they are processed.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package io.prometheus.metrics.config;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;

class OpenMetrics2PropertiesTest {

@Test
void load() {
OpenMetrics2Properties properties =
load(
new HashMap<>(
Map.of(
"io.prometheus.open_metrics2.content_negotiation",
"true",
"io.prometheus.open_metrics2.disable_suffix_appending",
"true",
"io.prometheus.open_metrics2.composite_values",
"true",
"io.prometheus.open_metrics2.exemplar_compliance",
"true",
"io.prometheus.open_metrics2.native_histograms",
"true")));
assertThat(properties.getContentNegotiation()).isTrue();
assertThat(properties.getDisableSuffixAppending()).isTrue();
assertThat(properties.getCompositeValues()).isTrue();
assertThat(properties.getExemplarCompliance()).isTrue();
assertThat(properties.getNativeHistograms()).isTrue();
}

@Test
void loadInvalidValue() {
assertThatExceptionOfType(PrometheusPropertiesException.class)
.isThrownBy(
() ->
load(
new HashMap<>(
Map.of("io.prometheus.open_metrics2.content_negotiation", "invalid"))))
.withMessage(
"io.prometheus.open_metrics2.content_negotiation: Expecting 'true' or 'false'. Found:"
+ " invalid");
assertThatExceptionOfType(PrometheusPropertiesException.class)
.isThrownBy(
() ->
load(
new HashMap<>(
Map.of("io.prometheus.open_metrics2.disable_suffix_appending", "invalid"))))
.withMessage(
"io.prometheus.open_metrics2.disable_suffix_appending: Expecting 'true' or 'false'."
+ " Found: invalid");
assertThatExceptionOfType(PrometheusPropertiesException.class)
.isThrownBy(
() ->
load(
new HashMap<>(
Map.of("io.prometheus.open_metrics2.composite_values", "invalid"))))
.withMessage(
"io.prometheus.open_metrics2.composite_values: Expecting 'true' or 'false'. Found:"
+ " invalid");
assertThatExceptionOfType(PrometheusPropertiesException.class)
.isThrownBy(
() ->
load(
new HashMap<>(
Map.of("io.prometheus.open_metrics2.exemplar_compliance", "invalid"))))
.withMessage(
"io.prometheus.open_metrics2.exemplar_compliance: Expecting 'true' or 'false'. Found:"
+ " invalid");
assertThatExceptionOfType(PrometheusPropertiesException.class)
.isThrownBy(
() ->
load(
new HashMap<>(
Map.of("io.prometheus.open_metrics2.native_histograms", "invalid"))))
.withMessage(
"io.prometheus.open_metrics2.native_histograms: Expecting 'true' or 'false'. Found:"
+ " invalid");
}

private static OpenMetrics2Properties load(Map<String, String> map) {
Map<Object, Object> regularProperties = new HashMap<>(map);
PropertySource propertySource = new PropertySource(regularProperties);
return OpenMetrics2Properties.load(propertySource);
}

@Test
void builder() {
OpenMetrics2Properties properties =
OpenMetrics2Properties.builder()
.contentNegotiation(true)
.disableSuffixAppending(true)
.compositeValues(false)
.exemplarCompliance(true)
.nativeHistograms(false)
.build();
assertThat(properties.getContentNegotiation()).isTrue();
assertThat(properties.getDisableSuffixAppending()).isTrue();
assertThat(properties.getCompositeValues()).isFalse();
assertThat(properties.getExemplarCompliance()).isTrue();
assertThat(properties.getNativeHistograms()).isFalse();
}

@Test
void builderEnableAll() {
OpenMetrics2Properties properties = OpenMetrics2Properties.builder().enableAll().build();
assertThat(properties.getContentNegotiation()).isTrue();
assertThat(properties.getDisableSuffixAppending()).isTrue();
assertThat(properties.getCompositeValues()).isTrue();
assertThat(properties.getExemplarCompliance()).isTrue();
assertThat(properties.getNativeHistograms()).isTrue();
}

@Test
void defaultValues() {
OpenMetrics2Properties properties = OpenMetrics2Properties.builder().build();
assertThat(properties.getContentNegotiation()).isFalse();
assertThat(properties.getDisableSuffixAppending()).isFalse();
assertThat(properties.getCompositeValues()).isFalse();
assertThat(properties.getExemplarCompliance()).isFalse();
assertThat(properties.getNativeHistograms()).isFalse();
}

@Test
void partialConfiguration() {
OpenMetrics2Properties properties =
OpenMetrics2Properties.builder().contentNegotiation(true).compositeValues(true).build();
assertThat(properties.getContentNegotiation()).isTrue();
assertThat(properties.getDisableSuffixAppending()).isFalse();
assertThat(properties.getCompositeValues()).isTrue();
assertThat(properties.getExemplarCompliance()).isFalse();
assertThat(properties.getNativeHistograms()).isFalse();
}
}
Loading