diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 9b43bd350..95f1c0a35 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -61,6 +61,7 @@ |spring.sleuth.trace-id128 | `false` | When true, generate 128-bit trace IDs instead of 64-bit ones. |spring.sleuth.tracer.mode | | Set which tracer implementation should be picked. |spring.sleuth.tx.enabled | `true` | Enable Spring TX instrumentation. +|spring.sleuth.vault.enabled | `true` | Enable Spring Vault instrumentation. |spring.sleuth.web.additional-skip-pattern | | Additional pattern for URLs that should be skipped in tracing. This will be appended to the {@link SleuthWebProperties#skipPattern}. |spring.sleuth.web.client.enabled | `true` | Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}. |spring.sleuth.web.client.skip-pattern | | Pattern for URLs that should be skipped in client side tracing. diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc index 04f70fe95..4127620f7 100644 --- a/docs/src/main/asciidoc/integrations.adoc +++ b/docs/src/main/asciidoc/integrations.adoc @@ -634,3 +634,12 @@ This feature is available for all tracer implementations. If you have R2DBC Proxy on the classpath we will instrument the `ConnectionFactory`so that it contains a custom `ProxyExecutionListener`. In order to disable this instrumentation set `spring.sleuth.r2dbc.enabled` to `false`. + + +[[sleuth-vault-integration]] +== Spring Vault + +This feature is available for all tracer implementations. + +We're instrumenting the `RestTemplate` or `WebClient` instances used by Spring Vault to communicate with Vault. +In order to disable this instrumentation set `spring.sleuth.vault.enabled` to `false`. diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml index 4be621272..46a491f01 100644 --- a/spring-cloud-sleuth-autoconfigure/pom.xml +++ b/spring-cloud-sleuth-autoconfigure/pom.xml @@ -183,6 +183,11 @@ spring-security-oauth2-autoconfigure true + + org.springframework.vault + spring-vault-core + true + diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java index 3e7d85c46..507d87116 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -20,7 +20,6 @@ import reactor.netty.http.client.HttpClient; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -29,7 +28,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer; -import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; import org.springframework.cloud.sleuth.CurrentTraceContext; @@ -50,6 +48,7 @@ import org.springframework.cloud.sleuth.propagation.Propagator; import org.springframework.context.ConfigurableApplicationContext; 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.security.oauth2.client.OAuth2RestTemplate; import org.springframework.web.client.RestTemplate; @@ -81,22 +80,27 @@ class TraceWebClientAutoConfiguration { httpClientHandler); } + @Bean + @Order + TraceRestTemplateCustomizer traceRestTemplateCustomizer(BeanFactory beanFactory) { + return new TraceRestTemplateCustomizer(new LazyTraceClientHttpRequestInterceptor(beanFactory)); + } + + @Bean + static TraceRestTemplateBeanPostProcessor traceRestTemplateBeanPostProcessor(ListableBeanFactory beanFactory) { + return new TraceRestTemplateBeanPostProcessor(beanFactory); + } + @Configuration(proxyBeanMethods = false) - protected static class TraceInterceptorConfiguration { - - @Autowired - private BeanFactory beanFactory; + @ConditionalOnClass(org.springframework.vault.client.RestTemplateCustomizer.class) + @ConditionalOnProperty(value = "spring.sleuth.vault.enabled", matchIfMissing = true) + static class VaultRestTemplateCustomizerConfiguration { @Bean - static TraceRestTemplateBeanPostProcessor traceRestTemplateBeanPostProcessor( - ListableBeanFactory beanFactory) { - return new TraceRestTemplateBeanPostProcessor(beanFactory); - } - - @Bean - @Order - RestTemplateCustomizer traceRestTemplateCustomizer() { - return new TraceRestTemplateCustomizer(new LazyTraceClientHttpRequestInterceptor(this.beanFactory)); + @Order(Ordered.HIGHEST_PRECEDENCE) + org.springframework.vault.client.RestTemplateCustomizer traceVaultRestTemplateCustomizer( + TraceRestTemplateCustomizer restTemplateCustomizer) { + return restTemplateCustomizer::customize; } } @@ -144,6 +148,21 @@ class TraceWebClientAutoConfiguration { return new TraceWebClientBeanPostProcessor(springContext); } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(org.springframework.vault.client.WebClientCustomizer.class) + @ConditionalOnProperty(value = "spring.sleuth.vault.enabled", matchIfMissing = true) + static class VaultWebClientCustomizerConfiguration { + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + org.springframework.vault.client.WebClientCustomizer traceVaultWebClientCustomizer( + ConfigurableApplicationContext springContext) { + return webClientBuilder -> new TraceWebClientBeanPostProcessor(springContext) + .postProcessAfterInitialization(webClientBuilder, ""); + } + + } + } @Configuration(proxyBeanMethods = false) diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 97669ed28..a428a7a2d 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -178,6 +178,12 @@ "type": "java.lang.Boolean", "description": "Enable R2dbc instrumentation.", "defaultValue": true + }, + { + "name": "spring.sleuth.vault.enabled", + "type": "java.lang.Boolean", + "description": "Enable Spring Vault instrumentation.", + "defaultValue": true } ] } diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceVaultConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceVaultConfigurationTests.java new file mode 100644 index 000000000..07a2ed9d7 --- /dev/null +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceVaultConfigurationTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-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.cloud.sleuth.autoconfig.instrument.web.client; + +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration; +import org.springframework.vault.client.RestTemplateCustomizer; +import org.springframework.vault.client.WebClientCustomizer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TraceVaultConfigurationTests { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withPropertyValues("spring.sleuth.noop.enabled=true").withConfiguration( + AutoConfigurations.of(TraceNoOpAutoConfiguration.class, TraceWebClientAutoConfiguration.class)); + + @Test + public void shouldNotCreateVaultRestTemplateCustomizerWhenVaultNotOnClasspath() { + this.contextRunner.withClassLoader(new FilteredClassLoader(RestTemplateCustomizer.class)) + .run((context) -> assertThat(context).doesNotHaveBean(RestTemplateCustomizer.class)); + } + + @Test + public void shouldNotCreateVaultWebClientCustomizerWhenVaultNotOnClasspath() { + this.contextRunner.withClassLoader(new FilteredClassLoader(WebClientCustomizer.class)) + .run((context) -> assertThat(context).doesNotHaveBean(WebClientCustomizer.class)); + } + + @Test + public void shouldNotCreateVaultRestTemplateCustomizerWhenSleuthVaultDisabled() { + this.contextRunner.withPropertyValues("spring.sleuth.vault.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(RestTemplateCustomizer.class)); + } + + @Test + public void shouldNotCreateVaultWebClientCustomizerWhenSleuthVaultDisabled() { + this.contextRunner.withPropertyValues("spring.sleuth.vault.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(WebClientCustomizer.class)); + } + + @Test + public void shouldCreateVaultRestTemplateCustomizerWhenVaultNotOnClasspath() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(RestTemplateCustomizer.class)); + } + + @Test + public void shouldCreateVaultWebClientCustomizerWhenVaultNotOnClasspath() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(WebClientCustomizer.class)); + } + +}