Skip to content
Permalink
Browse files
fixing openmetrics exporter (instead of prometheus) + base metrics na…
…mes/types
  • Loading branch information
rmannibucau committed Jan 2, 2020
1 parent 175b859 commit ad966e091c81492cc44b522c1ddcd2c8c8343be9
Showing 6 changed files with 189 additions and 83 deletions.
@@ -16,14 +16,14 @@
*/
package org.apache.geronimo.microprofile.metrics.common;

import static java.lang.String.format;

import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.util.List;
import java.util.function.LongSupplier;

import javax.json.bind.annotation.JsonbTransient;
@@ -34,6 +34,7 @@
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricType;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.Tag;

// isnt it super weird to hardcode that instead of defining a JMX integration?
// also the gauge/counter choice is quite surprising sometimes
@@ -102,18 +103,27 @@ public void register() {
.withName("thread.count")
.withDisplayName("Thread Count")
.withDescription("Displays the current number of live threads including both daemon and non-daemon threads")
.withType(MetricType.COUNTER).withUnit(MetricUnits.NONE).build(), counter(threadMXBean::getThreadCount));
.withType(MetricType.GAUGE)
.withUnit(MetricUnits.NONE)
.build(),
gauge(threadMXBean::getThreadCount));
registry.register(Metadata.builder()
.withName("thread.daemon.count")
.withDisplayName("Daemon Thread Count")
.withDescription("Displays the current number of live daemon threads.")
.withType(MetricType.COUNTER).withUnit(MetricUnits.NONE).build(), counter(threadMXBean::getDaemonThreadCount));
.withType(MetricType.GAUGE)
.withUnit(MetricUnits.NONE)
.build(),
gauge(threadMXBean::getDaemonThreadCount));
registry.register(Metadata.builder()
.withName("thread.max.count")
.withDisplayName("Peak Thread Count")
.withDescription("Displays the peak live thread count since the Java virtual machine started or peak was reset." +
"This includes daemon and non-daemon threads.")
.withType(MetricType.COUNTER).withUnit(MetricUnits.NONE).build(), counter(threadMXBean::getPeakThreadCount));
.withType(MetricType.GAUGE)
.withUnit(MetricUnits.NONE)
.build(),
gauge(threadMXBean::getPeakThreadCount));

final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
registry.register(Metadata.builder()
@@ -137,22 +147,31 @@ public void register() {
"if the amount of used memory does not exceed this maximum size.")
.withType(MetricType.GAUGE).withUnit(MetricUnits.BYTES).build(), gauge(memoryMXBean.getHeapMemoryUsage()::getMax));

ManagementFactory.getGarbageCollectorMXBeans().forEach(garbageCollectorMXBean -> {
final List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
garbageCollectorMXBeans.forEach(garbageCollectorMXBean -> {
registry.register(Metadata.builder()
.withName(format("gc.%s.count", garbageCollectorMXBean.getName()))
.withName("gc.total")
.withDisplayName("Garbage Collection Count")
.withDescription("Displays the total number of collections that have occurred." +
"This attribute lists -1 if the collection count is undefined for this collector.")
.withType(MetricType.COUNTER).withUnit(MetricUnits.NONE).build(), counter(garbageCollectorMXBean::getCollectionCount));
.withType(MetricType.COUNTER)
.withUnit(MetricUnits.NONE)
.build(),
counter(garbageCollectorMXBean::getCollectionCount),
new Tag("name", garbageCollectorMXBean.getName()));
registry.register(Metadata.builder()
.withName(format("gc.%s.time", garbageCollectorMXBean.getName()))
.withName("gc.time")
.withDisplayName("Garbage Collection Time")
.withDescription("Displays the approximate accumulated collection elapsed time in milliseconds." +
"This attribute displays -1 if the collection elapsed time is undefined for this collector." +
"The Java virtual machine implementation may use a high resolution timer to measure the elapsed time." +
"This attribute may display the same value even if the collection count has been incremented if" +
"the collection elapsed time is very short.")
.withType(MetricType.GAUGE).withUnit(MetricUnits.MILLISECONDS).build(), gauge(garbageCollectorMXBean::getCollectionTime));
.withType(MetricType.GAUGE)
.withUnit(MetricUnits.MILLISECONDS)
.build(),
gauge(garbageCollectorMXBean::getCollectionTime),
new Tag("name", garbageCollectorMXBean.getName()));
});
}

