Skip to content
Permalink
Browse files
better tag handling for all metric types
  • Loading branch information
rmannibucau committed Jan 2, 2020
1 parent 6e9ddc3 commit 7c894380f5d6ae9a048a8e00f2ef10ff1a3b55a8
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 29 deletions.
@@ -244,7 +244,7 @@ public Histogram histogram(final String name) {

@Override
public Histogram histogram(final String name, final Tag... tags) {
return histogram(Metadata.builder().withName(name).withType(MetricType.HISTOGRAM).build());
return histogram(Metadata.builder().withName(name).withType(MetricType.HISTOGRAM).build(), tags);
}

@Override
@@ -254,7 +254,7 @@ public Meter meter(final String name) {

@Override
public Meter meter(final String name, final Tag... tags) {
return meter(Metadata.builder().withName(name).withType(MetricType.METERED).build());
return meter(Metadata.builder().withName(name).withType(MetricType.METERED).build(), tags);
}

@Override
@@ -264,7 +264,7 @@ public Timer timer(final String name) {

@Override
public Timer timer(final String name, final Tag... tags) {
return timer(Metadata.builder().withName(name).withType(MetricType.TIMER).build());
return timer(Metadata.builder().withName(name).withType(MetricType.TIMER).build(), tags);
}

@Override
@@ -24,6 +24,7 @@
import static java.util.stream.Collectors.toMap;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -41,12 +42,18 @@
import javax.ws.rs.core.UriInfo;

import org.apache.geronimo.microprofile.metrics.common.prometheus.PrometheusFormatter;
import org.eclipse.microprofile.metrics.ConcurrentGauge;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
import org.eclipse.microprofile.metrics.Histogram;
import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.Meter;
import org.eclipse.microprofile.metrics.Metered;
import org.eclipse.microprofile.metrics.Metric;
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.Snapshot;
import org.eclipse.microprofile.metrics.Timer;

@Path("metrics")
public class MetricsEndpoints {
@@ -89,7 +96,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(Map.Entry::getKey, m -> map(m.getValue())))));
.collect(toMap(this::getKey, m -> toJson(map(m.getValue()), formatTags(m.getKey())), (a, b) -> a))));
}

@GET
@@ -114,7 +121,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(Map.Entry::getKey, it -> map(it.getValue())));
.collect(toMap(this::getKey, it -> toJson(map(it.getValue()), formatTags(it.getKey())), (a, b) -> a));
}

@GET
@@ -174,12 +181,12 @@ public Object getMetadata(@PathParam("registry") final String registry,
@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()))));
.collect(toMap(Map.Entry::getKey, e -> mapMeta(e.getValue(), new MetricID(e.getKey())), (a, b) -> a));
}

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

