Extend prometheus declarative config support to include without_scope_info, with_resource_constant_labels (#6840)

This commit is contained in:
jack-berg 2025-07-10 17:10:00 -05:00 committed by GitHub
parent 915c64a365
commit cecfb835be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 343 additions and 126 deletions

View File

@ -26,6 +26,7 @@ import io.prometheus.metrics.model.registry.PrometheusRegistry;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
@ -39,13 +40,17 @@ import javax.annotation.Nullable;
*/
public final class PrometheusHttpServer implements MetricReader {
private final String host;
private final int port;
private final boolean otelScopeEnabled;
@Nullable private final Predicate<String> allowedResourceAttributesFilter;
private final MemoryMode memoryMode;
private final DefaultAggregationSelector defaultAggregationSelector;
private final PrometheusHttpServerBuilder builder;
private final HTTPServer httpServer;
private final PrometheusMetricReader prometheusMetricReader;
private final PrometheusRegistry prometheusRegistry;
private final String host;
private final MemoryMode memoryMode;
private final DefaultAggregationSelector defaultAggregationSelector;
/**
* Returns a new {@link PrometheusHttpServer} which can be registered to an {@link
@ -73,11 +78,15 @@ public final class PrometheusHttpServer implements MetricReader {
@Nullable HttpHandler defaultHandler,
DefaultAggregationSelector defaultAggregationSelector,
@Nullable Authenticator authenticator) {
this.host = host;
this.port = port;
this.otelScopeEnabled = otelScopeEnabled;
this.allowedResourceAttributesFilter = allowedResourceAttributesFilter;
this.memoryMode = memoryMode;
this.defaultAggregationSelector = defaultAggregationSelector;
this.builder = builder;
this.prometheusMetricReader =
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
this.host = host;
this.memoryMode = memoryMode;
this.prometheusRegistry = prometheusRegistry;
prometheusRegistry.register(prometheusMetricReader);
// When memory mode is REUSABLE_DATA, concurrent reads lead to data corruption. To prevent this,
@ -106,7 +115,6 @@ public final class PrometheusHttpServer implements MetricReader {
} catch (IOException e) {
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
}
this.defaultAggregationSelector = defaultAggregationSelector;
}
@Override
@ -160,7 +168,16 @@ public final class PrometheusHttpServer implements MetricReader {
@Override
public String toString() {
return "PrometheusHttpServer{address=" + getAddress() + "}";
StringJoiner joiner = new StringJoiner(",", "PrometheusHttpServer{", "}");
joiner.add("host=" + host);
joiner.add("port=" + port);
joiner.add("otelScopeEnabled=" + otelScopeEnabled);
joiner.add("allowedResourceAttributesFilter=" + allowedResourceAttributesFilter);
joiner.add("memoryMode=" + memoryMode);
joiner.add(
"defaultAggregationSelector="
+ DefaultAggregationSelector.asString(defaultAggregationSelector));
return joiner.toString();
}
/**

View File

@ -9,7 +9,9 @@ import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import java.util.List;
/**
* Declarative configuration SPI implementation for {@link PrometheusHttpServer}.
@ -37,11 +39,28 @@ public class PrometheusComponentProvider implements ComponentProvider<MetricRead
if (port != null) {
prometheusBuilder.setPort(port);
}
String host = config.getString("host");
if (host != null) {
prometheusBuilder.setHost(host);
}
Boolean withoutScopeInfo = config.getBoolean("without_scope_info");
if (withoutScopeInfo != null) {
prometheusBuilder.setOtelScopeEnabled(!withoutScopeInfo);
}
DeclarativeConfigProperties withResourceConstantLabels =
config.getStructured("with_resource_constant_labels");
if (withResourceConstantLabels != null) {
List<String> included = withResourceConstantLabels.getScalarList("included", String.class);
List<String> excluded = withResourceConstantLabels.getScalarList("excluded", String.class);
if (included != null || excluded != null) {
prometheusBuilder.setAllowedResourceAttributesFilter(
IncludeExcludePredicate.createPatternMatching(included, excluded));
}
}
return prometheusBuilder.build();
}
}

View File

@ -409,7 +409,15 @@ class PrometheusHttpServerTest {
@Test
void stringRepresentation() {
assertThat(prometheusServer.toString())
.isEqualTo("PrometheusHttpServer{address=" + prometheusServer.getAddress() + "}");
.isEqualTo(
"PrometheusHttpServer{"
+ "host=localhost,"
+ "port=0,"
+ "otelScopeEnabled=true,"
+ "allowedResourceAttributesFilter=null,"
+ "memoryMode=REUSABLE_DATA,"
+ "defaultAggregationSelector=DefaultAggregationSelector{COUNTER=default, UP_DOWN_COUNTER=default, HISTOGRAM=default, OBSERVABLE_COUNTER=default, OBSERVABLE_UP_DOWN_COUNTER=default, OBSERVABLE_GAUGE=default, GAUGE=default}"
+ "}");
}
@Test

View File

@ -5,8 +5,6 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static io.opentelemetry.sdk.internal.GlobUtil.createGlobPatternPredicate;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.ResourceConfiguration;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
@ -15,6 +13,7 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Experi
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalResourceDetectorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import java.util.Collections;
@ -90,35 +89,6 @@ final class ResourceFactory implements Factory<ResourceModel, Resource> {
if (included == null && excluded == null) {
return ResourceFactory::matchAll;
}
if (included == null) {
return excludedPredicate(excluded);
}
if (excluded == null) {
return includedPredicate(included);
}
return includedPredicate(included).and(excludedPredicate(excluded));
}
/**
* Returns a predicate which matches strings matching any of the {@code included} glob patterns.
*/
private static Predicate<String> includedPredicate(List<String> included) {
Predicate<String> result = attributeKey -> false;
for (String include : included) {
result = result.or(createGlobPatternPredicate(include));
}
return result;
}
/**
* Returns a predicate which matches strings NOT matching any of the {@code excluded} glob
* patterns.
*/
private static Predicate<String> excludedPredicate(List<String> excluded) {
Predicate<String> result = attributeKey -> true;
for (String exclude : excluded) {
result = result.and(createGlobPatternPredicate(exclude).negate());
}
return result;
return IncludeExcludePredicate.createPatternMatching(included, excluded);
}
}

View File

@ -7,12 +7,10 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ViewStreamModel;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.metrics.ViewBuilder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
final class ViewFactory implements Factory<ViewStreamModel, View> {
@ -35,7 +33,11 @@ final class ViewFactory implements Factory<ViewStreamModel, View> {
}
IncludeExcludeModel attributeKeys = model.getAttributeKeys();
if (attributeKeys != null) {
addAttributeKeyFilter(builder, attributeKeys.getIncluded(), attributeKeys.getExcluded());
List<String> included = attributeKeys.getIncluded();
List<String> excluded = attributeKeys.getExcluded();
if (included != null || excluded != null) {
builder.setAttributeFilter(IncludeExcludePredicate.createExactMatching(included, excluded));
}
}
if (model.getAggregation() != null) {
builder.setAggregation(
@ -46,25 +48,4 @@ final class ViewFactory implements Factory<ViewStreamModel, View> {
}
return builder.build();
}
private static void addAttributeKeyFilter(
ViewBuilder builder, @Nullable List<String> included, @Nullable List<String> excluded) {
if (included == null && excluded == null) {
return;
}
if (included == null) {
Set<String> excludedKeys = new HashSet<>(excluded);
// TODO: set predicate with useful toString implementation
builder.setAttributeFilter(attributeKey -> !excludedKeys.contains(attributeKey));
return;
}
if (excluded == null) {
Set<String> includedKeys = new HashSet<>(included);
builder.setAttributeFilter(includedKeys);
return;
}
Set<String> includedKeys = new HashSet<>(included);
excluded.forEach(includedKeys::remove);
builder.setAttributeFilter(includedKeys);
}
}

View File

@ -6,6 +6,7 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@ -20,12 +21,14 @@ import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.CardinalityLimitsModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalPrometheusMetricExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpMetricExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PeriodicMetricReaderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PullMetricExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PullMetricReaderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import java.io.Closeable;
@ -92,7 +95,7 @@ class MetricReaderFactoryTest {
@Test
void create_PeriodicConfigured() {
List<Closeable> closeables = new ArrayList<>();
io.opentelemetry.sdk.metrics.export.MetricReader expectedReader =
MetricReader expectedReader =
io.opentelemetry.sdk.metrics.export.PeriodicMetricReader.builder(
OtlpHttpMetricExporter.getDefault())
.setInterval(Duration.ofMillis(1))
@ -143,8 +146,7 @@ class MetricReaderFactoryTest {
new ExperimentalPrometheusMetricExporterModel()
.withPort(port)))),
context);
io.opentelemetry.sdk.metrics.export.MetricReader reader =
readerAndCardinalityLimits.getMetricReader();
MetricReader reader = readerAndCardinalityLimits.getMetricReader();
cleanup.addCloseable(reader);
cleanup.addCloseables(closeables);
@ -160,7 +162,14 @@ class MetricReaderFactoryTest {
List<Closeable> closeables = new ArrayList<>();
PrometheusHttpServer expectedReader =
PrometheusHttpServer.builder().setHost("localhost").setPort(port).build();
PrometheusHttpServer.builder()
.setHost("localhost")
.setPort(port)
.setOtelScopeEnabled(false)
.setAllowedResourceAttributesFilter(
IncludeExcludePredicate.createPatternMatching(
singletonList("foo"), singletonList("bar")))
.build();
// Close the reader to avoid port conflict with the new instance created by MetricReaderFactory
expectedReader.close();
@ -170,16 +179,22 @@ class MetricReaderFactoryTest {
new MetricReaderModel()
.withPull(
new PullMetricReaderModel()
.withCardinalityLimits(new CardinalityLimitsModel().withDefault(100))
.withExporter(
new PullMetricExporterModel()
.withPrometheusDevelopment(
new ExperimentalPrometheusMetricExporterModel()
.withHost("localhost")
.withPort(port)))
.withCardinalityLimits(new CardinalityLimitsModel().withDefault(100))),
.withPort(port)
.withWithResourceConstantLabels(
new IncludeExcludeModel()
.withIncluded(singletonList("foo"))
.withExcluded(singletonList("bar")))
.withWithoutScopeInfo(true)
.withWithoutTypeSuffix(true)
.withWithoutUnits(true)))),
context);
io.opentelemetry.sdk.metrics.export.MetricReader reader =
readerAndCardinalityLimits.getMetricReader();
MetricReader reader = readerAndCardinalityLimits.getMetricReader();
cleanup.addCloseable(reader);
cleanup.addCloseables(closeables);

