Skip to content

Commit

Permalink
JSON exporter for Micrometer
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartisk committed Sep 17, 2020
1 parent 33d1d35 commit 928f180
Show file tree
Hide file tree
Showing 17 changed files with 795 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.quarkus.micrometer.deployment.export;

import java.util.function.BooleanSupplier;

import org.jboss.logging.Logger;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.micrometer.deployment.MicrometerRegistryProviderBuildItem;
import io.quarkus.micrometer.runtime.config.MicrometerConfig;
import io.quarkus.micrometer.runtime.export.JsonMeterRegistryProvider;
import io.quarkus.micrometer.runtime.export.JsonRecorder;
import io.quarkus.micrometer.runtime.registry.json.JsonMeterRegistry;
import io.quarkus.vertx.http.deployment.RouteBuildItem;

public class JsonRegistryProcessor {

private static final Logger log = Logger.getLogger(JsonRegistryProcessor.class);

public static class JsonRegistryEnabled implements BooleanSupplier {
MicrometerConfig mConfig;

public boolean getAsBoolean() {
return mConfig.checkRegistryEnabledWithDefault(mConfig.export.json);
}
}

@BuildStep(onlyIf = JsonRegistryEnabled.class)
@Record(ExecutionTime.STATIC_INIT)
public void initializeJsonRegistry(MicrometerConfig config,
BuildProducer<MicrometerRegistryProviderBuildItem> registryProviders,
BuildProducer<RouteBuildItem> routes,
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
JsonRecorder recorder,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
additionalBeans.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(JsonMeterRegistryProvider.class)
.setUnremovable().build());
registryProviders.produce(new MicrometerRegistryProviderBuildItem(JsonMeterRegistry.class));
routes.produce(new RouteBuildItem(recorder.route(config.export.json.path), recorder.getHandler()));
reflectiveClasses.produce(ReflectiveClassBuildItem
.builder("org.HdrHistogram.Histogram",
"org.HdrHistogram.DoubleHistogram",
"org.HdrHistogram.ConcurrentHistogram")
.constructors(true).build());
log.debug("Initialized a JSON meter registry on path=" + config.export.json.path);
}

}
7 changes: 7 additions & 0 deletions extensions/micrometer/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
<artifactId>micrometer-core</artifactId>
</dependency>

<!-- Needed for the JSON meter registry -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
</dependency>


