Skip to content

Commit 1604d05

Browse files
committed
Improve binders to take advantage of new function-tracking counters.
* Cache metrics * Executor service metrics * Fixed bugs in Prometheus in Dropwizard when registering multiple custom meters whose IDs vary only by tag values.
1 parent 4d38516 commit 1604d05

File tree

20 files changed

+281
-90
lines changed

20 files changed

+281
-90
lines changed

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/CacheMetrics.java

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.micrometer.core.instrument.*;
2222

2323
import java.util.Arrays;
24+
import java.util.Collections;
2425

2526
import static java.util.Collections.singletonList;
2627

@@ -42,25 +43,16 @@ public CacheMetrics(String name, Iterable<Tag> tags, Cache<?, ?> cache) {
4243
public void bindTo(MeterRegistry registry) {
4344
registry.gauge(name + "_size", tags, cache, Cache::size);
4445

45-
registry.register(Meters.build(name + "_requests")
46-
.type(Meter.Type.Counter)
47-
.create(cache, (n, cacheRef) -> {
48-
CacheStats stats = cacheRef.stats();
49-
return Arrays.asList(
50-
/**
51-
* The sum of these two measurements is equal to {@link CacheStats#requestCount()}
52-
*/
53-
new Measurement(n, singletonList(Tag.of("result", "hit")), stats.hitCount()),
54-
new Measurement(n, singletonList(Tag.of("result", "miss")), stats.missCount())
55-
);
56-
}));
57-
58-
registry.gauge(name + "_evictions", tags, cache, c -> c.stats().evictionCount());
59-
registry.gauge(name + "_load_duration", tags, cache, c -> c.stats().totalLoadTime());
46+
registry.counter(name + "_requests", Tags.zip("result", "miss"), cache, c -> c.stats().missCount());
47+
registry.counter(name + "_requests", Tags.zip("result", "hit"), cache, c -> c.stats().hitCount());
48+
registry.counter(name + "_evictions", tags, cache, c -> c.stats().evictionCount());
6049

6150
if (cache instanceof LoadingCache) {
62-
registry.gauge(name + "_loads", tags, cache, c -> c.stats().loadCount());
63-
registry.gauge(name + "_load_failures", tags, cache, c -> c.stats().loadExceptionCount());
51+
// dividing these gives you a measure of load latency
52+
registry.counter(name + "_load_duration", tags, cache, c -> c.stats().totalLoadTime());
53+
registry.counter(name + "_loads", tags, cache, c -> c.stats().loadCount());
54+
55+
registry.counter(name + "_load_failures", tags, cache, c -> c.stats().loadExceptionCount());
6456
}
6557
}
6658
}

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/ClassLoaderMetrics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ public void bindTo(MeterRegistry registry) {
2626
ClassLoadingMXBean classLoadingBean = ManagementFactory.getClassLoadingMXBean();
2727

2828
registry.gauge("classes_loaded", classLoadingBean, ClassLoadingMXBean::getLoadedClassCount);
29-
registry.gauge("classes_unloaded", classLoadingBean, ClassLoadingMXBean::getUnloadedClassCount);
29+
registry.counter("classes_unloaded", classLoadingBean, ClassLoadingMXBean::getUnloadedClassCount);
3030
}
3131
}

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/ExecutorServiceMetrics.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,26 +85,20 @@ private void monitor(MeterRegistry registry, ThreadPoolExecutor tp) {
8585
return;
8686
}
8787

88-
registry.register(Meters.build(name)
89-
.type(Meter.Type.Counter)
90-
.tags(tags)
91-
.create(tp, (n, tpRef) -> Arrays.asList(
92-
// The sum of the three lifecycle phases is monotonically increasing, though scheduled and active
93-
// can go up and down as tasks are added and completed. The sum of these three measurements
94-
// gives you the total of all tasks submitted irrespective of whether they have been completed or not.
95-
new Measurement(n, singletonList(Tag.of("lifecycle", "scheduled")), tpRef.getTaskCount()),
96-
new Measurement(n, singletonList(Tag.of("lifecycle", "completed")), tpRef.getCompletedTaskCount()),
97-
new Measurement(n, singletonList(Tag.of("lifecycle", "active")), tpRef.getActiveCount())
98-
)));
88+
// queued tasks = tasks - completed - active
89+
registry.counter(name + "_tasks", tp, tpRef -> tpRef.getTaskCount() + tpRef.getCompletedTaskCount() + tpRef.getActiveCount());
90+
registry.counter(name + "_completed", tp, ThreadPoolExecutor::getCompletedTaskCount);
9991