View File

@ -12,9 +12,10 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Aggreg
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExplicitBucketHistogramAggregationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ViewStreamModel;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.metrics.View;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Collections;
import org.junit.jupiter.api.Test;
class ViewFactoryTest {
@ -38,7 +39,9 @@ class ViewFactoryTest {
View.builder()
.setName("name")
.setDescription("description")
.setAttributeFilter(new HashSet<>(Arrays.asList("foo", "bar")))
.setAttributeFilter(
IncludeExcludePredicate.createExactMatching(
Arrays.asList("foo", "bar"), Collections.singletonList("baz")))
.setAggregation(
io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram(
Arrays.asList(1.0, 2.0)))
@ -51,7 +54,9 @@ class ViewFactoryTest {
.withName("name")
.withDescription("description")
.withAttributeKeys(
new IncludeExcludeModel().withIncluded(Arrays.asList("foo", "bar")))
new IncludeExcludeModel()
.withIncluded(Arrays.asList("foo", "bar"))
.withExcluded(Collections.singletonList("baz")))
.withAggregation(
new AggregationModel()
.withExplicitBucketHistogram(

View File

@ -0,0 +1,122 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.internal;
import static io.opentelemetry.sdk.internal.GlobUtil.createGlobPatternPredicate;
import static java.util.stream.Collectors.joining;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Predicate;
import javax.annotation.Nullable;
/**
* A predicate that evaluates if a string matches a configurable set of {@code included} and {@code
* excluded} string.
*
* <p>Supports optional glob pattern matching. See {@link GlobUtil}.
*
* <p>String equality is evaluated using {@link String#equalsIgnoreCase(String)}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class IncludeExcludePredicate implements Predicate<String> {
private final boolean globMatchingEnabled;
@Nullable private final Set<String> included;
@Nullable private final Set<String> excluded;
private final Predicate<String> predicate;
private IncludeExcludePredicate(
@Nullable Collection<String> included,
@Nullable Collection<String> excluded,
boolean globMatchingEnabled) {
this.globMatchingEnabled = globMatchingEnabled;
this.included = included == null ? null : new LinkedHashSet<>(included);
this.excluded = excluded == null ? null : new LinkedHashSet<>(excluded);
if (this.included != null && this.excluded != null) {
this.predicate =
includedPredicate(this.included, globMatchingEnabled)
.and(excludedPredicate(this.excluded, globMatchingEnabled));
} else if (this.included == null && this.excluded != null) {
this.predicate = excludedPredicate(this.excluded, globMatchingEnabled);
} else if (this.excluded == null && this.included != null) {
this.predicate = includedPredicate(this.included, globMatchingEnabled);
} else {
throw new IllegalArgumentException(
"At least one of includedPatterns or excludedPatterns must not be null");
}
}
/**
* Create a (case-insensitive) exact matching include exclude predicate.
*
* @throws IllegalArgumentException if {@code included} AND {@code excluded} are null.
*/
public static Predicate<String> createExactMatching(
@Nullable Collection<String> included, @Nullable Collection<String> excluded) {
return new IncludeExcludePredicate(included, excluded, /* globMatchingEnabled= */ false);
}
/**
* Create a pattern matching include exclude predicate.
*
* <p>See {@link GlobUtil} for pattern matching details.
*
* @throws IllegalArgumentException if {@code included} AND {@code excluded} are null.
*/
public static Predicate<String> createPatternMatching(
@Nullable Collection<String> included, @Nullable Collection<String> excluded) {
return new IncludeExcludePredicate(included, excluded, /* globMatchingEnabled= */ true);
}
@Override
public boolean test(String s) {
return predicate.test(s);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ", "IncludeExcludePredicate{", "}");
joiner.add("globMatchingEnabled=" + globMatchingEnabled);
if (included != null) {
joiner.add("included=" + included.stream().collect(joining(", ", "[", "]")));
}
if (excluded != null) {
joiner.add("excluded=" + excluded.stream().collect(joining(", ", "[", "]")));
}
return joiner.toString();
}
private static Predicate<String> includedPredicate(
Set<String> included, boolean globMatchingEnabled) {
Predicate<String> result = attributeKey -> false;
for (String include : included) {
if (globMatchingEnabled) {
result = result.or(createGlobPatternPredicate(include));
} else {
result = result.or(include::equalsIgnoreCase);
}
}
return result;
}
private static Predicate<String> excludedPredicate(
Set<String> excluded, boolean globMatchingEnabled) {
Predicate<String> result = attributeKey -> true;
for (String exclude : excluded) {
if (globMatchingEnabled) {
result = result.and(createGlobPatternPredicate(exclude).negate());
} else {
result = result.and(s -> !exclude.equalsIgnoreCase(s));
}
}
return result;
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.internal;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class IncludeExcludePredicateTest {
private static final Predicate<String> EXACT_INCLUDE =
IncludeExcludePredicate.createExactMatching(singletonList("foo"), null);
private static final Predicate<String> EXACT_EXCLUDE =
IncludeExcludePredicate.createExactMatching(null, singletonList("bar"));
private static final Predicate<String> EXACT_INCLUDE_AND_EXCLUDE =
IncludeExcludePredicate.createExactMatching(singletonList("foo"), singletonList("bar"));
private static final Predicate<String> EXACT_MULTI =
IncludeExcludePredicate.createExactMatching(asList("foo", "fooo"), asList("bar", "barr"));
private static final Predicate<String> PATTERN_INCLUDE =
IncludeExcludePredicate.createPatternMatching(singletonList("f?o"), null);
private static final Predicate<String> PATTERN_EXCLUDE =
IncludeExcludePredicate.createPatternMatching(null, singletonList("b?r"));
private static final Predicate<String> PATTERN_INCLUDE_AND_EXCLUDE =
IncludeExcludePredicate.createPatternMatching(singletonList("f?o"), singletonList("b?r"));
private static final Predicate<String> PATTERN_MULTI =
IncludeExcludePredicate.createPatternMatching(asList("f?o", "f?oo"), asList("b?r", "b?rr"));
@ParameterizedTest
@MethodSource("testArgs")
void test(Predicate<String> predicate, String testCase, boolean expectedResult) {
assertThat(predicate.test(testCase)).isEqualTo(expectedResult);
}
private static Stream<Arguments> testArgs() {
return Stream.of(
// exact matching
// include only
Arguments.of(EXACT_INCLUDE, "foo", true),
Arguments.of(EXACT_INCLUDE, "bar", false),
Arguments.of(EXACT_INCLUDE, "baz", false),
// exclude only
Arguments.of(EXACT_EXCLUDE, "foo", true),
Arguments.of(EXACT_EXCLUDE, "bar", false),
Arguments.of(EXACT_EXCLUDE, "baz", true),
// include and exclude
Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "foo", true),
Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "bar", false),
Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "baz", false),
// multi
Arguments.of(EXACT_MULTI, "foo", true),
Arguments.of(EXACT_MULTI, "fooo", true),
Arguments.of(EXACT_MULTI, "bar", false),
Arguments.of(EXACT_MULTI, "barr", false),
Arguments.of(EXACT_MULTI, "baz", false),
// pattern matching
// include only
Arguments.of(PATTERN_INCLUDE, "foo", true),
Arguments.of(PATTERN_INCLUDE, "bar", false),
Arguments.of(PATTERN_INCLUDE, "baz", false),
// exclude only
Arguments.of(PATTERN_EXCLUDE, "foo", true),
Arguments.of(PATTERN_EXCLUDE, "bar", false),
Arguments.of(PATTERN_EXCLUDE, "baz", true),
// include and exclude
Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "foo", true),
Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "bar", false),
Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "baz", false),
// multi
Arguments.of(PATTERN_MULTI, "foo", true),
Arguments.of(PATTERN_MULTI, "fooo", true),
Arguments.of(PATTERN_MULTI, "bar", false),
Arguments.of(PATTERN_MULTI, "barr", false),
Arguments.of(PATTERN_MULTI, "baz", false));
}
@ParameterizedTest
@MethodSource("stringRepresentationArgs")
void stringRepresentation(Predicate<String> predicate, String exepectedString) {
assertThat(predicate.toString()).isEqualTo(exepectedString);
}
private static Stream<Arguments> stringRepresentationArgs() {
return Stream.of(
Arguments.of(
EXACT_INCLUDE, "IncludeExcludePredicate{globMatchingEnabled=false, included=[foo]}"),
Arguments.of(
EXACT_EXCLUDE, "IncludeExcludePredicate{globMatchingEnabled=false, excluded=[bar]}"),
Arguments.of(
EXACT_INCLUDE_AND_EXCLUDE,
"IncludeExcludePredicate{globMatchingEnabled=false, included=[foo], excluded=[bar]}"),
Arguments.of(
EXACT_MULTI,
"IncludeExcludePredicate{globMatchingEnabled=false, included=[foo, fooo], excluded=[bar, barr]}"),
Arguments.of(
PATTERN_INCLUDE, "IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o]}"),
Arguments.of(
PATTERN_EXCLUDE, "IncludeExcludePredicate{globMatchingEnabled=true, excluded=[b?r]}"),
Arguments.of(
PATTERN_INCLUDE_AND_EXCLUDE,
"IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o], excluded=[b?r]}"),
Arguments.of(
PATTERN_MULTI,
"IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o, f?oo], excluded=[b?r, b?rr]}"));
}
}

