From 9948fc3e3967e748d029ecacb88ddd16c7f778d2 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 19 Oct 2022 22:06:40 +0200 Subject: [PATCH] Use GraphQL Observation instrumentation This commit migrates the existing metrics support (added in #29140) to the new `Observation` instrumentation contributed in spring-projects/spring-graphql#501. We cannot have a smoother migration path here as the instrumentation does not use the same context information for metadata extraction. Closes gh-32794 --- .../metrics/MetricsProperties.java | 20 -- .../GraphQlMetricsAutoConfiguration.java | 68 ------- .../GraphQlObservationAutoConfiguration.java | 53 +++++ .../graphql/package-info.java | 4 +- ...itional-spring-configuration-metadata.json | 26 +++ ...ot.autoconfigure.AutoConfiguration.imports | 2 +- .../GraphQlMetricsAutoConfigurationTests.java | 98 ---------- ...phQlObservationAutoConfigurationTests.java | 71 +++++++ .../graphql/DefaultGraphQlTagsProvider.java | 77 -------- .../GraphQlMetricsInstrumentation.java | 166 ---------------- .../actuate/metrics/graphql/GraphQlTags.java | 101 ---------- .../graphql/GraphQlTagsContributor.java | 43 ---- .../metrics/graphql/GraphQlTagsProvider.java | 42 ---- .../actuate/metrics/graphql/package-info.java | 20 -- .../GraphQlMetricsInstrumentationTests.java | 185 ------------------ .../metrics/graphql/GraphQlTagsTests.java | 95 --------- 16 files changed, 153 insertions(+), 918 deletions(-) delete mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfiguration.java rename spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/{metrics => observation}/graphql/package-info.java (82%) delete mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfigurationTests.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfigurationTests.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/DefaultGraphQlTagsProvider.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentation.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTags.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsContributor.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsProvider.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/package-info.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentationTests.java delete mode 100644 spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java index 3999d16471..f06176bb95 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -63,8 +63,6 @@ public class MetricsProperties { private final Data data = new Data(); - private final Graphql graphql = new Graphql(); - private final System system = new System(); private final Distribution distribution = new Distribution(); @@ -93,10 +91,6 @@ public class MetricsProperties { return this.data; } - public Graphql getGraphql() { - return this.graphql; - } - public System getSystem() { return this.system; } @@ -249,20 +243,6 @@ public class MetricsProperties { } - public static class Graphql { - - /** - * Auto-timed queries settings. - */ - @NestedConfigurationProperty - private final AutoTimeProperties autotime = new AutoTimeProperties(); - - public AutoTimeProperties getAutotime() { - return this.autotime; - } - - } - public static class System { private final Diskspace diskspace = new Diskspace(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfiguration.java deleted file mode 100644 index 83c282c5aa..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfiguration.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012-2022 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.boot.actuate.autoconfigure.metrics.graphql; - -import graphql.GraphQL; -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.MetricsProperties; -import org.springframework.boot.actuate.autoconfigure.metrics.PropertiesAutoTimer; -import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; -import org.springframework.boot.actuate.metrics.graphql.DefaultGraphQlTagsProvider; -import org.springframework.boot.actuate.metrics.graphql.GraphQlMetricsInstrumentation; -import org.springframework.boot.actuate.metrics.graphql.GraphQlTagsContributor; -import org.springframework.boot.actuate.metrics.graphql.GraphQlTagsProvider; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.graphql.execution.GraphQlSource; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring - * GraphQL endpoints. - * - * @author Brian Clozel - * @since 2.7.0 - */ -@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class, - SimpleMetricsExportAutoConfiguration.class }) -@ConditionalOnBean(MeterRegistry.class) -@ConditionalOnClass({ GraphQL.class, GraphQlSource.class }) -@EnableConfigurationProperties(MetricsProperties.class) -public class GraphQlMetricsAutoConfiguration { - - @Bean - @ConditionalOnMissingBean(GraphQlTagsProvider.class) - public DefaultGraphQlTagsProvider graphQlTagsProvider(ObjectProvider contributors) { - return new DefaultGraphQlTagsProvider(contributors.orderedStream().toList()); - } - - @Bean - public GraphQlMetricsInstrumentation graphQlMetricsInstrumentation(MeterRegistry meterRegistry, - GraphQlTagsProvider tagsProvider, MetricsProperties properties) { - return new GraphQlMetricsInstrumentation(meterRegistry, tagsProvider, - new PropertiesAutoTimer(properties.getGraphql().getAutotime())); - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfiguration.java new file mode 100644 index 0000000000..0885ad5f42 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2022 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.boot.actuate.autoconfigure.observation.graphql; + +import graphql.GraphQL; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; + +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.graphql.execution.GraphQlSource; +import org.springframework.graphql.observation.GraphQlObservationInstrumentation; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring + * GraphQL endpoints. + * + * @author Brian Clozel + * @since 3.0.0 + */ +@AutoConfiguration(after = ObservationAutoConfiguration.class) +@ConditionalOnBean(ObservationRegistry.class) +@ConditionalOnClass({ GraphQL.class, GraphQlSource.class, Observation.class }) +@SuppressWarnings("removal") +public class GraphQlObservationAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public GraphQlObservationInstrumentation graphQlObservationInstrumentation( + ObservationRegistry observationRegistry) { + return new GraphQlObservationInstrumentation(observationRegistry); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/package-info.java similarity index 82% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/package-info.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/package-info.java index 471595d95f..38b92cddbe 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/package-info.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/package-info.java @@ -15,6 +15,6 @@ */ /** - * Auto-configuration for Spring GraphQL metrics. + * Auto-configuration for Spring GraphQL observations. */ -package org.springframework.boot.actuate.autoconfigure.metrics.graphql; +package org.springframework.boot.actuate.autoconfigure.observation.graphql; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 85188e5535..886cb73afe 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1885,6 +1885,32 @@ "replacement": "management.wavefront.uri" } }, + { + "name": "management.metrics.graphql.autotime.enabled", + "description": "Whether to automatically time web client requests.", + "defaultValue": true, + "deprecation": { + "level": "error", + "reason": "Should be applied at the ObservationRegistry level." + } + }, + { + "name": "management.metrics.graphql.autotime.percentiles", + "description": "Computed non-aggregable percentiles to publish.", + "deprecation": { + "level": "error", + "reason": "Should be applied at the ObservationRegistry level." + } + }, + { + "name": "management.metrics.graphql.autotime.percentiles-histogram", + "description": "Whether percentile histograms should be published.", + "defaultValue": false, + "deprecation": { + "level": "error", + "reason": "Should be applied at the ObservationRegistry level." + } + }, { "name": "management.metrics.mongo.command.enabled", "description": "Whether to enable Mongo client command metrics.", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 1e75220603..967582a86d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -67,7 +67,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetri org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver.StackdriverMetricsExportAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdMetricsExportAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration -org.springframework.boot.actuate.autoconfigure.metrics.graphql.GraphQlMetricsAutoConfiguration +org.springframework.boot.actuate.autoconfigure.observation.graphql.GraphQlObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.integration.IntegrationMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfigurationTests.java deleted file mode 100644 index 22a9063ef8..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfigurationTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012-2022 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.boot.actuate.autoconfigure.metrics.graphql; - -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 org.junit.jupiter.api.Test; - -import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; -import org.springframework.boot.actuate.metrics.graphql.DefaultGraphQlTagsProvider; -import org.springframework.boot.actuate.metrics.graphql.GraphQlMetricsInstrumentation; -import org.springframework.boot.actuate.metrics.graphql.GraphQlTagsProvider; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link GraphQlMetricsAutoConfiguration}. - * - * @author Brian Clozel - */ -class GraphQlMetricsAutoConfigurationTests { - - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple()) - .withConfiguration(AutoConfigurations.of(GraphQlMetricsAutoConfiguration.class)); - - @Test - void backsOffWhenMeterRegistryIsMissing() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(GraphQlMetricsAutoConfiguration.class)) - .run((context) -> assertThat(context).doesNotHaveBean(DefaultGraphQlTagsProvider.class) - .doesNotHaveBean(GraphQlMetricsInstrumentation.class)); - } - - @Test - void definesTagsProviderAndInstrumentationWhenMeterRegistryIsPresent() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DefaultGraphQlTagsProvider.class) - .hasSingleBean(GraphQlMetricsInstrumentation.class)); - } - - @Test - void tagsProviderBacksOffIfAlreadyPresent() { - this.contextRunner.withUserConfiguration(TagsProviderConfiguration.class).run((context) -> assertThat(context) - .doesNotHaveBean(DefaultGraphQlTagsProvider.class).hasSingleBean(TestGraphQlTagsProvider.class)); - } - - @Configuration(proxyBeanMethods = false) - static class TagsProviderConfiguration { - - @Bean - TestGraphQlTagsProvider tagsProvider() { - return new TestGraphQlTagsProvider(); - } - - } - - static class TestGraphQlTagsProvider implements GraphQlTagsProvider { - - @Override - public Iterable getExecutionTags(InstrumentationExecutionParameters parameters, ExecutionResult result, - Throwable exception) { - return null; - } - - @Override - public Iterable getErrorTags(InstrumentationExecutionParameters parameters, GraphQLError error) { - return null; - } - - @Override - public Iterable getDataFetchingTags(DataFetcher dataFetcher, - InstrumentationFieldFetchParameters parameters, Throwable exception) { - return null; - } - - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfigurationTests.java new file mode 100644 index 0000000000..326a8e2adf --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/graphql/GraphQlObservationAutoConfigurationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2022 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.boot.actuate.autoconfigure.observation.graphql; + +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.tck.TestObservationRegistry; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.graphql.observation.GraphQlObservationInstrumentation; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraphQlObservationAutoConfiguration}. + * + * @author Brian Clozel + */ +class GraphQlObservationAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withBean(TestObservationRegistry.class, TestObservationRegistry::create) + .withConfiguration(AutoConfigurations.of(GraphQlObservationAutoConfiguration.class)); + + @Test + void backsOffWhenObservationRegistryIsMissing() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GraphQlObservationAutoConfiguration.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GraphQlObservationInstrumentation.class)); + } + + @Test + void definesInstrumentationWhenObservationRegistryIsPresent() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(GraphQlObservationInstrumentation.class)); + } + + @Test + void instrumentationBacksOffIfAlreadyPresent() { + this.contextRunner.withUserConfiguration(InstrumentationConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(GraphQlObservationInstrumentation.class) + .hasBean("customInstrumentation")); + } + + @Configuration(proxyBeanMethods = false) + static class InstrumentationConfiguration { + + @Bean + GraphQlObservationInstrumentation customInstrumentation(ObservationRegistry registry) { + return new GraphQlObservationInstrumentation(registry); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/DefaultGraphQlTagsProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/DefaultGraphQlTagsProvider.java deleted file mode 100644 index d945adb58c..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/DefaultGraphQlTagsProvider.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2020-2022 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.boot.actuate.metrics.graphql; - -import java.util.Collections; -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; - -/** - * Default implementation for {@link GraphQlTagsProvider}. - * - * @author Brian Clozel - * @since 2.7.0 - */ -public class DefaultGraphQlTagsProvider implements GraphQlTagsProvider { - - private final List contributors; - - public DefaultGraphQlTagsProvider(List contributors) { - this.contributors = contributors; - } - - public DefaultGraphQlTagsProvider() { - this(Collections.emptyList()); - } - - @Override - 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)); - } - 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/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentation.java deleted file mode 100644 index f03e3d33d8..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentation.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2020-2022 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.boot.actuate.metrics.graphql; - -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicLong; - -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.InstrumentationCreateStateParameters; -import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; -import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import graphql.schema.DataFetcher; -import io.micrometer.core.instrument.DistributionSummary; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.Timer; - -import org.springframework.boot.actuate.metrics.AutoTimer; - -/** - * Micrometer-based {@link SimpleInstrumentation}. - * - * @author Brian Clozel - * @since 2.7.0 - */ -public class GraphQlMetricsInstrumentation extends SimpleInstrumentation { - - private final MeterRegistry registry; - - private final GraphQlTagsProvider tagsProvider; - - private final AutoTimer autoTimer; - - private final DistributionSummary dataFetchingSummary; - - public GraphQlMetricsInstrumentation(MeterRegistry registry, GraphQlTagsProvider tagsProvider, - AutoTimer autoTimer) { - this.registry = registry; - this.tagsProvider = tagsProvider; - this.autoTimer = autoTimer; - this.dataFetchingSummary = DistributionSummary.builder("graphql.request.datafetch.count").baseUnit("calls") - .description("Count of DataFetcher calls per request.").register(this.registry); - } - - @Override - public InstrumentationState createState(InstrumentationCreateStateParameters parameters) { - return new RequestMetricsInstrumentationState(this.autoTimer, this.registry); - } - - @Override - public InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters, - InstrumentationState state) { - if (this.autoTimer.isEnabled() && state instanceof RequestMetricsInstrumentationState instrumentationState) { - instrumentationState.startTimer(); - return new SimpleInstrumentationContext() { - @Override - public void onCompleted(ExecutionResult result, Throwable exc) { - Iterable tags = GraphQlMetricsInstrumentation.this.tagsProvider.getExecutionTags(parameters, - result, exc); - instrumentationState.tags(tags).stopTimer(); - if (!result.getErrors().isEmpty()) { - result.getErrors() - .forEach((error) -> GraphQlMetricsInstrumentation.this.registry.counter("graphql.error", - GraphQlMetricsInstrumentation.this.tagsProvider.getErrorTags(parameters, error)) - .increment()); - } - GraphQlMetricsInstrumentation.this.dataFetchingSummary - .record(instrumentationState.getDataFetchingCount()); - } - }; - } - return super.beginExecution(parameters); - } - - @Override - public DataFetcher instrumentDataFetcher(DataFetcher dataFetcher, - InstrumentationFieldFetchParameters parameters, InstrumentationState state) { - if (this.autoTimer.isEnabled() && !parameters.isTrivialDataFetcher() - && state instanceof RequestMetricsInstrumentationState instrumentationState) { - return (environment) -> { - Timer.Sample sample = Timer.start(this.registry); - try { - Object value = dataFetcher.get(environment); - if (value instanceof CompletionStage completion) { - return completion.whenComplete((result, error) -> recordDataFetcherMetric(sample, - instrumentationState, dataFetcher, parameters, error)); - } - else { - recordDataFetcherMetric(sample, instrumentationState, dataFetcher, parameters, null); - return value; - } - } - catch (Throwable throwable) { - recordDataFetcherMetric(sample, instrumentationState, dataFetcher, parameters, throwable); - throw throwable; - } - }; - } - return super.instrumentDataFetcher(dataFetcher, parameters, state); - } - - private void recordDataFetcherMetric(Timer.Sample sample, RequestMetricsInstrumentationState instrumentationState, - 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)); - instrumentationState.incrementDataFetchingCount(); - } - - static class RequestMetricsInstrumentationState implements InstrumentationState { - - private final MeterRegistry registry; - - private final Timer.Builder timer; - - private Timer.Sample sample; - - private final AtomicLong dataFetchingCount = new AtomicLong(); - - RequestMetricsInstrumentationState(AutoTimer autoTimer, MeterRegistry registry) { - this.timer = autoTimer.builder("graphql.request"); - this.registry = registry; - } - - RequestMetricsInstrumentationState tags(Iterable tags) { - this.timer.tags(tags); - return this; - } - - void startTimer() { - this.sample = Timer.start(this.registry); - } - - void stopTimer() { - this.sample.stop(this.timer.register(this.registry)); - } - - void incrementDataFetchingCount() { - this.dataFetchingCount.incrementAndGet(); - } - - long getDataFetchingCount() { - return this.dataFetchingCount.get(); - } - - } - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTags.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTags.java deleted file mode 100644 index eb8cd10a88..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTags.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020-2022 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.boot.actuate.metrics.graphql; - -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; - -import org.springframework.util.CollectionUtils; - -/** - * Factory methods for Tags associated with a GraphQL request. - * - * @author Brian Clozel - * @since 2.7.0 - */ -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_ERROR_TYPE = Tag.of("error.type", "UNKNOWN"); - - private GraphQlTags() { - - } - - 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("error.type", ((ErrorType) errorType).name()); - } - return UNKNOWN_ERROR_TYPE; - } - - public static Tag errorPath(GraphQLError error) { - StringBuilder builder = new StringBuilder(); - List pathSegments = error.getPath(); - if (!CollectionUtils.isEmpty(pathSegments)) { - builder.append('$'); - for (Object segment : pathSegments) { - try { - Integer.parseUnsignedInt(segment.toString()); - builder.append("[*]"); - } - catch (NumberFormatException exc) { - builder.append('.'); - builder.append(segment); - } - } - } - return Tag.of("error.path", builder.toString()); - } - - public static Tag dataFetchingOutcome(Throwable exception) { - return (exception != null) ? OUTCOME_ERROR : OUTCOME_SUCCESS; - } - - public static Tag dataFetchingPath(InstrumentationFieldFetchParameters parameters) { - ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo(); - StringBuilder dataFetchingPath = new StringBuilder(); - if (executionStepInfo.hasParent() && executionStepInfo.getParent().getType() instanceof GraphQLObjectType) { - dataFetchingPath.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName()); - dataFetchingPath.append('.'); - } - dataFetchingPath.append(executionStepInfo.getPath().getSegmentName()); - return Tag.of("path", dataFetchingPath.toString()); - } - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsContributor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsContributor.java deleted file mode 100644 index 55b97055ab..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsContributor.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2020-2022 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.boot.actuate.metrics.graphql; - -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; - -/** - * A contributor of {@link Tag Tags} for Spring GraphQL-based request handling. Typically, - * used by a {@link GraphQlTagsProvider} to provide tags in addition to its defaults. - * - * @author Brian Clozel - * @since 2.7.0 - */ -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/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsProvider.java deleted file mode 100644 index a60ee6df07..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020-2022 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.boot.actuate.metrics.graphql; - -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; - -/** - * Provides {@link Tag Tags} for Spring GraphQL-based request handling. - * - * @author Brian Clozel - * @since 2.7.0 - */ -public interface GraphQlTagsProvider { - - 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/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/package-info.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/package-info.java deleted file mode 100644 index dd70ecba23..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/graphql/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020-2022 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. - */ - -/** - * Provides instrumentation support for Spring GraphQL. - */ -package org.springframework.boot.actuate.metrics.graphql; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentationTests.java deleted file mode 100644 index efe30504e1..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlMetricsInstrumentationTests.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2012-2022 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.boot.actuate.metrics.graphql; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -import graphql.ExecutionInput; -import graphql.ExecutionResult; -import graphql.ExecutionResultImpl; -import graphql.execution.instrumentation.InstrumentationContext; -import graphql.execution.instrumentation.InstrumentationState; -import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters; -import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; -import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.DataFetchingEnvironmentImpl; -import graphql.schema.GraphQLSchema; -import graphql.schema.idl.SchemaGenerator; -import io.micrometer.core.instrument.DistributionSummary; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.MockClock; -import io.micrometer.core.instrument.Timer; -import io.micrometer.core.instrument.simple.SimpleConfig; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.actuate.metrics.AutoTimer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link GraphQlMetricsInstrumentation}. - * - * @author Brian Clozel - */ -@SuppressWarnings("unchecked") -class GraphQlMetricsInstrumentationTests { - - private final ExecutionInput input = ExecutionInput.newExecutionInput("{greeting}").build(); - - private final GraphQLSchema schema = SchemaGenerator.createdMockedSchema("type Query { greeting: String }"); - - private MeterRegistry registry; - - private GraphQlMetricsInstrumentation instrumentation; - - private InstrumentationState state; - - private InstrumentationExecutionParameters parameters; - - @BeforeEach - void setup() { - this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); - this.instrumentation = new GraphQlMetricsInstrumentation(this.registry, mock(GraphQlTagsProvider.class), - AutoTimer.ENABLED); - this.state = this.instrumentation - .createState(new InstrumentationCreateStateParameters(this.schema, this.input)); - this.parameters = new InstrumentationExecutionParameters(this.input, this.schema, this.state); - } - - @Test - void shouldRecordTimerWhenResult() { - InstrumentationContext execution = this.instrumentation.beginExecution(this.parameters, - this.state); - ExecutionResult result = new ExecutionResultImpl("Hello", null); - execution.onCompleted(result, null); - - Timer timer = this.registry.find("graphql.request").timer(); - assertThat(timer).isNotNull(); - assertThat(timer.count()).isEqualTo(1); - } - - @Test - void shouldRecordDataFetchingCount() throws Exception { - InstrumentationContext execution = this.instrumentation.beginExecution(this.parameters, - this.state); - ExecutionResult result = new ExecutionResultImpl("Hello", null); - - DataFetcher dataFetcher = mock(DataFetcher.class); - given(dataFetcher.get(any())).willReturn("Hello"); - InstrumentationFieldFetchParameters fieldFetchParameters = mockFieldFetchParameters(false); - - DataFetcher instrumented = this.instrumentation.instrumentDataFetcher(dataFetcher, fieldFetchParameters, - this.state); - DataFetchingEnvironment environment = DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build(); - instrumented.get(environment); - - execution.onCompleted(result, null); - - DistributionSummary summary = this.registry.find("graphql.request.datafetch.count").summary(); - assertThat(summary).isNotNull(); - assertThat(summary.count()).isEqualTo(1); - } - - @Test - void shouldRecordDataFetchingMetricWhenSuccess() throws Exception { - DataFetcher dataFetcher = mock(DataFetcher.class); - given(dataFetcher.get(any())).willReturn("Hello"); - InstrumentationFieldFetchParameters fieldFetchParameters = mockFieldFetchParameters(false); - - DataFetcher instrumented = this.instrumentation.instrumentDataFetcher(dataFetcher, fieldFetchParameters, - this.state); - DataFetchingEnvironment environment = DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build(); - instrumented.get(environment); - - Timer timer = this.registry.find("graphql.datafetcher").timer(); - assertThat(timer).isNotNull(); - assertThat(timer.count()).isEqualTo(1); - } - - @Test - void shouldRecordDataFetchingMetricWhenSuccessCompletionStage() throws Exception { - DataFetcher> dataFetcher = mock(DataFetcher.class); - given(dataFetcher.get(any())).willReturn(CompletableFuture.completedFuture("Hello")); - InstrumentationFieldFetchParameters fieldFetchParameters = mockFieldFetchParameters(false); - - DataFetcher instrumented = this.instrumentation.instrumentDataFetcher(dataFetcher, fieldFetchParameters, - this.state); - DataFetchingEnvironment environment = DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build(); - instrumented.get(environment); - - Timer timer = this.registry.find("graphql.datafetcher").timer(); - assertThat(timer).isNotNull(); - assertThat(timer.count()).isEqualTo(1); - } - - @Test - void shouldRecordDataFetchingMetricWhenError() throws Exception { - DataFetcher> dataFetcher = mock(DataFetcher.class); - given(dataFetcher.get(any())).willThrow(new IllegalStateException("test")); - InstrumentationFieldFetchParameters fieldFetchParameters = mockFieldFetchParameters(false); - - DataFetcher instrumented = this.instrumentation.instrumentDataFetcher(dataFetcher, fieldFetchParameters, - this.state); - DataFetchingEnvironment environment = DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build(); - assertThatThrownBy(() -> instrumented.get(environment)).isInstanceOf(IllegalStateException.class); - - Timer timer = this.registry.find("graphql.datafetcher").timer(); - assertThat(timer).isNotNull(); - assertThat(timer.count()).isEqualTo(1); - } - - @Test - void shouldNotRecordDataFetchingMetricWhenTrivial() throws Exception { - DataFetcher dataFetcher = mock(DataFetcher.class); - given(dataFetcher.get(any())).willReturn("Hello"); - InstrumentationFieldFetchParameters fieldFetchParameters = mockFieldFetchParameters(true); - - DataFetcher instrumented = this.instrumentation.instrumentDataFetcher(dataFetcher, fieldFetchParameters, - this.state); - DataFetchingEnvironment environment = DataFetchingEnvironmentImpl.newDataFetchingEnvironment().build(); - instrumented.get(environment); - - Timer timer = this.registry.find("graphql.datafetcher").timer(); - assertThat(timer).isNull(); - } - - private InstrumentationFieldFetchParameters mockFieldFetchParameters(boolean isTrivial) { - InstrumentationFieldFetchParameters fieldFetchParameters = mock(InstrumentationFieldFetchParameters.class); - given(fieldFetchParameters.isTrivialDataFetcher()).willReturn(isTrivial); - return fieldFetchParameters; - } - -} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsTests.java deleted file mode 100644 index c9d04d2732..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/graphql/GraphQlTagsTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2012-2022 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.boot.actuate.metrics.graphql; - -import java.util.Arrays; - -import graphql.ErrorType; -import graphql.ExecutionResult; -import graphql.ExecutionResultImpl; -import graphql.GraphQLError; -import graphql.GraphqlErrorBuilder; -import io.micrometer.core.instrument.Tag; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link GraphQlTags}. - * - * @author Brian Clozel - */ -class GraphQlTagsTests { - - @Test - void executionOutcomeShouldSucceed() { - ExecutionResult result = ExecutionResultImpl.newExecutionResult().build(); - Tag outcomeTag = GraphQlTags.executionOutcome(result, null); - assertThat(outcomeTag).isEqualTo(Tag.of("outcome", "SUCCESS")); - } - - @Test - void executionOutcomeShouldErrorWhenExceptionThrown() { - ExecutionResult result = ExecutionResultImpl.newExecutionResult().build(); - Tag tag = GraphQlTags.executionOutcome(result, new IllegalArgumentException("test error")); - assertThat(tag).isEqualTo(Tag.of("outcome", "ERROR")); - } - - @Test - void executionOutcomeShouldErrorWhenResponseErrors() { - GraphQLError error = GraphqlErrorBuilder.newError().message("Invalid query").build(); - Tag tag = GraphQlTags.executionOutcome(ExecutionResultImpl.newExecutionResult().addError(error).build(), null); - assertThat(tag).isEqualTo(Tag.of("outcome", "ERROR")); - } - - @Test - void errorTypeShouldBeDefinedIfPresent() { - GraphQLError error = GraphqlErrorBuilder.newError().errorType(ErrorType.DataFetchingException) - .message("test error").build(); - Tag errorTypeTag = GraphQlTags.errorType(error); - assertThat(errorTypeTag).isEqualTo(Tag.of("error.type", "DataFetchingException")); - } - - @Test - void errorPathShouldUseJsonPathFormat() { - GraphQLError error = GraphqlErrorBuilder.newError().path(Arrays.asList("project", "name")).message("test error") - .build(); - Tag errorPathTag = GraphQlTags.errorPath(error); - assertThat(errorPathTag).isEqualTo(Tag.of("error.path", "$.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).isEqualTo(Tag.of("error.path", "$.issues[*].title")); - } - - @Test - void dataFetchingOutcomeShouldBeSuccessfulIfNoException() { - Tag fetchingOutcomeTag = GraphQlTags.dataFetchingOutcome(null); - assertThat(fetchingOutcomeTag).isEqualTo(Tag.of("outcome", "SUCCESS")); - } - - @Test - void dataFetchingOutcomeShouldBeErrorIfException() { - Tag fetchingOutcomeTag = GraphQlTags.dataFetchingOutcome(new IllegalStateException("error state")); - assertThat(fetchingOutcomeTag).isEqualTo(Tag.of("outcome", "ERROR")); - } - -}