92+
registry.gauge(name + "_active", tp, ThreadPoolExecutor::getActiveCount);
10093
registry.gauge(name + "_queue_size", tags, tp, tpRef -> tpRef.getQueue().size());
10194
registry.gauge(name + "_pool_size", tags, tp, ThreadPoolExecutor::getPoolSize);
10295
}
10396

10497
private void monitor(MeterRegistry registry, ForkJoinPool fj) {
105-
registry.gauge(name + "_active", fj, ForkJoinPool::getActiveThreadCount);
98+
registry.counter(name + "_steal_count", fj, ForkJoinPool::getStealCount);
99+
106100
registry.gauge(name + "_queued_tasks", fj, ForkJoinPool::getQueuedTaskCount);
101+
registry.gauge(name + "_active", fj, ForkJoinPool::getActiveThreadCount);
107102
registry.gauge(name + "_running_threads", fj, ForkJoinPool::getRunningThreadCount);
108-
registry.gauge(name + "_steal_count", fj, ForkJoinPool::getStealCount);
109103
}
110104
}

micrometer-core/src/main/java/io/micrometer/core/instrument/dropwizard/DropwizardMeterRegistry.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,11 @@ public LongTaskTimer longTaskTimer(String name, Iterable<Tag> tags) {
114114

115115
@Override
116116
public MeterRegistry register(Meter meter) {
117-
MapAccess.computeIfAbsent(meterMap, new MeterId(meter.getName(), meter.getTags()), id -> {
118-
for (int i = 0; i < meter.measure().size(); i++) {
119-
final int index = i;
120-
registry.register(toDropwizardName(id), (Gauge<Double>) () -> meter.measure().get(index).getValue());
121-
}
122-
return meter;
117+
meter.measure().forEach(ms -> {
118+
MapAccess.computeIfAbsent(meterMap, new MeterId(ms.getName(), ms.getTags()), id -> {
119+
registry.register(toDropwizardName(id), (Gauge<Double>) ms::getValue);
120+
return meter;
121+
});
123122
});
124123

125124
return this;

micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/PrometheusDistributionSummary.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import io.micrometer.core.instrument.Meters;
1919
import io.micrometer.core.instrument.Tag;
20+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusSummary;
2021
import io.micrometer.core.instrument.util.MeterId;
2122
import io.micrometer.core.instrument.DistributionSummary;
2223
import io.micrometer.core.instrument.Measurement;

micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/PrometheusLongTaskTimer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.micrometer.core.instrument.Measurement;
2020
import io.micrometer.core.instrument.Meters;
2121
import io.micrometer.core.instrument.Tag;
22+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusLongTaskTimer;
2223
import io.micrometer.core.instrument.util.MeterId;
2324

2425
import java.util.List;

micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/PrometheusMeterRegistry.java

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
import io.micrometer.core.instrument.*;
1919
import io.micrometer.core.instrument.AbstractMeterRegistry;
2020
import io.micrometer.core.instrument.internal.FunctionTrackingCounter;
21+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusCollector;
22+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusLongTaskTimer;
23+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusSummary;
2124
import io.micrometer.core.instrument.util.MapAccess;
2225
import io.micrometer.core.instrument.util.MeterId;
2326
import io.micrometer.core.instrument.stats.hist.Histogram;
@@ -38,7 +41,6 @@
3841
import java.util.function.Function;
3942
import java.util.function.ToDoubleFunction;
4043
import java.util.stream.Collectors;
41-
import java.util.stream.Stream;
4244

4345
import static java.util.stream.Collectors.toList;
4446
import static java.util.stream.StreamSupport.stream;
@@ -188,45 +190,27 @@ public double get() {
188190
@Override
189191
public MeterRegistry register(Meter meter) {
190192
meterMap.computeIfAbsent(new MeterId(meter.getName(), meter.getTags()), id -> {
191-
collectorMap.computeIfAbsent(meter.getName(), name -> {
192-
Collector collector = new Collector() {
193-
@Override
194-
public List<MetricFamilySamples> collect() {
195-
List<MetricFamilySamples.Sample> samples = meter.measure().stream()
196-
.map(m -> {
197-
Iterable<Tag> allTags = withCommonTags(m.getTags());
198-
List<String> tagKeys = new ArrayList<>();
199-
List<String> tagValues = new ArrayList<>();
200-
for (Tag tag : allTags) {
201-
tagKeys.add(tagFormatter.formatTagKey(tag.getKey()));
202-
tagValues.add(tagFormatter.formatTagValue(tag.getValue()));
203-
}
204-
return new MetricFamilySamples.Sample(tagFormatter.formatName(m.getName()), tagKeys, tagValues, m.getValue());
205-
})
206-
.collect(toList());
207-
208-
Type type = Type.UNTYPED;
209-
switch (meter.getType()) {
210-
case Counter:
211-
type = Type.COUNTER;
212-
break;
213-
case Gauge:
214-
type = Type.GAUGE;
215-
break;
216-
case DistributionSummary:
217-
case Timer:
218-
type = Type.SUMMARY;
219-
break;
220-
}
221-
222-
return Collections.singletonList(new MetricFamilySamples(meter.getName(), type, " ", samples));
223-
}
224-
};
193+
CustomPrometheusCollector c = (CustomPrometheusCollector) collectorMap.computeIfAbsent(meter.getName(), name -> {
194+
Collector.Type type = Collector.Type.UNTYPED;
195+
switch (meter.getType()) {
196+
case Counter:
197+
type = Collector.Type.COUNTER;
198+
break;
199+
case Gauge:
200+
type = Collector.Type.GAUGE;
201+
break;
202+
case DistributionSummary:
203+
case Timer:
204+
type = Collector.Type.SUMMARY;
205+
break;
206+
}
225207

208+
Collector collector = new CustomPrometheusCollector(name, type);
226209
registry.register(collector);
227210
return collector;
228211
});
229212

213+
c.child(meter::measure);
230214
return meter;
231215
});
232216

micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/PrometheusTimer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.micrometer.core.instrument.Clock;
1919
import io.micrometer.core.instrument.Measurement;
2020
import io.micrometer.core.instrument.AbstractTimer;
21+
import io.micrometer.core.instrument.prometheus.internal.CustomPrometheusSummary;
2122
import io.micrometer.core.instrument.util.MeterId;
2223
import io.micrometer.core.instrument.util.TimeUtils;
2324

micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/CustomCollectorChild.java renamed to micrometer-core/src/main/java/io/micrometer/core/instrument/prometheus/internal/CustomCollectorChild.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,23 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package io.micrometer.core.instrument.prometheus;
16+
package io.micrometer.core.instrument.prometheus.internal;
1717

1818
import io.micrometer.core.instrument.Measurement;
1919
import io.micrometer.core.instrument.Tag;
2020
import io.prometheus.client.Collector;
2121

2222
import java.util.List;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
import java.util.concurrent.ConcurrentMap;
25+
import java.util.concurrent.atomic.AtomicLong;
2326
import java.util.stream.Collectors;
2427
import java.util.stream.IntStream;
2528
import java.util.stream.Stream;
2629

30+
import static java.util.stream.Collectors.toList;
31+
import static java.util.stream.StreamSupport.stream;
32+
2733
public interface CustomCollectorChild {
2834
Stream<Collector.MetricFamilySamples.Sample> collect();
2935

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright 2017 Pivotal Software, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.instrument.prometheus.internal;
17+
18+
import io.micrometer.core.instrument.Measurement;
19+
import io.micrometer.core.instrument.Tag;
20+
import io.prometheus.client.Collector;
21+
22+
import java.util.Collection;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.concurrent.ConcurrentLinkedQueue;
26+
import java.util.function.Supplier;
27+
import java.util.stream.Stream;
28+
29+
import static java.util.stream.Collectors.toList;
30+
31+
public class CustomPrometheusCollector extends Collector {
32+
private final String name;
33+
private final Type type;
34+
private final Collection<Child> children = new ConcurrentLinkedQueue<>();
35+
36+
public CustomPrometheusCollector(String name, Type type) {
37+
this.type = type;
38+
this.name = name;
39+
}
40+
41+
public Child child(Supplier<List<Measurement>> measurementSupplier) {
42+
Child child = new Child(measurementSupplier);
43+
children.add(child);
44+
return child;
45+
}
46+
47+
@Override
48+
public List<MetricFamilySamples> collect() {
49+
return Collections.singletonList(new MetricFamilySamples(name, type, " ", children.stream()
50+
.flatMap(Child::collect).collect(toList())));
51+
}
52+
53+
class Child implements CustomCollectorChild {
54+
private final Supplier<List<Measurement>> measurementSupplier;
55+
56+
Child(Supplier<List<Measurement>> measurementSupplier) {
57+
this.measurementSupplier = measurementSupplier;
58+
}
59+
60+
@Override
61+
public Stream<Collector.MetricFamilySamples.Sample> collect() {
62+
return measurementSupplier.get().stream()
63+
.map(m -> {
64+
List<String> keys = m.getTags().stream().map(Tag::getKey).collect(toList());
65+
List<String> values = m.getTags().stream().map(Tag::getValue).collect(toList());
66+
return new MetricFamilySamples.Sample(name, keys, values, m.getValue());
67+
});
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)