View File

@ -5,9 +5,8 @@
package io.opentelemetry.sdk.metrics;
import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.setIncludes;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorFactory;
import io.opentelemetry.sdk.metrics.internal.state.MetricStorage;
@ -75,7 +74,7 @@ public final class ViewBuilder {
*/
public ViewBuilder setAttributeFilter(Set<String> keysToRetain) {
Objects.requireNonNull(keysToRetain, "keysToRetain");
return setAttributeFilter(setIncludes(keysToRetain));
return setAttributeFilter(IncludeExcludePredicate.createExactMatching(keysToRetain, null));
}
/**

View File

@ -16,7 +16,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.concurrent.Immutable;
@ -98,30 +97,6 @@ public abstract class AttributesProcessor {
return new AppendingAttributesProcessor(attributes);
}
/** Creates a {@link Predicate} which tests if the {@code set} includes the input. */
public static Predicate<String> setIncludes(Set<String> set) {
return new SetIncludesPredicate(set);
}
/** Predicate which tests if the {@code set} includes the input. */
private static class SetIncludesPredicate implements Predicate<String> {
private final Set<String> set;
private SetIncludesPredicate(Set<String> set) {
this.set = set;
}
@Override
public boolean test(String s) {
return set.contains(s);
}
@Override
public String toString() {
return "SetIncludesPredicate{set=" + set + "}";
}
}
/**
* Processor which filters attributes according to a {@link AttributeKey#getKey()} {@link
* Predicate}.

View File

@ -36,7 +36,7 @@ class ViewTest {
+ "name=name, "
+ "description=description, "
+ "aggregation=SumAggregation, "
+ "attributesProcessor=AttributeKeyFilteringProcessor{nameFilter=SetIncludesPredicate{set=[key1, key2]}}, "
+ "attributesProcessor=AttributeKeyFilteringProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[key1, key2]}}, "
+ "cardinalityLimit=10"
+ "}");
}

View File

@ -6,13 +6,13 @@
package io.opentelemetry.sdk.metrics.internal.view;
import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.append;
import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.setIncludes;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.singleton;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import java.util.Collections;
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
import org.junit.jupiter.api.Test;
/** Tests for the {@link AttributesProcessor} DSL-ish library. */
@ -29,26 +29,15 @@ class AttributesProcessorTest {
.containsEntry("test", "keep");
}
@Test
void filterKeyName_SetIncludes() {
AttributesProcessor processor =
AttributesProcessor.filterByKeyName(setIncludes(Collections.singleton("test")));
assertThat(
processor.process(
Attributes.builder().put("remove", "me").put("test", "keep").build(),
Context.root()))
.hasSize(1)
.containsEntry("test", "keep");
}
@Test
void filterKeyName_toString() {
AttributesProcessor processor =
AttributesProcessor.filterByKeyName(setIncludes(Collections.singleton("test")));
AttributesProcessor.filterByKeyName(
IncludeExcludePredicate.createExactMatching(singleton("test"), null));
assertThat(processor.toString())
.isEqualTo("AttributeKeyFilteringProcessor{nameFilter=SetIncludesPredicate{set=[test]}}");
.isEqualTo(
"AttributeKeyFilteringProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[test]}}");
}
@Test
@ -117,12 +106,13 @@ class AttributesProcessorTest {
@Test
void appendBaggage_toString() {
AttributesProcessor processor =
AttributesProcessor.appendBaggageByKeyName(setIncludes(Collections.singleton("keep")));
AttributesProcessor.appendBaggageByKeyName(
IncludeExcludePredicate.createExactMatching(singleton("keep"), null));
assertThat(processor.toString())
.isEqualTo(
"BaggageAppendingAttributesProcessor{"
+ "nameFilter=SetIncludesPredicate{set=[keep]}"
+ "nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[keep]}"
+ "}");
}
@ -143,13 +133,14 @@ class AttributesProcessorTest {
@Test
void joinedAttributes_toString() {
AttributesProcessor processor =
AttributesProcessor.appendBaggageByKeyName(setIncludes(Collections.singleton("keep")))
AttributesProcessor.appendBaggageByKeyName(
IncludeExcludePredicate.createExactMatching(singleton("keep"), null))
.then(append(Attributes.builder().put("key", "value").build()));
assertThat(processor.toString())
.isEqualTo(
"JoinedAttributesProcessor{processors=["
+ "BaggageAppendingAttributesProcessor{nameFilter=SetIncludesPredicate{set=[keep]}}, "
+ "BaggageAppendingAttributesProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[keep]}}, "
+ "AppendingAttributesProcessor{additionalAttributes={key=\"value\"}}"
+ "]}");
}