Skip to content

Commit 5c965d3

Browse files
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 <k.schnitter@sap.com>
1 parent 3418b26 commit 5c965d3

2 files changed

Lines changed: 83 additions & 7 deletions

File tree

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporter.java

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import io.opentelemetry.sdk.metrics.export.MetricExporter;
1111

1212
import java.util.Collection;
13+
import java.util.HashSet;
1314
import java.util.List;
1415
import java.util.function.Predicate;
15-
import java.util.stream.Collectors;
16+
17+
import static java.util.stream.Collectors.toCollection;
18+
import static java.util.stream.Collectors.toList;
1619

1720
public class FilteringMetricExporter implements MetricExporter {
1821

@@ -29,7 +32,7 @@ private FilteringMetricExporter(MetricExporter delegate, Predicate<MetricData> p
2932

3033
@Override
3134
public CompletableResultCode export(Collection<MetricData> collection) {
32-
List<MetricData> filteredMetrics = collection.stream().filter(predicate).collect(Collectors.toList());
35+
List<MetricData> filteredMetrics = collection.stream().filter(predicate).collect(toList());
3336
return delegate.export(filteredMetrics);
3437
}
3538

@@ -99,13 +102,54 @@ public MetricExporter build() {
99102
}
100103

101104
Predicate<MetricData> predicate = metricData -> true;
102-
if (!includedNames.isEmpty()) {
103-
predicate = predicate.and(metricData -> includedNames.contains(metricData.getName()));
105+
predicate = addInclusions(predicate, includedNames);
106+
predicate = addExclusions(predicate, excludedNames);
107+
return new FilteringMetricExporter(delegate, predicate);
108+
}
109+
110+
private static Predicate<MetricData> addInclusions(Predicate<MetricData> predicate,
111+
List<String> includedNames) {
112+
if (includedNames.isEmpty()) {
113+
return predicate;
104114
}
105-
if (!excludedNames.isEmpty()) {
106-
predicate = predicate.and(metricData -> !excludedNames.contains(metricData.getName()));
115+
116+
HashSet<String> names = getNames(includedNames);
117+
if (!names.isEmpty()) {
118+
predicate = predicate.and(metricData -> names.contains(metricData.getName()));
107119
}
108-
return new FilteringMetricExporter(delegate, predicate);
120+
List<String> prefixes = getPrefixes(includedNames);
121+
if (!prefixes.isEmpty()) {
122+
predicate = predicate.and(
123+
metricData -> prefixes.stream().anyMatch(p -> metricData.getName().startsWith(p)));
124+
}
125+
return predicate;
126+
}
127+
128+
private static Predicate<MetricData> addExclusions(Predicate<MetricData> predicate,
129+
List<String> excludedNames) {
130+
if (excludedNames.isEmpty()) {
131+
return predicate;
132+
}
133+
134+
HashSet<String> names = getNames(excludedNames);
135+
if (!names.isEmpty()) {
136+
predicate = predicate.and(metricData -> !names.contains(metricData.getName()));
137+
}
138+
List<String> prefixes = getPrefixes(excludedNames);
139+
if (!prefixes.isEmpty()) {
140+
predicate = predicate.and(
141+
metricData -> prefixes.stream().anyMatch(p -> !metricData.getName().startsWith(p)));
142+
}
143+
return predicate;
144+
}
145+
146+
private static HashSet<String> getNames(List<String> names) {
147+
return names.stream().filter(n -> !n.endsWith("*")).collect(toCollection(HashSet::new));
148+
}
149+
150+
private static List<String> getPrefixes(List<String> names) {
151+
return names.stream().filter(n -> n.endsWith("*")).map(n -> n.substring(0, n.length() - 1))
152+
.collect(toList());
109153
}
110154
}
111155
}

cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/FilteringMetricExporterTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,38 @@ void rejectsExcludedFromIncluded() {
103103
assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric);
104104
}
105105

106+
@Test
107+
void supportsWildcardsOnIncluded() {
108+
ConfigProperties config = createConfig(MapEntry.entry("include.names", "incl*"));
109+
try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) {
110+
exporter.export(asList(includedMetric, excludedMetric, anotherMetric));
111+
}
112+
verify(delegate).export(exported.capture());
113+
assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric);
114+
}
115+
116+
@Test
117+
void supportsWildcardsOnEncluded() {
118+
ConfigProperties config = createConfig(MapEntry.entry("exclude.names", "excl*"));
119+
try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config).build()) {
120+
exporter.export(asList(includedMetric, excludedMetric, anotherMetric));
121+
}
122+
verify(delegate).export(exported.capture());
123+
assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric, anotherMetric);
124+
}
125+
126+
@Test
127+
void supportsConfigPrefixes() {
128+
ConfigProperties config = createConfig(MapEntry.entry("config.include.names", "included,excluded"),
129+
MapEntry.entry("config.exclude.names", "excluded"));
130+
try (MetricExporter exporter = FilteringMetricExporter.wrap(delegate).withConfig(config)
131+
.withPropertyPrefix("config").build()) {
132+
exporter.export(asList(includedMetric, excludedMetric, anotherMetric));
133+
}
134+
verify(delegate).export(exported.capture());
135+
assertThat(exported.getValue()).containsExactlyInAnyOrder(includedMetric);
136+
}
137+
106138
@SafeVarargs
107139
private static ConfigProperties createConfig(MapEntry<String, String>... entries) {
108140
HashMap<String, String> map = new HashMap<>();

0 commit comments

Comments
 (0)