From 593b5311e96841b06139e9827d4083078e7c29b9 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 19 Dec 2022 12:13:14 +0100 Subject: [PATCH] Configure GraphQL web propagation for tracing As of spring-projects/spring-graphql#547, Spring GraphQL introduced a `PropagationWebGraphQlInterceptor` that propagates the incoming tracing information in HTTP request headers into the GraphQL context. This commit auto-configures the propagation interceptor if the application exposes a GraphQL HTTP endpoint and if it is configured for Tracing support. Fixes gh-33542 --- .../GraphQlObservationAutoConfiguration.java | 21 ++++++++++ ...phQlObservationAutoConfigurationTests.java | 40 +++++++++++++++++++ 2 files changed, 61 insertions(+) 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 index 6ef41b4246b8..e38b4959e570 100644 --- 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 @@ -19,6 +19,7 @@ import graphql.GraphQL; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; +import io.micrometer.tracing.propagation.Propagator; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; @@ -28,10 +29,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.graphql.execution.GraphQlSource; import org.springframework.graphql.observation.DataFetcherObservationConvention; import org.springframework.graphql.observation.ExecutionRequestObservationConvention; import org.springframework.graphql.observation.GraphQlObservationInstrumentation; +import org.springframework.graphql.observation.PropagationWebGraphQlInterceptor; +import org.springframework.graphql.server.WebGraphQlHandler; /** * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring @@ -55,4 +61,19 @@ public GraphQlObservationInstrumentation graphQlObservationInstrumentation(Obser dataFetcherConvention.getIfAvailable()); } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(Propagator.class) + @ConditionalOnBean(WebGraphQlHandler.class) + static class TracingObservationConfiguration { + + @Bean + @ConditionalOnBean(Propagator.class) + @ConditionalOnMissingBean + @Order(Ordered.HIGHEST_PRECEDENCE + 1) + public PropagationWebGraphQlInterceptor propagationWebGraphQlInterceptor(Propagator propagator) { + return new PropagationWebGraphQlInterceptor(propagator); + } + + } + } 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 index cba3b691d92f..a35161368cd0 100644 --- 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 @@ -18,6 +18,7 @@ import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; +import io.micrometer.tracing.propagation.Propagator; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -27,8 +28,11 @@ import org.springframework.graphql.observation.DefaultDataFetcherObservationConvention; import org.springframework.graphql.observation.DefaultExecutionRequestObservationConvention; import org.springframework.graphql.observation.GraphQlObservationInstrumentation; +import org.springframework.graphql.observation.PropagationWebGraphQlInterceptor; +import org.springframework.graphql.server.WebGraphQlHandler; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link GraphQlObservationAutoConfiguration}. @@ -72,6 +76,24 @@ void instrumentationUsesCustomConventionsIfAvailable() { }); } + @Test + void propagationInterceptorNotContributedWhenPropagatorIsMissing() { + this.contextRunner.withUserConfiguration(WebGraphQlConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean(PropagationWebGraphQlInterceptor.class)); + } + + @Test + void propagationInterceptorNotContributedWhenNotWebApplication() { + this.contextRunner.withUserConfiguration(TracingConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean(PropagationWebGraphQlInterceptor.class)); + } + + @Test + void propagationInterceptorContributed() { + this.contextRunner.withUserConfiguration(WebGraphQlConfiguration.class, TracingConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(PropagationWebGraphQlInterceptor.class)); + } + @Configuration(proxyBeanMethods = false) static class InstrumentationConfiguration { @@ -105,4 +127,22 @@ static class CustomDataFetcherObservationConvention extends DefaultDataFetcherOb } + @Configuration(proxyBeanMethods = false) + static class WebGraphQlConfiguration { + + @Bean + WebGraphQlHandler webGraphQlHandler() { + return mock(WebGraphQlHandler.class); + } + } + + @Configuration(proxyBeanMethods = false) + static class TracingConfiguration { + + @Bean + Propagator propagator() { + return mock(Propagator.class); + } + } + }