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
This commit is contained in:
Brian Clozel
2022-12-19 12:13:14 +01:00
parent 5a973b9723
commit 593b5311e9
2 changed files with 61 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.observation.graphql;
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.ConditionalOnBean;
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 class GraphQlObservationAutoConfiguration {
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);
}
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.observation.graphql;
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.context.annotation.Configuration;
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 @@ class GraphQlObservationAutoConfigurationTests {
});
}
@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 @@ class GraphQlObservationAutoConfigurationTests {
}
@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);
}
}
}