diff --git a/README.md b/README.md index daa13e89..7a9624aa 100644 --- a/README.md +++ b/README.md @@ -153,14 +153,39 @@ A custom `WebInterceptor` can, for example, change the HTTP request/response hea ### Metrics -If the `spring-boot-starter-actuator` dependency is on the classpath, metrics will be collected for GraphQL queries. +If the `spring-boot-starter-actuator` dependency is on the classpath, metrics will be collected for GraphQL requests. You can see those metrics by exposing the metrics endpoint with `application.properties`: ```properties management.endpoints.web.exposure.include=health,metrics,info ``` + +#### GraphQL Request (timer) -You can then check those metrics at `http://localhost:8080/actuator/metrics/graphql.query`. +A Request metric timer is available at `/actuator/metrics/graphql.request`. +| Tag | Description | Sample values | +|---------|-----------------|--------------------| +| outcome | Request outcome | "SUCCESS", "ERROR" | + + +#### GraphQL Data Fetcher (timer) + +A Data Fetcher metric timer is available at `/actuator/metrics/graphql.datafetcher`. + +| Tag | Description | Sample values | +|---------|-----------------------|--------------------| +| path | data fetcher path | "Query.project" | +| outcome | data fetching outcome | "SUCCESS", "ERROR" | + + +#### GraphQL Error (counter) + +A counter metric counter is available at `/actuator/metrics/graphql.error`. + +| Tag | Description | Sample values | +|-----------|-----------------|-------------------------| +| errorType | error type | "DataFetchingException" | +| errorPath | error JSON Path | "$.project" | ## Sample applications diff --git a/graphql-spring-boot-starter/build.gradle b/graphql-spring-boot-starter/build.gradle index f0c72f2c..48620129 100644 --- a/graphql-spring-boot-starter/build.gradle +++ b/graphql-spring-boot-starter/build.gradle @@ -43,6 +43,7 @@ dependencies { annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.springframework.boot:spring-boot-autoconfigure-processor' + testImplementation project(':spring-graphql-test') testImplementation 'com.fasterxml.jackson.core:jackson-databind' testImplementation 'org.springframework:spring-webflux' testImplementation 'org.springframework:spring-webmvc' @@ -52,6 +53,7 @@ dependencies { testImplementation 'org.apache.tomcat.embed:tomcat-embed-core' testImplementation 'org.apache.tomcat.embed:tomcat-embed-websocket' testImplementation 'org.springframework.boot:spring-boot-actuator-autoconfigure' + testImplementation 'io.micrometer:micrometer-core' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/DefaultGraphQLTagsProvider.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/DefaultGraphQLTagsProvider.java index a488c1e9..8ad8282a 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/DefaultGraphQLTagsProvider.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/DefaultGraphQLTagsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,34 +16,48 @@ package org.springframework.graphql.boot.actuate.metrics; -import graphql.ErrorClassification; -import graphql.ErrorType; +import java.util.List; + import graphql.ExecutionResult; +import graphql.GraphQLError; import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; +import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; +import graphql.schema.DataFetcher; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class DefaultGraphQLTagsProvider implements GraphQLTagsProvider { - private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS"); + private final List contributors; - private static final Tag OUTCOME_ERROR = Tag.of("outcome", "ERROR"); + public DefaultGraphQLTagsProvider(List contributors) { + this.contributors = contributors; + } @Override - public Iterable getTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception) { - Tags tags = Tags.of(Tag.of("query", parameters.getQuery())); - if (result.isDataPresent()) { - tags = tags.and(OUTCOME_SUCCESS); + public Iterable getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception) { + Tags tags = Tags.of(GraphQLTags.executionOutcome(result, exception)); + for (GraphQLTagsContributor contributor : this.contributors) { + tags = tags.and(contributor.getExecutionTags(parameters, result, exception)); } - else { - tags = tags.and(OUTCOME_ERROR); - if (!result.getErrors().isEmpty()) { - ErrorClassification errorClassification = result.getErrors().get(0).getErrorType(); - if (errorClassification instanceof ErrorType) { - tags = tags.and(Tag.of("errorType", ((ErrorType) errorClassification).name())); - } - } + return tags; + } + + @Override + public Iterable getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error) { + Tags tags = Tags.of(GraphQLTags.errorType(error), GraphQLTags.errorPath(error)); + for (GraphQLTagsContributor contributor : this.contributors) { + tags = tags.and(contributor.getErrorTags(parameters, error)); + } + return tags; + } + + @Override + public Iterable getDataFetchingTags(DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception) { + Tags tags = Tags.of(GraphQLTags.dataFetchingOutcome(exception), GraphQLTags.dataFetchingPath(parameters)); + for (GraphQLTagsContributor contributor : this.contributors) { + tags = tags.and(contributor.getDataFetchingTags(dataFetcher, parameters, exception)); } return tags; } diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsAutoConfiguration.java index 580b0929..2d5a91fd 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,11 @@ package org.springframework.graphql.boot.actuate.metrics; +import java.util.stream.Collectors; + import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; @@ -42,8 +45,8 @@ public class GraphQLMetricsAutoConfiguration { @Bean @ConditionalOnMissingBean(GraphQLTagsProvider.class) - public DefaultGraphQLTagsProvider graphQLTagsProvider() { - return new DefaultGraphQLTagsProvider(); + public DefaultGraphQLTagsProvider graphQLTagsProvider(ObjectProvider contributors) { + return new DefaultGraphQLTagsProvider(contributors.orderedStream().collect(Collectors.toList())); } @Bean diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsInstrumentation.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsInstrumentation.java index 91729cef..87079185 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsInstrumentation.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLMetricsInstrumentation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,16 @@ package org.springframework.graphql.boot.actuate.metrics; +import java.util.concurrent.CompletionStage; + import graphql.ExecutionResult; import graphql.execution.instrumentation.InstrumentationContext; import graphql.execution.instrumentation.InstrumentationState; import graphql.execution.instrumentation.SimpleInstrumentation; import graphql.execution.instrumentation.SimpleInstrumentationContext; import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; +import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; +import graphql.schema.DataFetcher; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; @@ -44,40 +48,89 @@ public class GraphQLMetricsInstrumentation extends SimpleInstrumentation { @Override public InstrumentationState createState() { - return new MetricsInstrumentationState(this.registry); + return new RequestMetricsInstrumentationState(this.autoTimer, this.registry); } @Override public InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters) { - MetricsInstrumentationState state = parameters.getInstrumentationState(); - state.startTimer(); + if (this.autoTimer.isEnabled()) { + RequestMetricsInstrumentationState state = parameters.getInstrumentationState(); + state.startTimer(); + return new SimpleInstrumentationContext() { + @Override + public void onCompleted(ExecutionResult result, Throwable exc) { + Iterable tags = tagsProvider.getExecutionTags(parameters, result, exc); + state.tags(tags).stopTimer(); + if (!result.getErrors().isEmpty()) { + result.getErrors().forEach(error -> { + registry.counter("graphql.error", tagsProvider.getErrorTags(parameters, error)).increment(); + }); + } + } + }; + } + return super.beginExecution(parameters); + } - return new SimpleInstrumentationContext() { - @Override - public void onCompleted(ExecutionResult result, Throwable exc) { - Iterable tags = tagsProvider.getTags(parameters, result, exc); - state.stopTimer(autoTimer.builder("graphql.query").tags(tags)); - } - }; + @Override + public DataFetcher instrumentDataFetcher(DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters) { + if (this.autoTimer.isEnabled() && !parameters.isTrivialDataFetcher()) { + return (environment) -> { + Timer.Sample sample = Timer.start(this.registry); + try { + Object value = dataFetcher.get(environment); + if (value instanceof CompletionStage) { + CompletionStage completion = (CompletionStage) value; + return completion.whenComplete((result, error) -> { + recordDataFetcherMetric(sample, dataFetcher, parameters, error); + }); + } + else { + recordDataFetcherMetric(sample, dataFetcher, parameters, null); + return value; + } + + } + catch (Throwable throwable) { + recordDataFetcherMetric(sample, dataFetcher, parameters, throwable); + throw throwable; + } + }; + } + return super.instrumentDataFetcher(dataFetcher, parameters); + } + + private void recordDataFetcherMetric(Timer.Sample sample, DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable throwable) { + Timer.Builder timer = this.autoTimer.builder("graphql.datafetcher"); + timer.tags(this.tagsProvider.getDataFetchingTags(dataFetcher, parameters, throwable)); + sample.stop(timer.register(this.registry)); } - static class MetricsInstrumentationState implements InstrumentationState { + static class RequestMetricsInstrumentationState implements InstrumentationState { private final MeterRegistry registry; - private Timer.Sample timerSample; + private final Timer.Builder timer; - MetricsInstrumentationState(MeterRegistry registry) { + private Timer.Sample sample; + + RequestMetricsInstrumentationState(AutoTimer autoTimer, MeterRegistry registry) { + this.timer = autoTimer.builder("graphql.request"); this.registry = registry; } - public void startTimer() { - this.timerSample = Timer.start(this.registry); + public RequestMetricsInstrumentationState tags(Iterable tags) { + this.timer.tags(tags); + return this; } - public void stopTimer(Timer.Builder timer) { - this.timerSample.stop(timer.register(this.registry)); + public void startTimer() { + this.sample = Timer.start(this.registry); + } + + public void stopTimer() { + this.sample.stop(this.timer.register(this.registry)); } } diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTags.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTags.java new file mode 100644 index 00000000..8e916b66 --- /dev/null +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTags.java @@ -0,0 +1,94 @@ +/* + * Copyright 2020-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.boot.actuate.metrics; + +import java.util.List; + +import graphql.ErrorClassification; +import graphql.ErrorType; +import graphql.ExecutionResult; +import graphql.GraphQLError; +import graphql.execution.ExecutionStepInfo; +import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; +import graphql.schema.GraphQLObjectType; +import io.micrometer.core.instrument.Tag; + +/** + * Factory methods for Tags associated with a GraphQL requests. + * + * @author Brian Clozel + */ +public final class GraphQLTags { + + private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS"); + + private static final Tag OUTCOME_ERROR = Tag.of("outcome", "ERROR"); + + private static final Tag UNKNOWN_ERRORTYPE = Tag.of("errorType", "UNKNOWN"); + + public static Tag executionOutcome(ExecutionResult result, Throwable exception) { + if (exception == null && result.getErrors().isEmpty()) { + return OUTCOME_SUCCESS; + } + else { + return OUTCOME_ERROR; + } + } + + public static Tag errorType(GraphQLError error) { + ErrorClassification errorType = error.getErrorType(); + if (errorType instanceof ErrorType) { + return Tag.of("errorType", ((ErrorType) errorType).name()); + } + return UNKNOWN_ERRORTYPE; + } + + public static Tag errorPath(GraphQLError error) { + StringBuilder builder = new StringBuilder(); + List pathSegments = error.getPath(); + if (!pathSegments.isEmpty()) { + builder.append('$'); + for (Object segment : pathSegments) { + try { + int index = Integer.parseUnsignedInt(segment.toString()); + builder.append("[*]"); + } + catch (NumberFormatException exc) { + builder.append('.'); + builder.append(segment); + } + } + } + return Tag.of("errorPath", builder.toString()); + } + + public static Tag dataFetchingOutcome(Throwable exception) { + return (exception == null) ? OUTCOME_SUCCESS : OUTCOME_ERROR; + } + + public static Tag dataFetchingPath(InstrumentationFieldFetchParameters parameters) { + ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo(); + StringBuilder dataFetchingType = new StringBuilder(); + if (executionStepInfo.hasParent() && + executionStepInfo.getParent().getType() instanceof GraphQLObjectType) { + dataFetchingType.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName()); + dataFetchingType.append('.'); + } + dataFetchingType.append(executionStepInfo.getPath().getSegmentName()); + return Tag.of("path", dataFetchingType.toString()); + } +} diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsContributor.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsContributor.java new file mode 100644 index 00000000..8da71ed9 --- /dev/null +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsContributor.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.boot.actuate.metrics; + +import graphql.ExecutionResult; +import graphql.GraphQLError; +import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; +import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; +import graphql.schema.DataFetcher; +import io.micrometer.core.instrument.Tag; + +public interface GraphQLTagsContributor { + + Iterable getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception); + + Iterable getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error); + + Iterable getDataFetchingTags(DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception); +} diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsProvider.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsProvider.java index 0e410a80..79e30d6c 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsProvider.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,18 @@ package org.springframework.graphql.boot.actuate.metrics; import graphql.ExecutionResult; +import graphql.GraphQLError; import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; +import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; +import graphql.schema.DataFetcher; import io.micrometer.core.instrument.Tag; public interface GraphQLTagsProvider { - Iterable getTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception); + Iterable getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, Throwable exception); + + Iterable getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error); + + Iterable getDataFetchingTags(DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters, Throwable exception); + } diff --git a/graphql-spring-boot-starter/src/test/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsTests.java b/graphql-spring-boot-starter/src/test/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsTests.java new file mode 100644 index 00000000..29823bef --- /dev/null +++ b/graphql-spring-boot-starter/src/test/java/org/springframework/graphql/boot/actuate/metrics/GraphQLTagsTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.boot.actuate.metrics; + +import java.util.Arrays; + +import graphql.ErrorType; +import graphql.ExecutionResultImpl; +import graphql.GraphQLError; +import graphql.GraphqlErrorBuilder; +import io.micrometer.core.instrument.Tag; +import org.junit.jupiter.api.Test; + +import org.springframework.graphql.test.tester.TestExecutionResult; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraphQLTags} + * + * @author Brian Clozel + */ +class GraphQLTagsTests { + + @Test + void executionOutcomeShouldSucceed() { + Tag outcomeTag = GraphQLTags.executionOutcome(new TestExecutionResult(), null); + assertThat(outcomeTag.getValue()).isEqualTo("SUCCESS"); + } + + @Test + void executionOutcomeShouldErrorWhenExceptionThrown() { + Tag outcomeTag = GraphQLTags.executionOutcome(new TestExecutionResult(), new IllegalArgumentException("test error")); + assertThat(outcomeTag.getValue()).isEqualTo("ERROR"); + } + + @Test + void executionOutcomeShouldErrorWhenResponseErrors() { + ExecutionResultImpl.Builder builder = new ExecutionResultImpl.Builder(); + builder.addError(GraphqlErrorBuilder.newError().message("Invalid query").build()); + Tag outcomeTag = GraphQLTags.executionOutcome(builder.build(), null); + assertThat(outcomeTag.getValue()).isEqualTo("ERROR"); + } + + @Test + void errorTypeShouldBeDefinedIfPresent() { + GraphQLError error = GraphqlErrorBuilder.newError().errorType(ErrorType.DataFetchingException).message("test error").build(); + Tag errorTypeTag = GraphQLTags.errorType(error); + assertThat(errorTypeTag.getValue()).isEqualTo("DataFetchingException"); + } + + @Test + void errorPathShouldUseJsonPathFormat() { + GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("project", "name")).message("test error").build(); + Tag errorPathTag = GraphQLTags.errorPath(error); + assertThat(errorPathTag.getValue()).isEqualTo("$.project.name"); + } + + @Test + void errorPathShouldUseJsonPathFormatForIndices() { + GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("issues", "42", "title")).message("test error").build(); + Tag errorPathTag = GraphQLTags.errorPath(error); + assertThat(errorPathTag.getValue()).isEqualTo("$.issues[*].title"); + } + + @Test + void dataFetchingOutcomeShouldBeSuccessfulIfNoException() { + Tag fetchingOutcomeTag = GraphQLTags.dataFetchingOutcome(null); + assertThat(fetchingOutcomeTag.getValue()).isEqualTo("SUCCESS"); + } + + @Test + void dataFetchingOutcomeShouldBeErrorIfException() { + Tag fetchingOutcomeTag = GraphQLTags.dataFetchingOutcome(new IllegalStateException("error state")); + assertThat(fetchingOutcomeTag.getValue()).isEqualTo("ERROR"); + } + +} \ No newline at end of file diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/ReactorDataFetcherAdapter.java b/spring-graphql/src/main/java/org/springframework/graphql/support/ReactorDataFetcherAdapter.java index 5da3a79e..42f2e739 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/ReactorDataFetcherAdapter.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/ReactorDataFetcherAdapter.java @@ -123,8 +123,6 @@ class ReactorDataFetcherAdapter implements DataFetcher { Method method = ClassUtils.getMethod(dataFetcher.getClass(), "get", DataFetchingEnvironment.class); method = ClassUtils.getMostSpecificMethod(method, dataFetcher.getClass()); - Class returnType = method.getReturnType(); - System.out.println(returnType.getName()); dataFetcher = new ReactorDataFetcherAdapter(dataFetcher, parent.getName().equals("Subscription")); codeRegistry.dataFetcher(parent, fieldDefinition, dataFetcher);