Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package org.springframework.boot.actuate.metrics;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -81,15 +81,15 @@ private String getName(Meter meter) {
public MetricResponse metric(@Selector String requiredMetricName,
@Nullable List<String> tag) {
List<Tag> tags = parseTags(tag);
List<Meter> meters = new ArrayList<>();
collectMeters(meters, this.registry, requiredMetricName, tags);
Collection<Meter> meters = findFirstMatchingMeters(this.registry,
requiredMetricName, tags);
if (meters.isEmpty()) {
return null;
}
Map<Statistic, Double> samples = getSamples(meters);
Map<String, Set<String>> availableTags = getAvailableTags(meters);
tags.forEach((t) -> availableTags.remove(t.getKey()));
Meter.Id meterId = meters.get(0).getId();
Meter.Id meterId = meters.iterator().next().getId();
return new MetricResponse(requiredMetricName, meterId.getDescription(),
meterId.getBaseUnit(), asList(samples, Sample::new),
asList(availableTags, AvailableTag::new));
Expand All @@ -112,18 +112,25 @@ private Tag parseTag(String tag) {
return Tag.of(parts[0], parts[1]);
}

private void collectMeters(List<Meter> meters, MeterRegistry registry, String name,
private Collection<Meter> findFirstMatchingMeters(MeterRegistry registry, String name,
Iterable<Tag> tags) {
if (registry instanceof CompositeMeterRegistry) {
((CompositeMeterRegistry) registry).getRegistries()
.forEach((member) -> collectMeters(meters, member, name, tags));
return ((CompositeMeterRegistry) registry).getRegistries().stream()
.map((r) -> findFirstMatchingMeters(r, name, tags))
.filter((match) -> !match.isEmpty()).findFirst()
.orElse(Collections.emptyList());

}
else {
meters.addAll(registry.find(name).tags(tags).meters());
Collection<Meter> metersFound = registry.find(name).tags(tags).meters();
if (!metersFound.isEmpty()) {
return metersFound;
}
}
return Collections.emptyList();
}

private Map<Statistic, Double> getSamples(List<Meter> meters) {
private Map<Statistic, Double> getSamples(Collection<Meter> meters) {
Map<Statistic, Double> samples = new LinkedHashMap<>();
meters.forEach((meter) -> mergeMeasurements(samples, meter));
return samples;
Expand All @@ -138,7 +145,7 @@ private BiFunction<Double, Double, Double> mergeFunction(Statistic statistic) {
return Statistic.MAX.equals(statistic) ? Double::max : Double::sum;
}

private Map<String, Set<String>> getAvailableTags(List<Meter> meters) {
private Map<String, Set<String>> getAvailableTags(Collection<Meter> meters) {
Map<String, Set<String>> availableTags = new HashMap<>();
meters.forEach((meter) -> mergeAvailableTags(availableTags, meter));
return availableTags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,55 @@ public void metricValuesAreTheSumOfAllTimeSeriesMatchingTags() {
assertThat(getCount(response)).hasValue(4.0);
}

@Test
public void findFirstMatchingMetersFromNestedRegistries() {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
SimpleMeterRegistry reg1 = new SimpleMeterRegistry();
CompositeMeterRegistry reg2 = new CompositeMeterRegistry();
SimpleMeterRegistry reg3 = new SimpleMeterRegistry();

// 1st level nesting
composite.add(reg1);

// 2st level nesting
reg2.add(reg3);
composite.add(reg2);

// 2nd level registry has metrics
reg3.counter("cache", "result", "hit", "host", "1").increment(2);
reg3.counter("cache", "result", "miss", "host", "1").increment(2);
reg3.counter("cache", "result", "hit", "host", "2").increment(2);

MetricsEndpoint endpoint = new MetricsEndpoint(composite);

MetricsEndpoint.MetricResponse response = endpoint.metric("cache",
Collections.emptyList());
assertThat(response.getName()).isEqualTo("cache");
assertThat(availableTagKeys(response)).containsExactly("result", "host");
assertThat(getCount(response)).hasValue(6.0);

response = endpoint.metric("cache", Collections.singletonList("result:hit"));
assertThat(availableTagKeys(response)).containsExactly("host");
assertThat(getCount(response)).hasValue(4.0);
}

@Test
public void matchingMeterNotFoundInNestedRegistries() {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
CompositeMeterRegistry reg2 = new CompositeMeterRegistry();
SimpleMeterRegistry reg3 = new SimpleMeterRegistry();

// nested registries
reg2.add(reg3);
composite.add(reg2);

MetricsEndpoint endpoint = new MetricsEndpoint(composite);

MetricsEndpoint.MetricResponse response = endpoint.metric("invalid.metric.name",
Collections.emptyList());
assertThat(response).isNull();
}

@Test
public void metricTagValuesAreDeduplicated() {
this.registry.counter("cache", "host", "1", "region", "east", "result", "hit");
Expand Down