Skip to content

Commit

Permalink
Allow running with Micrometer metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartisk committed Mar 18, 2021
1 parent f199409 commit a9ab369
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 68 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<version.yasson>1.0.8</version.yasson>
<version.jakarta-validation>2.0.2</version.jakarta-validation>
<version.graphql-java>16.2</version.graphql-java>
<verison.io.micrometer>1.6.4</verison.io.micrometer>

<!-- Test -->
<version.arquillian.jetty>1.0.0.CR3</version.arquillian.jetty>
Expand Down Expand Up @@ -111,6 +112,11 @@
<artifactId>smallrye-metrics</artifactId>
<version>${version.smallrye.metrics}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${verison.io.micrometer}</version>
</dependency>

<dependency>
<groupId>org.eclipse.microprofile.context-propagation</groupId>
Expand Down
5 changes: 5 additions & 0 deletions server/implementation-cdi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
<artifactId>microprofile-metrics-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package io.smallrye.graphql.cdi.metrics;

import java.time.Duration;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;

import javax.enterprise.inject.spi.CDI;
import javax.enterprise.util.AnnotationLiteral;

import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricType;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.RegistryType;

import io.smallrye.graphql.api.Context;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.schema.model.Operation;
import io.smallrye.graphql.spi.EventingService;