<!-- Registry providers (optional) -->

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public GaugeAdapterImpl(String name, String description, String targetName, Stri
public GaugeAdapterImpl register(MetricDescriptor id, MeterRegistry registry) {
this.id = id;
if (gauge == null || metadata.cleanDirtyMetadata()) {
gauge = io.micrometer.core.instrument.Gauge.builder(metadata.name, this::getValue)
gauge = io.micrometer.core.instrument.Gauge.builder(metadata.name, this, g -> g.getValue().doubleValue())
.description(metadata.description())
.tags(id.tags())
.baseUnit(metadata.unit())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ConcurrentGaugeImpl implements ConcurrentGauge, MeterHolder {
Gauge gauge;

ConcurrentGaugeImpl register(MpMetadata metadata, MetricDescriptor metricInfo, MeterRegistry registry) {
gauge = io.micrometer.core.instrument.Gauge.builder(metricInfo.name(), longAdder::longValue)
gauge = io.micrometer.core.instrument.Gauge.builder(metricInfo.name(), longAdder, LongAdder::doubleValue)
.description(metadata.description())
.baseUnit(metadata.unit())
.tags(metricInfo.tags())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.quarkus.micrometer.runtime.config;

import java.time.Duration;
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

@ConfigGroup
public class JsonConfig implements MicrometerConfig.CapabilityEnabled {
/**
* Support for export to JSON format. Off by default.
*/
@ConfigItem(defaultValue = "false")
public Optional<Boolean> enabled;

/**
* The path for the JSON metrics endpoint.
* The default value is {@code /metrics-json}.
*/
@ConfigItem(defaultValue = "/metrics")
public String path;

/**
* Statistics like max, percentiles, and histogram counts decay over time to give greater weight to recent
* samples. Samples are accumulated to such statistics in ring buffers which rotate after
* the expiry, with this buffer length.
*/
@ConfigItem(defaultValue = "1024")
public Integer bufferLength;

/**
* Statistics like max, percentiles, and histogram counts decay over time to give greater weight to recent
* samples. Samples are accumulated to such statistics in ring buffers which rotate after
* this expiry, with a particular buffer length.
*/
@ConfigItem(defaultValue = "P3D")
public Duration expiry;

@Override
public Optional<Boolean> getEnabled() {
return enabled;
}

@Override
public String toString() {
return this.getClass().getSimpleName()
+ "{path='" + path
+ ",enabled=" + enabled
+ '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public static class ExportConfig {
public JmxConfig jmx;
public PrometheusConfig prometheus;
public StackdriverConfig stackdriver;
public JsonConfig json;
}

public static interface CapabilityEnabled {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.micrometer.runtime.export;

import javax.enterprise.inject.Produces;
import javax.inject.Singleton;

import io.micrometer.core.instrument.Clock;
import io.quarkus.micrometer.runtime.registry.json.JsonMeterRegistry;

@Singleton
public class JsonMeterRegistryProvider {

@Produces
@Singleton
public JsonMeterRegistry registry(Clock clock, io.quarkus.micrometer.runtime.config.MicrometerConfig config) {
return new JsonMeterRegistry(clock, config.export.json.bufferLength, config.export.json.expiry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.micrometer.runtime.export;

import java.util.function.Function;

import io.quarkus.micrometer.runtime.export.handlers.JsonHandler;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;

@Recorder
public class JsonRecorder {
JsonHandler handler;

public JsonHandler getHandler() {
if (handler == null) {
handler = new JsonHandler();
}

return handler;
}

public Function<Router, Route> route(String path) {
return new Function<Router, Route>() {
@Override
public Route apply(Router router) {
return router.route(path).order(2).produces("application/json");
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.function.Function;

import io.prometheus.client.exporter.common.TextFormat;
import io.quarkus.micrometer.runtime.export.handlers.PrometheusHandler;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.ext.web.Route;
Expand All @@ -24,7 +23,7 @@ public Function<Router, Route> route(String path) {
return new Function<Router, Route>() {
@Override
public Route apply(Router router) {
return router.route(path).produces(TextFormat.CONTENT_TYPE_004);
return router.route(path).order(1).produces("text/plain");
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.quarkus.micrometer.runtime.export.handlers;

import javax.enterprise.inject.Default;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.CDI;

import org.jboss.logging.Logger;

import io.quarkus.micrometer.runtime.registry.json.JsonMeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;

public class JsonHandler implements Handler<RoutingContext> {
private static final Logger log = Logger.getLogger(JsonHandler.class);

private JsonMeterRegistry registry;

private boolean setup = false;

@Override
public void handle(RoutingContext routingContext) {
if (!setup) {
setup();
}

HttpServerResponse response = routingContext.response();
if (registry == null) {
response.setStatusCode(500)
.setStatusMessage("Unable to resolve JSON registry instance");
} else {
response.putHeader("Content-Type", "application/json")
.end(Buffer.buffer(registry.scrape()));
}
}

private void setup() {
Instance<JsonMeterRegistry> registries = CDI.current().select(JsonMeterRegistry.class,
Default.Literal.INSTANCE);

if (registries.isUnsatisfied()) {
registry = null;
} else if (registries.isAmbiguous()) {
registry = registries.iterator().next();
log.warnf("Multiple JSON registries present: %s. Using %s with the built in scrape endpoint", registries,
registry);
} else {
registry = registries.get();
}

setup = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.quarkus.micrometer.runtime.registry.json;

import java.util.Arrays;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.concurrent.atomic.LongAdder;

import io.micrometer.core.instrument.AbstractDistributionSummary;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Statistic;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.TimeWindowMax;

public class JsonDistributionSummary extends AbstractDistributionSummary {

private final LongAdder count;
private final DoubleAdder total;
private final TimeWindowMax max;
private final TimeWindowMin min;

public JsonDistributionSummary(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
double scale, boolean supportsAggregablePercentiles) {
super(id, clock, distributionStatisticConfig, scale, supportsAggregablePercentiles);
this.count = new LongAdder();
this.total = new DoubleAdder();
this.max = new TimeWindowMax(clock, distributionStatisticConfig);
this.min = new TimeWindowMin(clock, distributionStatisticConfig);
}

@Override
protected void recordNonNegative(double amount) {
count.increment();
total.add(amount);
max.record(amount);
min.record(amount);
}

@Override
public long count() {
return count.longValue();
}

@Override
public double totalAmount() {
return total.sum();
}

@Override
public double max() {
return max.poll();
}

public double min() {
return min.poll();
}

@Override
public Iterable<Measurement> measure() {
return Arrays.asList(
new Measurement(() -> (double) count(), Statistic.COUNT),
new Measurement(this::totalAmount, Statistic.TOTAL),
new Measurement(this::max, Statistic.MAX));
}
}
Loading

0 comments on commit 928f180

Please sign in to comment.