private <T> Map<String, T> singleEntry(final String id, final MetricRegistry metricRegistry,
@@ -203,6 +210,69 @@ private Object map(final Metric metric) {
return metric;
}

private String getKey(final Map.Entry<MetricID, Metric> e) {
if (Counter.class.isInstance(e.getValue()) || Gauge.class.isInstance(e.getValue())) {
return e.getKey().getName() + formatTags(e.getKey());
}
return e.getKey().getName();
}

// https://github.com/eclipse/microprofile-metrics/issues/508
private Object toJson(final Object metric, final String nameSuffix) {
if (Timer.class.isInstance(metric)) {
final Timer meter = Timer.class.cast(metric);
final Map<Object, Object> map = new HashMap<>(15);
map.putAll(snapshot(meter.getSnapshot(), nameSuffix));
map.putAll(meter(meter, nameSuffix));
return map;
}
if (Meter.class.isInstance(metric)) {
return meter(Meter.class.cast(metric), nameSuffix);
}
if (Histogram.class.isInstance(metric)) {
final Histogram histogram = Histogram.class.cast(metric);
final Map<Object, Object> map = new HashMap<>(11);
map.putAll(snapshot(histogram.getSnapshot(), nameSuffix));
map.put("count" + nameSuffix, histogram.getCount());
return map;
}
if (ConcurrentGauge.class.isInstance(metric)) {
final ConcurrentGauge concurrentGauge = ConcurrentGauge.class.cast(metric);
final Map<Object, Object> map = new HashMap<>(3);
map.put("min" + nameSuffix, concurrentGauge.getMin());
map.put("current" + nameSuffix, concurrentGauge.getCount());
map.put("max" + nameSuffix, concurrentGauge.getMax());
return map;
}
// counters and gauges are unwrapped so skip it
return metric;
}

private Map<String, Object> meter(final Metered metered, final String nameSuffix) {
final Map<String, Object> map = new HashMap<>(5);
map.put("count" + nameSuffix, metered.getCount());
map.put("meanRate" + nameSuffix, metered.getMeanRate());
map.put("oneMinRate" + nameSuffix, metered.getOneMinuteRate());
map.put("fiveMinRate" + nameSuffix, metered.getFiveMinuteRate());
map.put("fifteenMinRate" + nameSuffix, metered.getFifteenMinuteRate());
return map;
}

private Map<String, Object> snapshot(final Snapshot snapshot, final String nameSuffix) {
final Map<String, Object> map = new HashMap<>(10);
map.put("p50" + nameSuffix, snapshot.getMedian());
map.put("p75" + nameSuffix, snapshot.get75thPercentile());
map.put("p95" + nameSuffix, snapshot.get95thPercentile());
map.put("p98" + nameSuffix, snapshot.get98thPercentile());
map.put("p99" + nameSuffix, snapshot.get99thPercentile());
map.put("p999" + nameSuffix, snapshot.get999thPercentile());
map.put("min" + nameSuffix, snapshot.getMin());
map.put("mean" + nameSuffix, snapshot.getMean());
map.put("max" + nameSuffix, snapshot.getMax());
map.put("stddev" + nameSuffix, snapshot.getStdDev());
return map;
}

private MetricRegistry findRegistry(final String registry) {
switch (Stream.of(MetricRegistry.Type.values()).filter(it -> it.getName().equals(registry)).findFirst()
.orElseThrow(() -> new WebApplicationException(Response.Status.NOT_FOUND))) {
@@ -215,6 +285,12 @@ 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(",")));
}

