From 3418b260b93238a87d0a7627942cb294cb75e1f5 Mon Sep 17 00:00:00 2001 From: Karsten Schnitter Date: Tue, 25 Nov 2025 09:48:20 +0100 Subject: [PATCH 1/4] Allow metric filtering by name Implementation of a decorator pattern filtering the metrics data by the configured names. Includes are applied before excludes. This change filters only by explicit name. Further commits introducing wildcards and documentation to follow. Signed-off-by: Karsten Schnitter --- .../CloudLoggingMetricsExporterProvider.java | 22 ++- .../DynatraceMetricsExporterProvider.java | 21 ++- .../ext/exporter/FilteringMetricExporter.java | 111 ++++++++++++ .../exporter/FilteringMetricExporterTest.java | 164 ++++++++++++++++++ 4 files changed, 301 insertions(+), 17 deletions(-) create mode 100644 cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java create mode 100644 cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java index 8e310f84..d7895194 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java @@ -27,6 +27,8 @@ public class CloudLoggingMetricsExporterProvider implements ConfigurableMetricExporterProvider { + private static final String GENERIC_CONFIG_PREFIX = "otel.exporter.cloud-logging."; + private static final String METRICS_CONFIG_PREFIX = "otel.exporter.cloud-logging.metrics."; private static final Logger LOG = Logger.getLogger(CloudLoggingMetricsExporterProvider.class.getName()); private final Function> servicesProvider; @@ -43,17 +45,17 @@ public CloudLoggingMetricsExporterProvider() { } private static String getCompression(ConfigProperties config) { - String compression = config.getString("otel.exporter.cloud-logging.metrics.compression"); - return compression != null ? compression : config.getString("otel.exporter.cloud-logging.compression", "gzip"); + String compression = config.getString(METRICS_CONFIG_PREFIX + "compression"); + return compression != null ? compression : config.getString(GENERIC_CONFIG_PREFIX + "compression", "gzip"); } private static Duration getTimeOut(ConfigProperties config) { - Duration timeout = config.getDuration("otel.exporter.cloud-logging.metrics.timeout"); - return timeout != null ? timeout : config.getDuration("otel.exporter.cloud-logging.timeout"); + Duration timeout = config.getDuration(METRICS_CONFIG_PREFIX + "timeout"); + return timeout != null ? timeout : config.getDuration(GENERIC_CONFIG_PREFIX + "timeout"); } private static AggregationTemporalitySelector getAggregationTemporalitySelector(ConfigProperties config) { - String temporalityStr = config.getString("otel.exporter.cloud-logging.metrics.temporality.preference"); + String temporalityStr = config.getString(METRICS_CONFIG_PREFIX + "temporality.preference"); if (temporalityStr == null) { return AggregationTemporalitySelector.alwaysCumulative(); } @@ -71,8 +73,7 @@ private static AggregationTemporalitySelector getAggregationTemporalitySelector( } private static DefaultAggregationSelector getDefaultAggregationSelector(ConfigProperties config) { - String defaultHistogramAggregation = - config.getString("otel.exporter.cloud-logging.metrics.default.histogram.aggregation"); + String defaultHistogramAggregation = config.getString(METRICS_CONFIG_PREFIX + "default.histogram.aggregation"); if (defaultHistogramAggregation == null) { return DefaultAggregationSelector.getDefault() .with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation()); @@ -101,8 +102,11 @@ public MetricExporter createExporter(ConfigProperties config) { List exporters = servicesProvider.apply(config).map(svc -> createExporter(config, svc)) .filter(exp -> !(exp instanceof NoopMetricExporter)) .collect(Collectors.toList()); - return MultiMetricExporter.composite(exporters, getAggregationTemporalitySelector(config), - getDefaultAggregationSelector(config)); + MetricExporter exporter = MultiMetricExporter.composite(exporters, getAggregationTemporalitySelector(config), + getDefaultAggregationSelector(config)); + exporter = FilteringMetricExporter.wrap(exporter).withConfig(config).withPropertyPrefix(METRICS_CONFIG_PREFIX) + .build(); + return exporter; } private MetricExporter createExporter(ConfigProperties config, CloudFoundryServiceInstance service) { diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java index 81bd3f08..0b43ed88 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java @@ -28,6 +28,10 @@ public class DynatraceMetricsExporterProvider implements ConfigurableMetricExpor public static final String CRED_DYNATRACE_APIURL = "apiurl"; public static final String DT_APIURL_METRICS_SUFFIX = "/v2/otlp/v1/metrics"; + + private static final String GENERIC_CONFIG_PREFIX = "otel.exporter.dynatrace."; + private static final String METRICS_CONFIG_PREFIX = "otel.exporter.dynatrace.metrics."; + private static final Logger LOG = Logger.getLogger(DynatraceMetricsExporterProvider.class.getName()); private static final AggregationTemporalitySelector ALWAYS_DELTA = instrumentType -> AggregationTemporality.DELTA; private final Function serviceProvider; @@ -41,17 +45,17 @@ public DynatraceMetricsExporterProvider(Function predicate; + + private FilteringMetricExporter(MetricExporter delegate, Predicate predicate) { + this.delegate = delegate; + this.predicate = predicate; + } + + @Override + public CompletableResultCode export(Collection collection) { + List filteredMetrics = collection.stream().filter(predicate).collect(Collectors.toList()); + return delegate.export(filteredMetrics); + } + + @Override + public CompletableResultCode flush() { + return delegate.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public void close() { + delegate.close(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return delegate.getAggregationTemporality(instrumentType); + } + + @Override + public Aggregation getDefaultAggregation(InstrumentType instrumentType) { + return delegate.getDefaultAggregation(instrumentType); + } + + @Override + public MemoryMode getMemoryMode() { + return delegate.getMemoryMode(); + } + + public static Builder wrap(MetricExporter delegate) { + return new Builder(delegate); + } + + public static class Builder { + + private final MetricExporter delegate; + private ConfigProperties config; + private String prefix = ""; + + public Builder(MetricExporter delegate) { + this.delegate = delegate; + } + + public Builder withConfig(ConfigProperties config) { + this.config = config; + return this; + } + + public Builder withPropertyPrefix(String prefix) { + this.prefix = prefix.endsWith(".") ? prefix : prefix + "."; + return this; + } + + public MetricExporter build() { + if (config == null) { + return delegate; + } + + List includedNames = config.getList(prefix + INCLUDED_NAMES_KEY); + List excludedNames = config.getList(prefix + EXCLUDED_NAMES_KEY); + if (includedNames.isEmpty() && excludedNames.isEmpty()) { + return delegate; + } + + Predicate predicate = metricData -> true; + if (!includedNames.isEmpty()) { + predicate = predicate.and(metricData -> includedNames.contains(metricData.getName())); + } + if (!excludedNames.isEmpty()) { + predicate = predicate.and(metricData -> !excludedNames.contains(metricData.getName())); + } + return new FilteringMetricExporter(delegate, predicate); + } + } +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java new file mode 100644 index 00000000..84d5026b --- /dev/null +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java @@ -0,0 +1,164 @@ +package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import org.assertj.core.data.MapEntry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.List; + +import static io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties.createFromMap; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class FilteringMetricExporterTest { + + @Mock + private MetricExporter delegate; + + @Mock(strictness = LENIENT) + private MetricData includedMetric; + + @Mock(strictness = LENIENT) + private MetricData excludedMetric; + + @Mock(strictness = LENIENT) + private MetricData anotherMetric; + + @Captor + ArgumentCaptor> exported; + + @BeforeEach + void setUp() { + when(includedMetric.getName()).thenReturn("included"); + when(excludedMetric.getName()).thenReturn("excluded"); + when(anotherMetric.getName()).thenReturn("another"); + } + + @Test + void exportsAllWithoutConfig() { + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric, excludedMetric, anotherMetric); + } + + @Test + void exportsAllWithEmptyConfig() { + DefaultConfigProperties config = createFromMap(emptyMap()); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric, excludedMetric, anotherMetric); + } + + @Test + void exportsOnlyIncluded() { + ConfigProperties config = createConfig(MapEntry.entry("include.names", "included")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric); + } + + @Test + void rejectsEncluded() { + ConfigProperties config = createConfig(MapEntry.entry("exclude.names", "excluded")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric, anotherMetric); + } + + @Test + void rejectsExcludedFromIncluded() { + ConfigProperties config = createConfig(MapEntry.entry("include.names", "included,excluded"), + MapEntry.entry("exclude.names", "excluded")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric); + } + + @SafeVarargs + private static ConfigProperties createConfig(MapEntry... entries) { + HashMap map = new HashMap<>(); + for (MapEntry entry: entries) { + map.put(entry.key, entry.value); + } + return createFromMap(map); + } + + @Test + void close() { + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + // nothing to do + } + verify(delegate).close(); + } + + @Test + void flush() { + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + exporter.flush(); + } + verify(delegate).flush(); + } + + @Test + void shutdown() { + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + exporter.shutdown(); + } + verify(delegate).shutdown(); + } + + @Test + void getAggregationTemporality() { + when(delegate.getAggregationTemporality(InstrumentType.COUNTER)).thenReturn(AggregationTemporality.DELTA); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + assertThat(exporter.getAggregationTemporality(InstrumentType.COUNTER)).isEqualTo( + AggregationTemporality.DELTA); + } + } + + @Test + void getDefaultAggregation() { + when(delegate.getDefaultAggregation(InstrumentType.COUNTER)).thenReturn(Aggregation.defaultAggregation()); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + assertThat(exporter.getDefaultAggregation(InstrumentType.COUNTER)).isEqualTo( + Aggregation.defaultAggregation()); + } + } + + @Test + void getMemoryMode() { + when(delegate.getMemoryMode()).thenReturn(MemoryMode.IMMUTABLE_DATA); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).build()) { + assertThat(exporter.getMemoryMode()).isEqualTo(MemoryMode.IMMUTABLE_DATA); + } + } +} From 5c965d303f5080ab63abd0d6740d009ec4a1c068 Mon Sep 17 00:00:00 2001 From: Karsten Schnitter Date: Tue, 25 Nov 2025 11:10:52 +0100 Subject: [PATCH 2/4] Add wildcard support Using an asterisk at the configured names ending transforms them into a prefix that is searched and filtered in the metric names. Signed-off-by: Karsten Schnitter --- .../ext/exporter/FilteringMetricExporter.java | 58 ++++++++++++++++--- .../exporter/FilteringMetricExporterTest.java | 32 ++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java index 9b155b2d..3438d49d 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java @@ -10,9 +10,12 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.function.Predicate; -import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; public class FilteringMetricExporter implements MetricExporter { @@ -29,7 +32,7 @@ private FilteringMetricExporter(MetricExporter delegate, Predicate p @Override public CompletableResultCode export(Collection collection) { - List filteredMetrics = collection.stream().filter(predicate).collect(Collectors.toList()); + List filteredMetrics = collection.stream().filter(predicate).collect(toList()); return delegate.export(filteredMetrics); } @@ -99,13 +102,54 @@ public MetricExporter build() { } Predicate predicate = metricData -> true; - if (!includedNames.isEmpty()) { - predicate = predicate.and(metricData -> includedNames.contains(metricData.getName())); + predicate = addInclusions(predicate, includedNames); + predicate = addExclusions(predicate, excludedNames); + return new FilteringMetricExporter(delegate, predicate); + } + + private static Predicate addInclusions(Predicate predicate, + List includedNames) { + if (includedNames.isEmpty()) { + return predicate; } - if (!excludedNames.isEmpty()) { - predicate = predicate.and(metricData -> !excludedNames.contains(metricData.getName())); + + HashSet names = getNames(includedNames); + if (!names.isEmpty()) { + predicate = predicate.and(metricData -> names.contains(metricData.getName())); } - return new FilteringMetricExporter(delegate, predicate); + List prefixes = getPrefixes(includedNames); + if (!prefixes.isEmpty()) { + predicate = predicate.and( + metricData -> prefixes.stream().anyMatch(p -> metricData.getName().startsWith(p))); + } + return predicate; + } + + private static Predicate addExclusions(Predicate predicate, + List excludedNames) { + if (excludedNames.isEmpty()) { + return predicate; + } + + HashSet names = getNames(excludedNames); + if (!names.isEmpty()) { + predicate = predicate.and(metricData -> !names.contains(metricData.getName())); + } + List prefixes = getPrefixes(excludedNames); + if (!prefixes.isEmpty()) { + predicate = predicate.and( + metricData -> prefixes.stream().anyMatch(p -> !metricData.getName().startsWith(p))); + } + return predicate; + } + + private static HashSet getNames(List names) { + return names.stream().filter(n -> !n.endsWith("*")).collect(toCollection(HashSet::new)); + } + + private static List getPrefixes(List names) { + return names.stream().filter(n -> n.endsWith("*")).map(n -> n.substring(0, n.length() - 1)) + .collect(toList()); } } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java index 84d5026b..bafd60ca 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java @@ -103,6 +103,38 @@ void rejectsExcludedFromIncluded() { assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric); } + @Test + void supportsWildcardsOnIncluded() { + ConfigProperties config = createConfig(MapEntry.entry("include.names", "incl*")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric); + } + + @Test + void supportsWildcardsOnEncluded() { + ConfigProperties config = createConfig(MapEntry.entry("exclude.names", "excl*")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric, anotherMetric); + } + + @Test + void supportsConfigPrefixes() { + ConfigProperties config = createConfig(MapEntry.entry("config.include.names", "included,excluded"), + MapEntry.entry("config.exclude.names", "excluded")); + try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config) + .withPropertyPrefix("config").build()) { + exporter.export(asList(includedMetric, excludedMetric, anotherMetric)); + } + verify(delegate).export(exported.capture()); + assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric); + } + @SafeVarargs private static ConfigProperties createConfig(MapEntry... entries) { HashMap map = new HashMap<>(); From efd83f7e0a67a2e76ba1bbd0a870c412a0112df3 Mon Sep 17 00:00:00 2001 From: Karsten Schnitter Date: Tue, 25 Nov 2025 11:24:18 +0100 Subject: [PATCH 3/4] Add documentation on filter configuration Signed-off-by: Karsten Schnitter --- .../README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cf-java-logging-support-opentelemetry-agent-extension/README.md b/cf-java-logging-support-opentelemetry-agent-extension/README.md index 2f3d51f1..441f3389 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/README.md +++ b/cf-java-logging-support-opentelemetry-agent-extension/README.md @@ -142,6 +142,21 @@ java -javaagent:/path/to/opentelemetry-javaagent-.jar \ The [OpenTelemetry Java Instrumentation project](https://github.com/open-telemetry/opentelemetry-java-instrumentation) provides detailed documentation on the configuration properties for [Logback](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/logback/logback-appender-1.0/javaagent) and [Log4j](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j/log4j-appender-2.17/javaagent). +### Filtering Metrics + +_This feature was introduced with version 4.1.0 of the extension._ + +You can filter which metrics are exported to Cloud Logging or Dynatrace by name using the following properties: + +| Property | Description | +|--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| +| `otel.exporter.cloud-logging.metrics.include.names` or `otel.exporter.dynatrace.metrics.include.names` | A comma-separated list of metric names to be forwarded. This may include a wild card "*" at the end of the name. | +| `otel.exporter.cloud-logging.metrics.exclude.names` or `otel.exporter.dynatrace.metrics.exclude.names` | A comma-separated list of metric names to be rejected. This may include a wild card "*" at the end of the name. | + +Note, that the `include` filter is applied before the `exclude` filter. +That means, if a metric matches both filters, it will be excluded. +The configuration applies to both the `cloud-logging` and `dynatrace` exporters independently. + ## Using User-Provided Service Instances ### SAP Cloud Logging From 461cd5b4af8bb01fd4c1587869a34af002e9a4f3 Mon Sep 17 00:00:00 2001 From: Karsten Schnitter Date: Wed, 26 Nov 2025 09:45:01 +0100 Subject: [PATCH 4/4] Update cf-java-logging-support-opentelemetry-agent-extension/README.md Co-authored-by: Christian Dinse <19688125+christiand93@users.noreply.github.com> --- .../README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cf-java-logging-support-opentelemetry-agent-extension/README.md b/cf-java-logging-support-opentelemetry-agent-extension/README.md index 441f3389..8b2d5c28 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/README.md +++ b/cf-java-logging-support-opentelemetry-agent-extension/README.md @@ -150,8 +150,8 @@ You can filter which metrics are exported to Cloud Logging or Dynatrace by name | Property | Description | |--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| `otel.exporter.cloud-logging.metrics.include.names` or `otel.exporter.dynatrace.metrics.include.names` | A comma-separated list of metric names to be forwarded. This may include a wild card "*" at the end of the name. | -| `otel.exporter.cloud-logging.metrics.exclude.names` or `otel.exporter.dynatrace.metrics.exclude.names` | A comma-separated list of metric names to be rejected. This may include a wild card "*" at the end of the name. | +| `otel.exporter.cloud-logging.metrics.include.names` or `otel.exporter.dynatrace.metrics.include.names` | A comma-separated list of metric names to be forwarded. This may include a wildcard "*" at the end of the name. | +| `otel.exporter.cloud-logging.metrics.exclude.names` or `otel.exporter.dynatrace.metrics.exclude.names` | A comma-separated list of metric names to be rejected. This may include a wildcard "*" at the end of the name. | Note, that the `include` filter is applied before the `exclude` filter. That means, if a metric matches both filters, it will be excluded.