Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 2b66bd6

Browse files
author
Bruno Rodrigues
committed
Added Metrics Instrumentation
1 parent ceece7d commit 2b66bd6

File tree

7 files changed

+120
-4
lines changed

7 files changed

+120
-4
lines changed

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ LIB_GRAPHQL_JAVA_VER = 9.2
4040
LIB_JUNIT_VER = 4.12
4141
LIB_SPRING_CORE_VER = 5.0.4.RELEASE
4242
LIB_SPRING_BOOT_VER = 2.0.5.RELEASE
43+
LIB_MICROMETER_VER = 1.0.6
4344
LIB_GRAPHQL_SERVLET_VER = 6.1.4
4445
LIB_GRAPHQL_JAVA_TOOLS_VER = 5.3.3
4546
LIB_COMMONS_IO_VER = 2.6

graphql-spring-boot-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies {
2121
compile "org.springframework.boot:spring-boot-configuration-processor:$LIB_SPRING_BOOT_VER"
2222
compile "org.springframework.boot:spring-boot-autoconfigure:$LIB_SPRING_BOOT_VER"
2323
compile "org.springframework.boot:spring-boot-starter-websocket:$LIB_SPRING_BOOT_VER"
24+
compile "io.micrometer:micrometer-core:$LIB_MICROMETER_VER"
2425
compile "com.graphql-java-kickstart:graphql-java-servlet:$LIB_GRAPHQL_SERVLET_VER"
2526
compile "commons-io:commons-io:$LIB_COMMONS_IO_VER"
2627
compile "com.graphql-java-kickstart:graphql-java-tools:$LIB_GRAPHQL_JAVA_TOOLS_VER"

graphql-spring-boot-autoconfigure/src/main/java/com/oembedler/moon/graphql/boot/GraphQLInstrumentationAutoConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import graphql.analysis.MaxQueryComplexityInstrumentation;
44
import graphql.analysis.MaxQueryDepthInstrumentation;
55
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
6+
import io.micrometer.core.instrument.MeterRegistry;
67
import org.springframework.beans.factory.annotation.Value;
78
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
89
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -44,4 +45,12 @@ public MaxQueryComplexityInstrumentation maxQueryComplexityInstrumentation() {
4445
public MaxQueryDepthInstrumentation maxQueryDepthInstrumentation() {
4546
return new MaxQueryDepthInstrumentation(maxQueryDepth);
4647
}
48+
49+
@Bean
50+
@ConditionalOnMissingBean
51+
@ConditionalOnProperty(value = "graphql.servlet.actuator-metrics", havingValue = "true")
52+
public MetricsInstrumentation metricsInstrumentation(MeterRegistry meterRegistry) {
53+
return new MetricsInstrumentation(meterRegistry);
54+
}
55+
4756
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.oembedler.moon.graphql.boot;
2+
3+
import graphql.ExecutionResult;
4+
import graphql.execution.instrumentation.InstrumentationContext;
5+
import graphql.execution.instrumentation.InstrumentationState;
6+
import graphql.execution.instrumentation.SimpleInstrumentation;
7+
import graphql.execution.instrumentation.SimpleInstrumentationContext;
8+
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
9+
import graphql.servlet.GraphQLContext;
10+
import io.micrometer.core.instrument.MeterRegistry;
11+
import io.micrometer.core.instrument.Timer;
12+
13+
import java.util.concurrent.TimeUnit;
14+
15+
/**
16+
* @author Bruno Rodrigues
17+
*/
18+
public class MetricsInstrumentation extends SimpleInstrumentation {
19+
20+
public InstrumentationState createState() {
21+
return new MetricsSupport();
22+
}
23+
24+
private MeterRegistry meterRegistry;
25+
26+
private static final String OPERATION_TIME_METRIC_NAME = "graphql.timer.operation";
27+
private static final String OPERATION_NAME_TAG = "operationName";
28+
private static final String UNKNOWN_OPERATION_NAME = "unknown";
29+
private static final String EXCEPTION_TAG = "exceptionName";
30+
private static final String NONE_EXCEPTION = "None";
31+
private static final String TIMER_DESCRIPTION = "Timer that records the time to fetch the data by Operation Name";
32+
33+
public MetricsInstrumentation(MeterRegistry meterRegistry) {
34+
this.meterRegistry = meterRegistry;
35+
}
36+
37+
@Override
38+
public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
39+
40+
return new SimpleInstrumentationContext<ExecutionResult>() {
41+
@Override
42+
public void onCompleted(ExecutionResult result, Throwable t) {
43+
44+
GraphQLContext graphQLContext = parameters.getContext();
45+
MetricsSupport metricsSupport = parameters.getInstrumentationState();
46+
if (graphQLContext.getHttpServletRequest().isPresent()) {
47+
48+
Timer timer = buildTimer(parameters.getOperation(), t);
49+
timer.record(metricsSupport.getTotalTime(), TimeUnit.NANOSECONDS);
50+
51+
}
52+
53+
}
54+
55+
};
56+
57+
}
58+
59+
private Timer buildTimer(String operationName, Throwable t) {
60+
return Timer.builder(OPERATION_TIME_METRIC_NAME)
61+
.description(TIMER_DESCRIPTION)
62+
.tag(OPERATION_NAME_TAG, operationName != null ? operationName : UNKNOWN_OPERATION_NAME)
63+
.tag(EXCEPTION_TAG, t == null ? NONE_EXCEPTION : t.getClass().getSimpleName())
64+
.register(meterRegistry);
65+
}
66+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.oembedler.moon.graphql.boot;
2+
3+
import graphql.execution.instrumentation.InstrumentationState;
4+
5+
/**
6+
* @author Bruno Rodrigues
7+
*/
8+
public class MetricsSupport implements InstrumentationState {
9+
10+
private final long startRequestNanos = System.nanoTime();
11+
12+
public long getTotalTime() {
13+
return System.nanoTime() - startRequestNanos;
14+
}
15+
16+
}

graphql-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
"name": "graphql.servlet.maxQueryDepth",
4545
"defaultValue": null,
4646
"type": "java.lang.Integer"
47+
},
48+
{
49+
"name": "graphql.servlet.actuator-metrics",
50+
"defaultValue": false,
51+
"type": "java.lang.Boolean"
4752
}
4853
]
4954
}

