Skip to content

Commit 6eb2663

Browse files
committed
Add TagReplacingFilter
Signed-off-by: etki <[email protected]>
1 parent 6367990 commit 6eb2663

File tree

5 files changed

+454
-24
lines changed

5 files changed

+454
-24
lines changed

micrometer-core/src/main/java/io/micrometer/core/instrument/config/MeterFilter.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import io.micrometer.common.lang.Nullable;
1919
import io.micrometer.core.instrument.*;
20+
import io.micrometer.core.instrument.config.filter.TagReplacingFilter;
2021
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
2122

2223
import java.time.Duration;
@@ -120,22 +121,7 @@ public Meter.Id map(Meter.Id id) {
120121
* @return A filter that replaces tag values.
121122
*/
122123
static MeterFilter replaceTagValues(String tagKey, Function<String, String> replacement, String... exceptions) {
123-
return new MeterFilter() {
124-
@Override
125-
public Meter.Id map(Meter.Id id) {
126-
List<Tag> tags = stream(id.getTagsAsIterable().spliterator(), false).map(t -> {
127-
if (!t.getKey().equals(tagKey))
128-
return t;
129-
for (String exception : exceptions) {
130-
if (t.getValue().equals(exception))
131-
return t;
132-
}
133-
return Tag.of(tagKey, replacement.apply(t.getValue()));
134-
}).collect(toList());
135-
136-
return id.replaceTags(tags);
137-
}
138-
};
124+
return TagReplacingFilter.classicValueReplacing(tagKey, replacement, exceptions);
139125
}
140126

141127
/**
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package io.micrometer.core.instrument.config.filter;
22

33
class FilterSupport {
4+
45
/**
5-
* At the moment of writing, it was impossible to estimate tags
6-
* count from the outside of class, but quite often a temporary
7-
* storage (ArrayList) had to be allocated during processing. To
8-
* avoid excessive resizes, this constant is introduced to
6+
* At the moment of writing, it was impossible to estimate tags count from the outside
7+
* of class, but quite often a temporary storage (ArrayList) had to be allocated
8+
* during processing. To avoid excessive resizes, this constant is introduced to
99
* preallocate space for such a list.
1010
*/
1111
public static final int DEFAULT_TAG_COUNT_EXPECTATION = 32;
1212

13-
private FilterSupport() {}
13+
private FilterSupport() {
14+
}
15+
1416
}

micrometer-core/src/main/java/io/micrometer/core/instrument/config/filter/NoOpFilter.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
import io.micrometer.core.instrument.config.MeterFilter;
44

55
/**
6-
* A fallback for all factory methods that have received an input
7-
* functionally equivalent to "abstain from processing".
6+
* A fallback for all factory methods that have received an input functionally equivalent
7+
* to "abstain from processing".
88
*
99
* @since 1.15
1010
*/
1111
public class NoOpFilter implements MeterFilter {
12+
1213
private static final MeterFilter INSTANCE = new NoOpFilter();
1314

14-
private NoOpFilter() {}
15+
private NoOpFilter() {
16+
}
1517

1618
public static MeterFilter create() {
1719
return INSTANCE;
1820
}
21+
1922
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package io.micrometer.core.instrument.config.filter;
2+
3+
import io.micrometer.common.lang.NonNull;
4+
import io.micrometer.core.instrument.Meter;
5+
import io.micrometer.core.instrument.Tag;
6+
import io.micrometer.core.instrument.config.MeterFilter;
7+
8+
import java.util.*;
9+
import java.util.function.BiFunction;
10+
import java.util.function.BiPredicate;
11+
import java.util.function.Function;
12+
13+
public class TagReplacingFilter implements MeterFilter {
14+
15+
private final BiPredicate<String, String> filter;
16+
17+
private final BiFunction<String, String, Tag> replacer;
18+
19+
private final int expectedTagCount;
20+
21+
TagReplacingFilter(BiPredicate<String, String> filter, BiFunction<String, String, Tag> replacer,
22+
int expectedTagCount) {
23+
this.replacer = replacer;
24+
this.filter = filter;
25+
this.expectedTagCount = expectedTagCount;
26+
}
27+
28+
@NonNull
29+
@Override
30+
public Meter.Id map(@NonNull Meter.Id id) {
31+
Iterator<Tag> iterator = id.getTagsAsIterable().iterator();
32+
33+
if (!iterator.hasNext()) {
34+
// fast path avoiding list allocation completely
35+
return id;
36+
}
37+
38+
List<Tag> replacement = new ArrayList<>(expectedTagCount);
39+
40+
boolean intercepted = false;
41+
while (iterator.hasNext()) {
42+
Tag tag = iterator.next();
43+
String key = tag.getKey();
44+
String value = tag.getValue();
45+
46+
if (filter.test(key, value)) {
47+
replacement.add(replacer.apply(key, value));
48+
intercepted = true;
49+
}
50+
else {
51+
replacement.add(tag);
52+
}
53+
}
54+
55+
return intercepted ? id.replaceTags(replacement) : id;
56+
}
57+
58+
public static MeterFilter of(BiPredicate<String, String> filter, BiFunction<String, String, Tag> replacer,
59+
int expectedSize) {
60+
return new TagReplacingFilter(filter, replacer, expectedSize);
61+
}
62+
63+
public static MeterFilter of(BiPredicate<String, String> filter, BiFunction<String, String, Tag> replacer) {
64+
return new TagReplacingFilter(filter, replacer, FilterSupport.DEFAULT_TAG_COUNT_EXPECTATION);
65+
}
66+
67+
public static MeterFilter classicValueReplacing(String key, Function<String, String> replacer,
68+
Collection<String> exceptions, int expectedSize) {
69+
return of(new ClassicFilter(key, new HashSet<>(exceptions)), new ValueReplacer(replacer), expectedSize);
70+
}
71+
72+
public static MeterFilter classicValueReplacing(String key, Function<String, String> replacer,
73+
Collection<String> exceptions) {
74+
return classicValueReplacing(key, replacer, exceptions, FilterSupport.DEFAULT_TAG_COUNT_EXPECTATION);
75+
}
76+
77+
public static MeterFilter classicValueReplacing(String key, Function<String, String> replacer,
78+
String... exceptions) {
79+
return classicValueReplacing(key, replacer, Arrays.asList(exceptions));
80+
}
81+
82+
private static class ClassicFilter implements BiPredicate<String, String> {
83+
84+
private final String matcher;
85+
86+
private final Set<String> exceptions;
87+
88+
public ClassicFilter(String matcher, Set<String> exceptions) {
89+
this.matcher = matcher;
90+
this.exceptions = exceptions;
91+
}
92+
93+
@Override
94+
public boolean test(String key, String value) {
95+
return key.equals(matcher) && !exceptions.contains(value);
96+
}
97+
98+
}
99+
100+
private static class ValueReplacer implements BiFunction<String, String, Tag> {
101+
102+
private final Function<String, String> delegate;
103+
104+
public ValueReplacer(Function<String, String> delegate) {
105+
this.delegate = delegate;
106+
}
107+
108+
@Override
109+
public Tag apply(String key, String value) {
110+
return Tag.of(key, delegate.apply(value));
111+
}
112+
113+
}
114+
115+
}

0 commit comments

Comments
 (0)