public static class Meta {
private final Metadata value;
private final MetricID metricID;
@@ -32,13 +32,13 @@
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.eclipse.microprofile.metrics.ConcurrentGauge;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
import org.eclipse.microprofile.metrics.Histogram;
import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.Meter;
import org.eclipse.microprofile.metrics.Metric;
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.Snapshot;
@@ -96,16 +96,28 @@ public StringBuilder toText(final MetricRegistry registry,
final Map<String, Metric> entries) {
final Map<String, Metadata> metadatas = registry.getMetadata();
return entries.entrySet().stream()
.map(it -> new Entry(metadatas.get(it.getKey()), registryKey + ':' + toPrometheusKey(metadatas.get(it.getKey())), it.getValue()))
.map(it -> {
String key = it.getKey();
final int tagSep = key.indexOf(';');
if (tagSep > 0) {
key = key.substring(0, tagSep);
}
final Metadata metadata = metadatas.get(key);
return new Entry(metadata, registryKey + ':' + toPrometheusKey(metadata), it.getValue());
})
.filter(it -> prefixFilter == null || prefixFilter.test(it.prometheusKey))
.map(entry -> {
switch (entry.metadata.getTypeRaw()) {
case COUNTER:
case CONCURRENT_GAUGE: {
case COUNTER: {
final String key = toPrometheusKey(entry.metadata);
return new StringBuilder()
.append(value(registryKey, key, Counter.class.cast(entry.metric).getCount(), entry.metadata));
}
case CONCURRENT_GAUGE: {
final String key = toPrometheusKey(entry.metadata);
return new StringBuilder()
.append(value(registryKey, key, ConcurrentGauge.class.cast(entry.metric).getCount(), entry.metadata));
}
case GAUGE: {
final Object val = Gauge.class.cast(entry.metric).getValue();
if (Number.class.isInstance(val)) {
@@ -183,7 +195,7 @@ private String toPrometheusKey(final Metadata metadata) {
}

private String toUnitSuffix(final Metadata metadata) {
return metadata.getUnit().orElse("").equalsIgnoreCase("none") ?
return metadata.getUnit().orElse("none").equalsIgnoreCase("none") ?
"" : ("_" + toPrometheusUnit(metadata.getUnit().orElse("")));
}

@@ -290,8 +302,8 @@ private double toPrometheusValue(final String unit, final double value) {
}
}

private String toPrometheus(final Metadata metadata) {
return metadata.getName()
private String toPrometheus(final Metadata id) {
return id.getName()
.replaceAll("[^\\w]+", "_")
.replaceAll("(.)(\\p{Upper})", "$1_$2")
.replace("__", "_")
@@ -60,6 +60,7 @@
import org.apache.geronimo.microprofile.metrics.common.BaseMetrics;
import org.apache.geronimo.microprofile.metrics.common.GaugeImpl;
import org.apache.geronimo.microprofile.metrics.common.RegistryImpl;
import org.apache.geronimo.microprofile.metrics.common.jaxrs.MetricsEndpoints;
import org.apache.geronimo.microprofile.metrics.jaxrs.CdiMetricsEndpoints;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
@@ -86,11 +87,13 @@ public class MetricsExtension implements Extension {
private final MetricRegistry vendorRegistry = new RegistryImpl();

private final Map<MetricID, Metadata> registrations = new HashMap<>();
private final Map<String, Function<BeanManager, Gauge<?>>> gaugeFactories = new HashMap<>();
private final Map<MetricID, Function<BeanManager, Gauge<?>>> gaugeFactories = new HashMap<>();
private final Collection<Runnable> producersRegistrations = new ArrayList<>();
private final Collection<CreationalContext<?>> creationalContexts = new ArrayList<>();

void letOtherExtensionsUseRegistries(@Observes final ProcessAnnotatedType<CdiMetricsEndpoints> processAnnotatedType) {
private Map<String, String> environmentalTags;

void vetoEndpointIfNotActivated(@Observes final ProcessAnnotatedType<CdiMetricsEndpoints> processAnnotatedType) {
if ("false".equalsIgnoreCase(System.getProperty("geronimo.metrics.jaxrs.activated"))) { // default is secured so deploy
processAnnotatedType.veto();
}
@@ -101,6 +104,13 @@ void vetoDefaultRegistry(@Observes final ProcessAnnotatedType<RegistryImpl> proc
processAnnotatedType.veto();
}

// can happen in shades
void vetoNonCdiEndpoint(@Observes final ProcessAnnotatedType<MetricsEndpoints> processAnnotatedType) {
if (processAnnotatedType.getAnnotatedType().getJavaClass() == MetricsEndpoints.class) { // not subclasses
processAnnotatedType.veto();
}
}

void letOtherExtensionsUseRegistries(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager beanManager) {
beforeBeanDiscovery.addQualifier(RegistryType.class);
beanManager.fireEvent(applicationRegistry);
@@ -114,6 +124,9 @@ void letOtherExtensionsUseRegistries(@Observes final BeforeBeanDiscovery beforeB
final String name = method.getAnnotated().getJavaMember().getName();
return name.equals("name") || name.equals("tags");
}).forEach(method -> method.remove(a -> a.annotationType() == Nonbinding.class));

// we must update @Metrics with that if it exists for the cdi resolution ot match since we make it binding
environmentalTags = new MetricID("foo").getTags();
}

void onMetric(@Observes final ProcessProducerField<? extends Metric, ?> processProducerField, final BeanManager beanManager) {
@@ -132,7 +145,7 @@ void onMetric(@Observes final ProcessProducerField<? extends Metric, ?> processP
}

void onMetric(@Observes ProcessProducerMethod<? extends Metric, ?> processProducerMethod,
final BeanManager beanManager) {
final BeanManager beanManager) {
final org.eclipse.microprofile.metrics.annotation.Metric config = processProducerMethod.getAnnotated()
.getAnnotation(org.eclipse.microprofile.metrics.annotation.Metric.class);
if (config == null) {
@@ -160,7 +173,7 @@ void onMetric(@Observes final ProcessInjectionPoint<?, ?> metricInjectionPointPr

final MetricType type = findType(clazz);
if (config != null) {
String name = Names.findName(injectionPoint.getMember().getDeclaringClass(), injectionPoint.getMember(),
final String name = Names.findName(injectionPoint.getMember().getDeclaringClass(), injectionPoint.getMember(),
of(config.name()).filter(it -> !it.isEmpty()).orElseGet(injectionPoint.getMember()::getName), config.absolute(),
"");
final Metadata metadata = Metadata.builder()
@@ -173,7 +186,7 @@ void onMetric(@Observes final ProcessInjectionPoint<?, ?> metricInjectionPointPr
final MetricID id = new MetricID(name, createTags(config.tags()));
addRegistration(metadata, id, true);

if (!name.equals(config.name())) {
if (!name.equals(config.name()) || !environmentalTags.isEmpty()) {
final Annotation[] newQualifiers = Stream.concat(metricInjectionPointProcessEvent.getInjectionPoint().getQualifiers().stream()
.filter(it -> it.annotationType() != org.eclipse.microprofile.metrics.annotation.Metric.class),
Stream.of(new MetricImpl(metadata, id)))
@@ -317,7 +330,7 @@ void findInterceptorMetrics(@Observes @WithAnnotations({
.build();
final MetricID metricID = new MetricID(name, createTags(gauge.tags()));
addRegistration(metadata, metricID, false);
gaugeFactories.put(name, beanManager -> {
gaugeFactories.put(metricID, beanManager -> {
final Object reference = getInstance(javaClass, beanManager);
final Method mtd = Method.class.cast(javaMember);
return new GaugeImpl<>(reference, mtd);
@@ -378,11 +391,11 @@ public Object getValue() {

void afterDeploymentValidation(@Observes final AfterDeploymentValidation afterDeploymentValidation,
final BeanManager beanManager) {
registrations.values().stream()
.filter(m -> m.getTypeRaw() == MetricType.GAUGE)
.forEach(metadata -> {
final Gauge<?> gauge = gaugeFactories.get(metadata.getName()).apply(beanManager);
applicationRegistry.register(metadata, gauge);
registrations.entrySet().stream()
.filter(e -> e.getValue().getTypeRaw() == MetricType.GAUGE)
.forEach(entry -> {
final Gauge<?> gauge = gaugeFactories.get(entry.getKey()).apply(beanManager);
applicationRegistry.register(entry.getValue(), gauge, entry.getKey().getTagsAsList().toArray(NO_TAG));
});
producersRegistrations.forEach(Runnable::run);

@@ -409,7 +422,9 @@ private void registerProducer(final BeanManager beanManager, final org.eclipse.m
beanClass = javaMember.getDeclaringClass();
}
final Metadata metadata = createMetadata(config, clazz, javaMember, beanClass);
applicationRegistry.register(metadata, Metric.class.cast(getInstance(clazz, beanManager, bean)));
applicationRegistry.register(
metadata, Metric.class.cast(getInstance(clazz, beanManager, bean)),
createTags(config.tags()));
}

private Metadata createMetadata(final org.eclipse.microprofile.metrics.annotation.Metric config,

0 comments on commit 7c89438

Please sign in to comment.