@@ -40,6 +40,7 @@
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricType;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.Timer;

@@ -96,7 +97,7 @@ public Counter counter(final Metadata metadata, final Tag... tags) {
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new CounterImpl(
metadata.getUnit().orElse("")), metadata, metricID);
metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
if (existing != null) {
holder = existing;
@@ -133,7 +134,7 @@ public ConcurrentGauge concurrentGauge(final Metadata metadata, final Tag... tag
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new ConcurrentGaugeImpl(
metadata.getUnit().orElse("")), metadata, metricID);
metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
if (existing != null) {
holder = existing;
@@ -159,7 +160,7 @@ public Histogram histogram(final Metadata metadata, final Tag... tags) {
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new HistogramImpl(metadata.getUnit().orElse("")), metadata, metricID);
holder = new Holder<>(new HistogramImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
@@ -185,7 +186,7 @@ public Meter meter(final Metadata metadata, final Tag... tags) {
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new MeterImpl(metadata.getUnit().orElse("")), metadata, metricID);
holder = new Holder<>(new MeterImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
@@ -211,7 +212,7 @@ public Timer timer(final Metadata metadata, final Tag... tags) {
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new TimerImpl(metadata.getUnit().orElse("")), metadata, metricID);
holder = new Holder<>(new TimerImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
@@ -26,7 +26,9 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import javax.ws.rs.GET;
@@ -57,6 +59,8 @@

@Path("metrics")
public class MetricsEndpoints {
private final Pattern semicolon = Pattern.compile(";");

private MetricRegistry baseRegistry;
private MetricRegistry vendorRegistry;
private MetricRegistry applicationRegistry;
@@ -96,7 +100,7 @@ public Object getJson(@Context final SecurityContext securityContext,
securityValidator.checkSecurity(securityContext, uriInfo);
return Stream.of(MetricRegistry.Type.values())
.collect(toMap(MetricRegistry.Type::getName, it -> findRegistry(it.getName()).getMetrics().entrySet().stream()
.collect(toMap(this::getKey, m -> toJson(map(m.getValue()), formatTags(m.getKey())), (a, b) -> a))));
.collect(toMap(this::getKey, m -> toJson(map(m.getValue()), formatTags(m.getKey())), this::merge))));
}

@GET
@@ -121,7 +125,7 @@ public Object getJson(@PathParam("registry") final String registry,
@Context final UriInfo uriInfo) {
securityValidator.checkSecurity(securityContext, uriInfo);
return findRegistry(registry).getMetrics().entrySet().stream()
.collect(toMap(this::getKey, it -> toJson(map(it.getValue()), formatTags(it.getKey())), (a, b) -> a));
.collect(toMap(this::getKey, it -> toJson(map(it.getValue()), formatTags(it.getKey())), this::merge));
}

@GET
@@ -169,8 +173,9 @@ public Object getMetadata(@PathParam("registry") final String registry,
@Context final SecurityContext securityContext,
@Context final UriInfo uriInfo) {
securityValidator.checkSecurity(securityContext, uriInfo);
return ofNullable(findRegistry(registry).getMetadata().get(name))
.map(metric -> singletonMap(name, mapMeta(metric, new MetricID(name))))
final MetricRegistry metricRegistry = findRegistry(registry);
return ofNullable(metricRegistry.getMetadata().get(name))
.map(metadata -> singletonMap(name, mapMeta(metadata, findMetricId(metricRegistry, metadata))))
.orElse(emptyMap());
}

@@ -181,21 +186,48 @@ public Object getMetadata(@PathParam("registry") final String registry,
@Context final SecurityContext securityContext,
@Context final UriInfo uriInfo) {
securityValidator.checkSecurity(securityContext, uriInfo);
return findRegistry(registry).getMetadata().entrySet().stream()
.collect(toMap(Map.Entry::getKey, e -> mapMeta(e.getValue(), new MetricID(e.getKey())), (a, b) -> a));
final MetricRegistry metricRegistry = findRegistry(registry);
return metricRegistry.getMetadata().entrySet().stream()
.collect(toMap(Map.Entry::getKey, e -> mapMeta(e.getValue(), findMetricId(metricRegistry, e.getValue())), this::merge));
}

private MetricID findMetricId(final MetricRegistry metricRegistry, final Metadata value) {
final Map<MetricID, Metric> metrics = metricRegistry.getMetrics();
final MetricID directKey = new MetricID(value.getName());
if (metrics.containsKey(directKey)) {
return directKey;
}
return metrics.keySet().stream()
.filter(it -> Objects.equals(it.getName(), value.getName()))
.findFirst()
.orElse(directKey);
}

private <A> A merge(final A a, final A b) {
if (Map.class.isInstance(a) && Map.class.isInstance(b)) {
final Map<String, Object> firstMap = (Map<String, Object>) a;
final Map<String, Object> secondMap = (Map<String, Object>) b;
final Map<String, Object> merged = Stream.concat(firstMap.entrySet().stream(), secondMap.entrySet().stream())
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (m1, m2) -> m1));
return (A) merged;
}
return a;
}

private Map<String, Metric> metrics(final MetricRegistry metricRegistry) {
return metricRegistry.getMetrics().entrySet().stream()
.collect(toMap(this::getKey, Map.Entry::getValue, (a, b) -> a));
.collect(toMap(this::getKey, Map.Entry::getValue, this::merge));
}

private <T> Map<String, T> singleEntry(final String id, final MetricRegistry metricRegistry,
final Function<Metric, T> metricMapper) {
final MetricID key = new MetricID(id);
return ofNullable(metricRegistry.getMetrics().get(key))
final Map<MetricID, Metric> metrics = metricRegistry.getMetrics();
return ofNullable(metrics.get(key)) // try first without any tag (fast access)
.map(metric -> singletonMap(id + formatTags(key), metricMapper.apply(metric)))
.orElseGet(Collections::emptyMap);
.orElseGet(() -> metrics.keySet().stream().filter(it -> Objects.equals(it.getName(), id)).findFirst() // else find first matching id
.map(metric -> singletonMap(id + formatTags(key), metricMapper.apply(metrics.get(metric))))
.orElseGet(Collections::emptyMap));
}

private Meta mapMeta(final Metadata value, final MetricID metricID) {
@@ -289,9 +321,9 @@ private MetricRegistry findRegistry(final String registry) {
}

private String formatTags(final MetricID id) {
return id.getTags().isEmpty() ? "" : (';' + id.getTags().entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(joining(",")));
return id.getTags().isEmpty() ? "" : (';' + id.getTagsAsList().stream()
.map(e -> e.getTagName() + "=" + semicolon.matcher(e.getTagValue()).replaceAll("_"))
.collect(joining(";")));
}

public static class Meta {
@@ -331,7 +363,7 @@ public boolean isReusable() {
return value.isReusable();
}

public String getTags() { // not sure why tck expect it, sounds worse than native getTags for clients
public String getTags() { // not sure why tck expect it, sounds worse than native getTags for clients (array of key/values)
return metricID.getTags().entrySet().stream().map(e -> e.getKey() + '=' + e.getValue()).collect(joining(","));
}
}

0 comments on commit ad966e0

Please sign in to comment.