Skip to content

Commit 22a6ee0

Browse files
committed
Polish MetricsEndpoint
See gh-10535
1 parent 3797583 commit 22a6ee0

File tree

2 files changed

+66
-56
lines changed

2 files changed

+66
-56
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,16 @@
1717
package org.springframework.boot.actuate.metrics;
1818

1919
import java.util.ArrayList;
20-
import java.util.Collection;
2120
import java.util.Collections;
2221
import java.util.HashMap;
23-
import java.util.HashSet;
22+
import java.util.LinkedHashMap;
23+
import java.util.LinkedHashSet;
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Set;
27+
import java.util.function.BiFunction;
2728
import java.util.stream.Collectors;
28-
import java.util.stream.Stream;
2929

30-
import io.micrometer.core.instrument.Measurement;
3130
import io.micrometer.core.instrument.Meter;
3231
import io.micrometer.core.instrument.MeterRegistry;
3332
import io.micrometer.core.instrument.Statistic;
@@ -44,6 +43,7 @@
4443
* An {@link Endpoint} for exposing the metrics held by a {@link MeterRegistry}.
4544
*
4645
* @author Jon Schneider
46+
* @author Phillip Webb
4747
* @since 2.0.0
4848
*/
4949
@Endpoint(id = "metrics")
@@ -57,24 +57,22 @@ public MetricsEndpoint(MeterRegistry registry) {
5757

5858
@ReadOperation
5959
public ListNamesResponse listNames() {
60-
return new ListNamesResponse(recurseListNames(this.registry));
60+
Set<String> names = new LinkedHashSet<>();
61+
collectNames(names, this.registry);
62+
return new ListNamesResponse(names);
6163
}
6264

63-
private Set<String> recurseListNames(MeterRegistry registry) {
64-
Set<String> names = new HashSet<>();
65+
private void collectNames(Set<String> names, MeterRegistry registry) {
6566
if (registry instanceof CompositeMeterRegistry) {
66-
for (MeterRegistry compositeMember : ((CompositeMeterRegistry) registry)
67-
.getRegistries()) {
68-
names.addAll(recurseListNames(compositeMember));
69-
}
67+
((CompositeMeterRegistry) registry).getRegistries()
68+
.forEach((member) -> collectNames(names, member));
7069
}
7170
else {
72-
registry.getMeters().stream().map(this::getMeterIdName).forEach(names::add);
71+
registry.getMeters().stream().map(this::getName).forEach(names::add);
7372
}
74-
return names;
7573
}
7674

77-
private String getMeterIdName(Meter meter) {
75+
private String getName(Meter meter) {
7876
return meter.getId().getName();
7977
}
8078

@@ -84,59 +82,73 @@ public MetricResponse metric(@Selector String requiredMetricName,
8482
Assert.isTrue(tag == null || tag.stream().allMatch((t) -> t.contains(":")),
8583
"Each tag parameter must be in the form key:value");
8684
List<Tag> tags = parseTags(tag);
87-
Collection<Meter> meters = recurseFindMeter(this.registry, requiredMetricName, tags);
85+
List<Meter> meters = new ArrayList<>();
86+
collectMeters(meters, this.registry, requiredMetricName, tags);
8887
if (meters.isEmpty()) {
8988
return null;
9089
}
91-
92-
Map<Statistic, Double> samples = new HashMap<>();
93-
Map<String, List<String>> availableTags = new HashMap<>();
94-
95-
for (Meter meter : meters) {
96-
for (Measurement ms : meter.measure()) {
97-
samples.merge(ms.getStatistic(), ms.getValue(), Double::sum);
98-
}
99-
for (Tag availableTag : meter.getId().getTags()) {
100-
availableTags.merge(availableTag.getKey(),
101-
Collections.singletonList(availableTag.getValue()),
102-
(t1, t2) -> Stream.concat(t1.stream(), t2.stream())
103-
.collect(Collectors.toList()));
104-
}
105-
}
106-
90+
Map<Statistic, Double> samples = getSamples(meters);
91+
Map<String, List<String>> availableTags = getAvailableTags(meters);
10792
tags.forEach((t) -> availableTags.remove(t.getKey()));
108-
10993
return new MetricResponse(requiredMetricName,
110-
samples.entrySet().stream()
111-
.map((sample) -> new MetricResponse.Sample(sample.getKey(),
112-
sample.getValue()))
113-
.collect(Collectors.toList()),
114-
availableTags.entrySet().stream()
115-
.map((tagValues) -> new MetricResponse.AvailableTag(
116-
tagValues.getKey(), tagValues.getValue()))
117-
.collect(Collectors.toList()));
94+
asList(samples, MetricResponse.Sample::new),
95+
asList(availableTags, MetricResponse.AvailableTag::new));
11896
}
11997

120-
private Collection<Meter> recurseFindMeter(MeterRegistry registry, String name,
98+
private List<Tag> parseTags(List<String> tags) {
99+
return tags == null ? Collections.emptyList() : tags.stream().map((t) -> {
100+
String[] tagParts = t.split(":", 2);
101+
return Tag.of(tagParts[0], tagParts[1]);
102+
}).collect(Collectors.toList());
103+
}
104+
105+
private void collectMeters(List<Meter> meters, MeterRegistry registry, String name,
121106
Iterable<Tag> tags) {
122-
Collection<Meter> meters = new ArrayList<>();
123107
if (registry instanceof CompositeMeterRegistry) {
124-
for (MeterRegistry compositeMember : ((CompositeMeterRegistry) registry)
125-
.getRegistries()) {
126-
meters.addAll(recurseFindMeter(compositeMember, name, tags));
127-
}
108+
((CompositeMeterRegistry) registry).getRegistries()
109+
.forEach((member) -> collectMeters(meters, member, name, tags));
128110
}
129111
else {
130112
meters.addAll(registry.find(name).tags(tags).meters());
131113
}
132-
return meters;
133114
}
134115

135-
private List<Tag> parseTags(List<String> tags) {
136-
return tags == null ? Collections.emptyList() : tags.stream().map((t) -> {
137-
String[] tagParts = t.split(":", 2);
138-
return Tag.of(tagParts[0], tagParts[1]);
139-
}).collect(Collectors.toList());
116+
private Map<Statistic, Double> getSamples(List<Meter> meters) {
117+
Map<Statistic, Double> samples = new LinkedHashMap<>();
118+
meters.forEach((meter) -> mergeMeasurements(samples, meter));
119+
return samples;
120+
}
121+
122+
private void mergeMeasurements(Map<Statistic, Double> samples, Meter meter) {
123+
meter.measure().forEach((measurement) -> samples.merge(measurement.getStatistic(),
124+
measurement.getValue(), Double::sum));
125+
}
126+
127+
private Map<String, List<String>> getAvailableTags(List<Meter> meters) {
128+
Map<String, List<String>> availableTags = new HashMap<>();
129+
meters.forEach((meter) -> mergeAvailableTags(availableTags, meter));
130+
return availableTags;
131+
}
132+
133+
private void mergeAvailableTags(Map<String, List<String>> availableTags,
134+
Meter meter) {
135+
meter.getId().getTags().forEach((tag) -> {
136+
List<String> value = Collections.singletonList(tag.getValue());
137+
availableTags.merge(tag.getKey(), value, this::merge);
138+
});
139+
}
140+
141+
private <T> List<T> merge(List<T> list1, List<T> list2) {
142+
List<T> result = new ArrayList<>(list1.size() + list2.size());
143+
result.addAll(list1);
144+
result.addAll(list2);
145+
return result;
146+
}
147+
148+
private <K, V, T> List<T> asList(Map<K, V> map, BiFunction<K, V, T> mapper) {
149+
return map.entrySet().stream()
150+
.map((entry) -> mapper.apply(entry.getKey(), entry.getValue()))
151+
.collect(Collectors.toCollection(ArrayList::new));
140152
}
141153

142154
/**
@@ -235,6 +247,8 @@ public String toString() {
235247
return "MeasurementSample{" + "statistic=" + this.statistic + ", value="
236248
+ this.value + '}';
237249
}
250+
238251
}
252+
239253
}
240254
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointTests.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ public void listNamesRecursesOverCompositeRegistries() {
6363
SimpleMeterRegistry reg2 = new SimpleMeterRegistry();
6464
composite.add(reg1);
6565
composite.add(reg2);
66-
6766
reg1.counter("counter1").increment();
6867
reg2.counter("counter2").increment();
69-
7068
MetricsEndpoint endpoint = new MetricsEndpoint(composite);
7169
assertThat(endpoint.listNames().getNames()).containsOnly("counter1", "counter2");
7270
}
@@ -103,10 +101,8 @@ public void metricPresentInOneRegistryOfACompositeAndNotAnother() {
103101
SimpleMeterRegistry reg2 = new SimpleMeterRegistry();
104102
composite.add(reg1);
105103
composite.add(reg2);
106-
107104
reg1.counter("counter1").increment();
108105
reg2.counter("counter2").increment();
109-
110106
MetricsEndpoint endpoint = new MetricsEndpoint(composite);
111107
assertThat(endpoint.metric("counter1", Collections.emptyList())).isNotNull();
112108
assertThat(endpoint.metric("counter2", Collections.emptyList())).isNotNull();

0 commit comments

Comments
 (0)