/**
* Listening for event and create metrics from it. Uses MP Metrics 3.x API.
*
* @author Jan Martiska ([email protected])
* @author Phillip Kruger ([email protected])
*/
public class MPMetricsService implements EventingService {

private MetricRegistry metricRegistry;
private final Map<Context, Long> startTimes = Collections.synchronizedMap(new IdentityHashMap<>());
private static final String METRIC_NAME = "mp_graphql";
private final String DESCRIPTION = "Call statistics for the operation denoted by the 'name' tag";

@Override
public Operation createOperation(Operation operation) {
final Tag[] tags = getTags(operation);

Metadata metadata = Metadata.builder()
.withName(METRIC_NAME)
.withType(MetricType.SIMPLE_TIMER)
.withDescription(DESCRIPTION)
.build();
getMetricRegistry().simpleTimer(metadata, tags);
return operation;
}

@Override
public void beforeDataFetch(Context context) {
startTimes.put(context, System.nanoTime());
}

@Override
public void afterDataFetch(Context context) {
Long startTime = startTimes.remove(context);
if (startTime != null) {
long duration = System.nanoTime() - startTime;
getMetricRegistry().simpleTimer(METRIC_NAME, getTags(context))
.update(Duration.ofNanos(duration));
}
}

@Override
public String getConfigKey() {
return ConfigKey.ENABLE_METRICS;
}

private MetricRegistry getMetricRegistry() {
if (metricRegistry == null) {
this.metricRegistry = CDI.current().select(MetricRegistry.class, new VendorType()).get();
}
return metricRegistry;
}

private Tag[] getTags(Context context) {
return new Tag[] {
new Tag("name", context.getFieldName()),
new Tag("type", context.getOperationType()),
new Tag("source", String.valueOf(context.getSource() != null))
};
}

private Tag[] getTags(Operation operation) {
return new Tag[] {
new Tag("name", operation.getName()),
new Tag("type", operation.getOperationType().toString()),
new Tag("source", String.valueOf(operation.isSourceField()))
};
}

class VendorType extends AnnotationLiteral<RegistryType> implements RegistryType {
@Override
public MetricRegistry.Type type() {
return MetricRegistry.Type.VENDOR;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,97 +1,55 @@
package io.smallrye.graphql.cdi.metrics;

import java.time.Duration;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;

import javax.enterprise.inject.spi.CDI;
import javax.enterprise.util.AnnotationLiteral;

import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricType;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.RegistryType;

import io.smallrye.graphql.api.Context;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.schema.model.Operation;
import io.smallrye.graphql.spi.EventingService;

/**
* Listening for event and create metrics from it
*
* @author Jan Martiska ([email protected])
* @author Phillip Kruger ([email protected])
* A wrapper that chooses an underlying service implementation for metrics
* and delegates to that implementation. The implementation is chosen based on
* detecting the availability of particular metrics APIs.
* Right now, it supports Micrometer and MP Metrics 3.x, while Micrometer
* is preferred.
*/
public class MetricsService implements EventingService {

private MetricRegistry metricRegistry;
private final Map<Context, Long> startTimes = Collections.synchronizedMap(new IdentityHashMap<>());
private static final String METRIC_NAME = "mp_graphql";
private final String DESCRIPTION = "Call statistics for the operation denoted by the 'name' tag";
private final EventingService wrapped;

public MetricsService() {
// Find out which metrics API is available
EventingService wrappedImpl;
try {
Class.forName("io.micrometer.core.instrument.MeterRegistry");
wrappedImpl = new MicrometerMetricsService();
} catch (ClassNotFoundException e) {
try {
Class.forName("org.eclipse.microprofile.metrics.MetricRegistry");
wrappedImpl = new MPMetricsService();
} catch (ClassNotFoundException classNotFoundException) {
wrappedImpl = null;
}
}
this.wrapped = wrappedImpl;
}

@Override
public Operation createOperation(Operation operation) {
final Tag[] tags = getTags(operation);

Metadata metadata = Metadata.builder()
.withName(METRIC_NAME)
.withType(MetricType.SIMPLE_TIMER)
.withDescription(DESCRIPTION)
.build();
getMetricRegistry().simpleTimer(metadata, tags);
return operation;
return wrapped.createOperation(operation);
}

@Override
public void beforeDataFetch(Context context) {
startTimes.put(context, System.nanoTime());
wrapped.beforeDataFetch(context);
}

@Override
public void afterDataFetch(Context context) {
Long startTime = startTimes.remove(context);
if (startTime != null) {
long duration = System.nanoTime() - startTime;
getMetricRegistry().simpleTimer(METRIC_NAME, getTags(context))
.update(Duration.ofNanos(duration));
}
wrapped.afterDataFetch(context);
}

@Override
public String getConfigKey() {
return ConfigKey.ENABLE_METRICS;
}

private MetricRegistry getMetricRegistry() {
if (metricRegistry == null) {
this.metricRegistry = CDI.current().select(MetricRegistry.class, new VendorType()).get();
}
return metricRegistry;
}

private Tag[] getTags(Context context) {
return new Tag[] {
new Tag("name", context.getFieldName()),
new Tag("type", context.getOperationType()),
new Tag("source", String.valueOf(context.getSource() != null))
};
}

private Tag[] getTags(Operation operation) {
return new Tag[] {
new Tag("name", operation.getName()),
new Tag("type", operation.getOperationType().toString()),
new Tag("source", String.valueOf(operation.isSourceField()))
};
}

class VendorType extends AnnotationLiteral<RegistryType> implements RegistryType {
@Override
public MetricRegistry.Type type() {
return MetricRegistry.Type.VENDOR;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.smallrye.graphql.cdi.metrics;

import java.time.Duration;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import io.smallrye.graphql.api.Context;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.schema.model.Operation;
import io.smallrye.graphql.spi.EventingService;

public class MicrometerMetricsService implements EventingService {

private final MeterRegistry meterRegistry = Metrics.globalRegistry;
private final Map<Context, Long> startTimes = Collections.synchronizedMap(new IdentityHashMap<>());
private static final String METRIC_NAME = "mp_graphql";
private final String DESCRIPTION = "Call statistics for the operation denoted by the 'name' tag";

@Override
public Operation createOperation(Operation operation) {
final Tags tags = getTags(operation);
Timer.builder(METRIC_NAME)
.tags(tags)
.description(DESCRIPTION)
.register(meterRegistry);
return operation;
}

@Override
public void beforeDataFetch(Context context) {
startTimes.put(context, System.nanoTime());
}

@Override
public void afterDataFetch(Context context) {
Long startTime = startTimes.remove(context);
if (startTime != null) {
long duration = System.nanoTime() - startTime;
meterRegistry.timer(METRIC_NAME, getTags(context))
.record(Duration.ofNanos(duration));
}
}

@Override
public String getConfigKey() {
return ConfigKey.ENABLE_METRICS;
}

private Tags getTags(Context context) {
return Tags.of("name", context.getFieldName())
.and("type", context.getOperationType())
.and("source", String.valueOf(context.getSource() != null));
}

private Tags getTags(Operation operation) {
return Tags.of("name", operation.getName())
.and("type", operation.getOperationType().toString())
.and("source", String.valueOf(operation.isSourceField()));
}

}

0 comments on commit a9ab369

Please sign in to comment.