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

Commit 7a0be2e

Browse files
author
Bruno Rodrigues
committed
Mapped everything on tracing support to metrics
1 parent 7c0fdf4 commit 7a0be2e

File tree

7 files changed

+128
-67
lines changed

7 files changed

+128
-67
lines changed

graphql-spring-boot-autoconfigure/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919

2020
dependencies {
21+
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:$LIB_SPRING_BOOT_VER"
22+
2123
compileOnly "org.springframework.boot:spring-boot-configuration-processor:$LIB_SPRING_BOOT_VER"
2224

2325
compile "org.springframework.boot:spring-boot-autoconfigure:$LIB_SPRING_BOOT_VER"

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.oembedler.moon.graphql.boot;
22

3+
import com.oembedler.moon.graphql.boot.metrics.MetricsInstrumentation;
4+
import com.oembedler.moon.graphql.boot.metrics.TracingNoResolversInstrumentation;
35
import graphql.analysis.MaxQueryComplexityInstrumentation;
46
import graphql.analysis.MaxQueryDepthInstrumentation;
57
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
@@ -30,13 +32,23 @@ public class GraphQLInstrumentationAutoConfiguration {
3032
@Value("${graphql.servlet.maxQueryDepth:#{null}}")
3133
private Integer maxQueryDepth;
3234

35+
@Value("${graphql.servlet.tracing-enabled:#{false}}")
36+
private Boolean tracingEnabled;
37+
3338
@Bean
3439
@ConditionalOnMissingBean
3540
@ConditionalOnProperty(value = "graphql.servlet.tracing-enabled", havingValue = "true")
3641
public TracingInstrumentation tracingInstrumentation() {
3742
return new TracingInstrumentation();
3843
}
3944

45+
@Bean
46+
@ConditionalOnMissingBean
47+
@ConditionalOnProperty(value = "graphql.servlet.tracing-enabled", havingValue = "false")
48+
public TracingNoResolversInstrumentation tracingNoResolversInstrumentation() {
49+
return new TracingNoResolversInstrumentation();
50+
}
51+
4052
@Bean
4153
@ConditionalOnMissingBean
4254
@ConditionalOnProperty(value = "graphql.servlet.max-query-complexity")
@@ -53,10 +65,10 @@ public MaxQueryDepthInstrumentation maxQueryDepthInstrumentation() {
5365

5466
@Bean
5567
@ConditionalOnProperty(value = "graphql.servlet.actuator-metrics", havingValue = "true")
56-
@ConditionalOnBean(MeterRegistry.class)
68+
@ConditionalOnBean({MeterRegistry.class, TracingInstrumentation.class})
5769
@ConditionalOnMissingBean
5870
public MetricsInstrumentation metricsInstrumentation(MeterRegistry meterRegistry) {
59-
return new MetricsInstrumentation(meterRegistry);
71+
return new MetricsInstrumentation(meterRegistry, tracingEnabled);
6072
}
6173

6274
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.fasterxml.jackson.databind.InjectableValues;
2323
import com.fasterxml.jackson.databind.ObjectMapper;
2424
import com.oembedler.moon.graphql.boot.error.GraphQLErrorHandlerFactory;
25+
import com.oembedler.moon.graphql.boot.metrics.MetricsInstrumentation;
2526
import graphql.execution.AsyncExecutionStrategy;
2627
import graphql.execution.ExecutionStrategy;
2728
import graphql.execution.SubscriptionExecutionStrategy;
@@ -47,6 +48,7 @@
4748
import org.springframework.web.servlet.config.annotation.CorsRegistryWorkaround;
4849

4950
import javax.servlet.MultipartConfigElement;
51+
import java.util.Collections;
5052
import java.util.List;
5153
import java.util.Map;
5254
import java.util.Optional;
@@ -182,6 +184,9 @@ public GraphQLQueryInvoker queryInvoker(ExecutionStrategyProvider executionStrat
182184
.withExecutionStrategyProvider(executionStrategyProvider);
183185

184186
if (instrumentations != null) {
187+
188+
//Metrics instrumentation should be the last to run (we need that from TracingInstrumentation)
189+
Collections.sort(instrumentations, (a,b) -> a instanceof MetricsInstrumentation ? 1 : 0);
185190
builder.with(instrumentations);
186191
}
187192

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

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.oembedler.moon.graphql.boot.metrics;
2+
3+
import graphql.ExecutionResult;
4+
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
5+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
6+
import io.micrometer.core.instrument.MeterRegistry;
7+
import io.micrometer.core.instrument.Timer;
8+
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.concurrent.CompletableFuture;
12+
import java.util.concurrent.TimeUnit;
13+
14+
/**
15+
* @author Bruno Rodrigues
16+
*/
17+
public class MetricsInstrumentation extends TracingInstrumentation {
18+
19+
private MeterRegistry meterRegistry;
20+
21+
private static final String QUERY_TIME_METRIC_NAME = "graphql.timer.query";
22+
private static final String RESOLVER_TIME_METRIC_NAME = "graphql.timer.resolver";
23+
private static final String OPERATION_NAME_TAG = "operationName";
24+
private static final String OPERATION = "operation";
25+
private static final String UNKNOWN_OPERATION_NAME = "unknown";
26+
private static final String PARENT = "parent";
27+
private static final String FIELD = "field";
28+
private static final String TIMER_DESCRIPTION = "Timer that records the time to fetch the data by Operation Name";
29+
30+
private Boolean tracingEnabled;
31+
32+
public MetricsInstrumentation(MeterRegistry meterRegistry, Boolean tracingEnabled) {
33+
this.meterRegistry = meterRegistry;
34+
this.tracingEnabled = tracingEnabled;
35+
}
36+
37+
@Override
38+
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {
39+
40+
if (executionResult.getExtensions() != null && executionResult.getExtensions().containsKey("tracing")) {
41+
42+
Map<String, Object> tracingData = (Map<String, Object>) executionResult.getExtensions().get("tracing");
43+
Timer executionTimer = buildQueryTimer(parameters.getOperation(), "execution");
44+
executionTimer.record((long) tracingData.get("duration"), TimeUnit.NANOSECONDS);
45+
46+
//These next 2 ifs might not run if the document is cached on the document provider
47+
if (tracingData.containsKey("validation") && ((Map<String, Object>)tracingData.get("validation")).containsKey("duration")) {
48+
Timer validationTimer = buildQueryTimer(parameters.getOperation(), "validation");
49+
validationTimer.record((long) ((Map<String, Object>)tracingData.get("validation")).get("duration"), TimeUnit.NANOSECONDS);
50+
}
51+
if (tracingData.containsKey("parsing") && ((Map<String, Object>)tracingData.get("parsing")).containsKey("duration")) {
52+
Timer parsingTimer = buildQueryTimer(parameters.getOperation(), "parsing");
53+
parsingTimer.record((long) ((Map<String, Object>)tracingData.get("parsing")).get("duration"), TimeUnit.NANOSECONDS);
54+
}
55+
56+
if (((Map<String, String>)tracingData.get("execution")).containsKey("resolvers")) {
57+
58+
((List<Map<String, Object>>)((Map<String, Object>)tracingData.get("execution")).get("resolvers")).forEach(field -> {
59+
60+
Timer fieldTimer = buildFieldTimer(parameters.getOperation(), "resolvers", (String)field.get("parentType"), (String)field.get("fieldName"));
61+
fieldTimer.record((long) field.get("duration"), TimeUnit.NANOSECONDS);
62+
63+
});
64+
65+
}
66+
67+
if (!tracingEnabled) {
68+
executionResult.getExtensions().remove("tracing");
69+
}
70+
}
71+
72+
return CompletableFuture.completedFuture(executionResult);
73+
}
74+
75+
private Timer buildQueryTimer(String operationName, String operation) {
76+
return Timer.builder(QUERY_TIME_METRIC_NAME)
77+
.description(TIMER_DESCRIPTION)
78+
.tag(OPERATION_NAME_TAG, operationName != null ? operationName : UNKNOWN_OPERATION_NAME)
79+
.tag(OPERATION, operation)
80+
.register(meterRegistry);
81+
}
82+
83+
private Timer buildFieldTimer(String operationName, String operation, String parent, String field) {
84+
return Timer.builder(RESOLVER_TIME_METRIC_NAME)
85+
.description(TIMER_DESCRIPTION)
86+
.tag(OPERATION_NAME_TAG, operationName != null ? operationName : UNKNOWN_OPERATION_NAME)
87+
.tag(PARENT, parent)
88+
.tag(FIELD, field)
89+
.tag(OPERATION, operation)
90+
.register(meterRegistry);
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.oembedler.moon.graphql.boot.metrics;
2+
3+
import graphql.execution.instrumentation.InstrumentationContext;
4+
import graphql.execution.instrumentation.SimpleInstrumentationContext;
5+
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
6+
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
7+
8+
public class TracingNoResolversInstrumentation extends TracingInstrumentation {
9+
10+
@Override
11+
public InstrumentationContext<Object> beginFieldFetch(InstrumentationFieldFetchParameters parameters) {
12+
return new SimpleInstrumentationContext<>();
13+
}
14+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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.MetricsInstrumentation;
4+
import com.oembedler.moon.graphql.boot.metrics.MetricsInstrumentation;
55
import com.oembedler.moon.graphql.boot.test.AbstractAutoConfigurationTest;
66
import graphql.analysis.MaxQueryComplexityInstrumentation;
77
import graphql.analysis.MaxQueryDepthInstrumentation;

0 commit comments

Comments
 (0)