graphql-spring-boot-autoconfigure/src/test/java/com/oembedler/moon/graphql/boot/test/instrumentation/GraphQLInstrumentationAutoConfigurationTest.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package com.oembedler.moon.graphql.boot.test.instrumentation;
22

33
import com.oembedler.moon.graphql.boot.GraphQLInstrumentationAutoConfiguration;
4-
import com.oembedler.moon.graphql.boot.GraphQLWebAutoConfiguration;
4+
import com.oembedler.moon.graphql.boot.MetricsInstrumentation;
55
import com.oembedler.moon.graphql.boot.test.AbstractAutoConfigurationTest;
66
import graphql.analysis.MaxQueryComplexityInstrumentation;
77
import graphql.analysis.MaxQueryDepthInstrumentation;
8-
import graphql.execution.AsyncExecutionStrategy;
9-
import graphql.execution.ExecutionStrategy;
108
import graphql.execution.instrumentation.Instrumentation;
119
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
1210
import graphql.schema.GraphQLObjectType;
1311
import graphql.schema.GraphQLSchema;
14-
import graphql.servlet.AbstractGraphQLHttpServlet;
12+
import io.micrometer.core.instrument.MeterRegistry;
13+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
1514
import org.junit.Assert;
1615
import org.junit.Test;
1716
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -36,6 +35,11 @@ GraphQLSchema schema() {
3635
return GraphQLSchema.newSchema().query(GraphQLObjectType.newObject().name("Query").build()).build();
3736
}
3837

38+
@Bean
39+
MeterRegistry meterRegistry() {
40+
return new SimpleMeterRegistry();
41+
}
42+
3943
}
4044

4145
@Test(expected = NoSuchBeanDefinitionException.class)
@@ -72,4 +76,18 @@ public void maxQueryDepthEnabled() {
7276

7377
Assert.assertNotNull(this.getContext().getBean(MaxQueryDepthInstrumentation.class));
7478
}
79+
80+
@Test
81+
public void actuatorMetricsEnabled() {
82+
load(DefaultConfiguration.class, "graphql.servlet.actuator-metrics=true");
83+
84+
Assert.assertNotNull(this.getContext().getBean(MetricsInstrumentation.class));
85+
}
86+
87+
@Test(expected = NoSuchBeanDefinitionException.class)
88+
public void actuatorMetricsDisabled() {
89+
load(DefaultConfiguration.class, "graphql.servlet.actuator-metrics=false");
90+
91+
this.getContext().getBean(MetricsInstrumentation.class);
92+
}
7593
}

0 commit comments

Comments
 (0)