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
Expand Up @@ -46,6 +46,7 @@ public class ExporterOpenTelemetryProperties {
private static final String SERVICE_VERSION = "service_version";
private static final String RESOURCE_ATTRIBUTES =
"resource_attributes"; // otel.resource.attributes
private static final String PRESERVE_NAMES = "preserve_names";
private static final String PREFIX = "io.prometheus.exporter.opentelemetry";

@Nullable private final String endpoint;
Expand All @@ -58,6 +59,7 @@ public class ExporterOpenTelemetryProperties {
@Nullable private final String serviceInstanceId;
@Nullable private final String serviceVersion;
private final Map<String, String> resourceAttributes;
@Nullable private final Boolean preserveNames;

private ExporterOpenTelemetryProperties(
@Nullable String protocol,
Expand All @@ -69,7 +71,8 @@ private ExporterOpenTelemetryProperties(
@Nullable String serviceNamespace,
@Nullable String serviceInstanceId,
@Nullable String serviceVersion,
Map<String, String> resourceAttributes) {
Map<String, String> resourceAttributes,
@Nullable Boolean preserveNames) {
this.protocol = protocol;
this.endpoint = endpoint;
this.headers = headers;
Expand All @@ -80,6 +83,7 @@ private ExporterOpenTelemetryProperties(
this.serviceInstanceId = serviceInstanceId;
this.serviceVersion = serviceVersion;
this.resourceAttributes = resourceAttributes;
this.preserveNames = preserveNames;
}

@Nullable
Expand Down Expand Up @@ -130,6 +134,16 @@ public Map<String, String> getResourceAttributes() {
return resourceAttributes;
}

/**
* When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}).
* When {@code false} (default), standard OTel name normalization is applied (stripping unit
* suffix).
*/
@Nullable
public Boolean getPreserveNames() {
return preserveNames;
}

/**
* 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.
Expand All @@ -147,6 +161,7 @@ static ExporterOpenTelemetryProperties load(PropertySource propertySource)
String serviceVersion = Util.loadString(PREFIX, SERVICE_VERSION, propertySource);
Map<String, String> resourceAttributes =
Util.loadMap(PREFIX, RESOURCE_ATTRIBUTES, propertySource);
Boolean preserveNames = Util.loadBoolean(PREFIX, PRESERVE_NAMES, propertySource);
return new ExporterOpenTelemetryProperties(
protocol,
endpoint,
Expand All @@ -157,7 +172,8 @@ static ExporterOpenTelemetryProperties load(PropertySource propertySource)
serviceNamespace,
serviceInstanceId,
serviceVersion,
resourceAttributes);
resourceAttributes,
preserveNames);
}

public static Builder builder() {
Expand All @@ -176,6 +192,7 @@ public static class Builder {
@Nullable private String serviceInstanceId;
@Nullable private String serviceVersion;
private final Map<String, String> resourceAttributes = new HashMap<>();
@Nullable private Boolean preserveNames;

private Builder() {}

Expand Down Expand Up @@ -318,6 +335,15 @@ public Builder resourceAttribute(String name, String value) {
return this;
}

/**
* When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}).
* When {@code false} (default), standard OTel name normalization is applied.
*/
public Builder preserveNames(boolean preserveNames) {
this.preserveNames = preserveNames;
return this;
}

public ExporterOpenTelemetryProperties build() {
return new ExporterOpenTelemetryProperties(
protocol,
Expand All @@ -329,7 +355,8 @@ public ExporterOpenTelemetryProperties build() {
serviceNamespace,
serviceInstanceId,
serviceVersion,
resourceAttributes);
resourceAttributes,
preserveNames);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public static class Builder {
@Nullable String serviceInstanceId;
@Nullable String serviceVersion;
final Map<String, String> resourceAttributes = new HashMap<>();
@Nullable Boolean preserveNames;

private Builder(PrometheusProperties config) {
this.config = config;
Expand Down Expand Up @@ -194,6 +195,15 @@ public Builder resourceAttribute(String name, String value) {
return this;
}

/**
* When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}).
* When {@code false} (default), standard OTel name normalization is applied.
*/
public Builder preserveNames(boolean preserveNames) {
this.preserveNames = preserveNames;
return this;
}

public OpenTelemetryExporter buildAndStart() {
if (registry == null) {
registry = PrometheusRegistry.defaultRegistry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ static MetricReader createReader(
instrumentationScopeInfo);

MetricReader reader = requireNonNull(readerRef.get());
boolean preserveNames = resolvePreserveNames(builder, config);
reader.register(
new PrometheusMetricProducer(registry, instrumentationScopeInfo, getResourceField(sdk)));
new PrometheusMetricProducer(
registry, instrumentationScopeInfo, getResourceField(sdk), preserveNames));
return reader;
}

Expand Down Expand Up @@ -107,6 +109,15 @@ private static Attributes otelResourceAttributes(
return builder.build();
}

static boolean resolvePreserveNames(
OpenTelemetryExporter.Builder builder, PrometheusProperties config) {
if (builder.preserveNames != null) {
return builder.preserveNames;
}
Boolean fromConfig = config.getExporterOpenTelemetryProperties().getPreserveNames();
return fromConfig != null && fromConfig;
}

static Resource getResourceField(AutoConfiguredOpenTelemetrySdk sdk) {
try {
Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getResource");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ class PrometheusMetricProducer implements CollectionRegistration {
private final PrometheusRegistry registry;
private final Resource resource;
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final boolean preserveNames;

public PrometheusMetricProducer(
PrometheusRegistry registry,
InstrumentationScopeInfo instrumentationScopeInfo,
Resource resource) {
Resource resource,
boolean preserveNames) {
this.registry = registry;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.resource = resource;
this.preserveNames = preserveNames;
}

@Override
Expand All @@ -57,7 +60,8 @@ public Collection<MetricData> collectAllMetrics() {
new MetricDataFactory(
resourceWithTargetInfo,
scopeFromInfo != null ? scopeFromInfo : instrumentationScopeInfo,
System.currentTimeMillis());
System.currentTimeMillis(),
preserveNames);
for (MetricSnapshot snapshot : snapshots) {
if (snapshot instanceof CounterSnapshot) {
addUnlessNull(result, factory.create((CounterSnapshot) snapshot));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ public class MetricDataFactory {
private final Resource resource;
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final long currentTimeMillis;
private final boolean preserveNames;

public MetricDataFactory(
Resource resource,
InstrumentationScopeInfo instrumentationScopeInfo,
long currentTimeMillis) {
long currentTimeMillis,
boolean preserveNames) {
this.resource = resource;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.currentTimeMillis = currentTimeMillis;
this.preserveNames = preserveNames;
}

@Nullable
Expand All @@ -36,7 +39,8 @@ public MetricData create(CounterSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusCounter(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}

@Nullable
Expand All @@ -48,7 +52,8 @@ public MetricData create(GaugeSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusGauge(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}

@Nullable
Expand All @@ -60,13 +65,15 @@ public MetricData create(HistogramSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusNativeHistogram(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
} else if (firstDataPoint.hasClassicHistogramData()) {
return new PrometheusMetricData<>(
snapshot.getMetadata(),
new PrometheusClassicHistogram(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}
}
return null;
Expand All @@ -81,7 +88,8 @@ public MetricData create(SummarySnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusSummary(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}

@Nullable
Expand All @@ -93,7 +101,8 @@ public MetricData create(InfoSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusInfo(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}

@Nullable
Expand All @@ -105,7 +114,8 @@ public MetricData create(StateSetSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusStateSet(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}

@Nullable
Expand All @@ -117,6 +127,7 @@ public MetricData create(UnknownSnapshot snapshot) {
snapshot.getMetadata(),
new PrometheusUnknown(snapshot, currentTimeMillis),
instrumentationScopeInfo,
resource);
resource,
preserveNames);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ class PrometheusMetricData<T extends PrometheusData<?>> implements MetricData {
MetricMetadata metricMetadata,
T data,
InstrumentationScopeInfo instrumentationScopeInfo,
Resource resource) {
Resource resource,
boolean preserveNames) {
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.resource = resource;
this.name = getNameWithoutUnit(metricMetadata);
this.name =
preserveNames ? metricMetadata.getOriginalName() : getNameWithoutUnit(metricMetadata);
this.description = metricMetadata.getHelp();
this.unit = convertUnit(metricMetadata.getUnit());
this.data = data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.assertj.MetricAssert;
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
import io.prometheus.metrics.core.metrics.Counter;
import io.prometheus.metrics.core.metrics.Gauge;
Expand All @@ -23,6 +24,7 @@
import io.prometheus.metrics.model.snapshots.Unit;
import io.prometheus.metrics.model.snapshots.UnknownSnapshot;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -47,7 +49,8 @@ void setUp() throws IllegalAccessException, NoSuchFieldException {
new PrometheusMetricProducer(
registry,
InstrumentationScopeInfo.create("test"),
Resource.create(Attributes.builder().put("staticRes", "value").build()));
Resource.create(Attributes.builder().put("staticRes", "value").build()),
false);

reader.register(prometheusMetricProducer);
}
Expand Down Expand Up @@ -324,6 +327,60 @@ void metricsWithoutDataPointsAreNotExported() {
assertThat(metrics).isEmpty();
}

@Test
void preserveNamesWithUnit() {
InMemoryMetricReader reader = InMemoryMetricReader.create();
PrometheusRegistry preserveRegistry = new PrometheusRegistry();
reader.register(
new PrometheusMetricProducer(
preserveRegistry,
InstrumentationScopeInfo.create("test"),
Resource.create(Attributes.builder().put("staticRes", "value").build()),
true));

Counter.builder().name("req").unit(Unit.BYTES).register(preserveRegistry).inc();

List<MetricData> metrics = new ArrayList<>(reader.collectAllMetrics());
assertThat(metrics).hasSize(1);
OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("req").hasUnit("By");
}

@Test
void preserveNamesWithUnitAlreadyInName() {
InMemoryMetricReader reader = InMemoryMetricReader.create();
PrometheusRegistry preserveRegistry = new PrometheusRegistry();
reader.register(
new PrometheusMetricProducer(
preserveRegistry,
InstrumentationScopeInfo.create("test"),
Resource.create(Attributes.builder().put("staticRes", "value").build()),
true));

Counter.builder().name("req_bytes").unit(Unit.BYTES).register(preserveRegistry).inc();

List<MetricData> metrics = new ArrayList<>(reader.collectAllMetrics());
assertThat(metrics).hasSize(1);
OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("req_bytes").hasUnit("By");
}

@Test
void preserveNamesWithoutUnit() {
InMemoryMetricReader reader = InMemoryMetricReader.create();
PrometheusRegistry preserveRegistry = new PrometheusRegistry();
reader.register(
new PrometheusMetricProducer(
preserveRegistry,
InstrumentationScopeInfo.create("test"),
Resource.create(Attributes.builder().put("staticRes", "value").build()),
true));

Counter.builder().name("events_total").register(preserveRegistry).inc();

List<MetricData> metrics = new ArrayList<>(reader.collectAllMetrics());
assertThat(metrics).hasSize(1);
OpenTelemetryAssertions.assertThat(metrics.get(0)).hasName("events_total");
}

private MetricAssert metricAssert() {
List<MetricData> metrics = testing.getMetrics();
assertThat(metrics).hasSize(1);
Expand Down
Loading
Loading