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 5ff5ca08b4c..5bc5625ee72 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 @@ -62,6 +62,8 @@ 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(); @@ -90,6 +92,10 @@ public class MetricsProperties { return this.data; } + public Graphql getGraphql() { + return this.graphql; + } + public System getSystem() { return this.system; } @@ -268,6 +274,20 @@ 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 new file mode 100644 index 00000000000..c9ad464b6fa --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfiguration.java @@ -0,0 +1,70 @@ +/* + * 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.boot.actuate.autoconfigure.metrics.graphql; + +import java.util.stream.Collectors; + +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.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.AutoConfigureAfter; +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.context.annotation.Configuration; +import org.springframework.graphql.execution.GraphQlSource; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring + * GraphQL endpoints. + * + * @author Brian Clozel + * @since 2.7.0 + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter({ 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().collect(Collectors.toList())); + } + + @Bean + public GraphQlMetricsInstrumentation graphQlMetricsInstrumentation(MeterRegistry meterRegistry, + GraphQlTagsProvider tagsProvider, MetricsProperties properties) { + return new GraphQlMetricsInstrumentation(meterRegistry, tagsProvider, properties.getGraphql().getAutotime()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index dd6eeb94970..a5027fee193 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -68,6 +68,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.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 new file mode 100644 index 00000000000..5c7c502dbd6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/graphql/GraphQlMetricsAutoConfigurationTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012-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.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; + } + + } + +}