Extend prometheus declarative config support to include without_scope_info, with_resource_constant_labels (#6840)
This commit is contained in:
parent
915c64a365
commit
cecfb835be
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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]}"));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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"
|
||||
+ "}");
|
||||
}
|
||||
|
|
|
@ -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\"}}"
|
||||
+ "]}");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue