From 92d0a30a959affc8e8ef3ad9cd880f4fafdd60b1 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 21 Apr 2021 12:20:25 +0000 Subject: [PATCH] Added Spring Cloud Config server support; fixes 1915 (#1917) --- docs/src/main/asciidoc/integrations.adoc | 10 +- pom.xml | 8 ++ spring-cloud-sleuth-autoconfigure/pom.xml | 10 ++ ...aceSpringCloudConfigAutoConfiguration.java | 51 ++++++++++ ...itional-spring-configuration-metadata.json | 6 ++ .../main/resources/META-INF/spring.factories | 1 + ...ringCloudConfigAutoConfigurationTests.java | 39 ++++++++ spring-cloud-sleuth-instrumentation/pom.xml | 10 ++ .../TraceEnvironmentRepositoryAspect.java | 54 ++++++++++ tests/brave/pom.xml | 1 + .../pom.xml | 84 ++++++++++++++++ .../config/ConfigServerIntegrationTests.java | 53 ++++++++++ .../src/test/resources/application.yml | 3 + tests/common/pom.xml | 5 + .../config/ConfigServerIntegrationTests.java | 98 +++++++++++++++++++ 15 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfiguration.java create mode 100644 spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfigurationTests.java create mode 100644 spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/config/TraceEnvironmentRepositoryAspect.java create mode 100644 tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/pom.xml create mode 100644 tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/config/ConfigServerIntegrationTests.java create mode 100644 tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/resources/application.yml create mode 100644 tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/config/ConfigServerIntegrationTests.java diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc index 6da99eafc..a05b772c4 100644 --- a/docs/src/main/asciidoc/integrations.adoc +++ b/docs/src/main/asciidoc/integrations.adoc @@ -566,4 +566,12 @@ IMPORTANT: The suggested approach to reactive programming and Sleuth is to use t This feature is available for all tracer implementations. If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command `Supplier` and the fallback `Function` in its trace representations. We will also instrument the reactive implementation of the CircuitBreaker. -In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`. \ No newline at end of file +In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`. + +[[sleuth-config-server-integration]] +== Spring Cloud Config Server + +This feature is available for all tracer implementations. + +If you have Spring Cloud Config Server running on the classpath, we will wrap the `EnvironmentRepository` in a span. +In order to disable this instrumentation set `spring.sleuth.config.server.enabled` to `false`. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 286d8b4e4..08c6c9d63 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,7 @@ 3.0.3-SNAPSHOT 3.0.3-SNAPSHOT 3.0.3-SNAPSHOT + 3.0.3-SNAPSHOT 2.0.2-SNAPSHOT 3.1.3-SNAPSHOT 3.1.3-SNAPSHOT @@ -229,6 +230,13 @@ pom import + + org.springframework.cloud + spring-cloud-config-dependencies + ${spring-cloud-config.version} + pom + import + org.springframework.cloud spring-cloud-task-dependencies diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml index 769757ac6..ac237bbf7 100644 --- a/spring-cloud-sleuth-autoconfigure/pom.xml +++ b/spring-cloud-sleuth-autoconfigure/pom.xml @@ -73,6 +73,16 @@ spring-integration-core true + + org.springframework.cloud + spring-cloud-config-server + true + + + org.springframework.cloud + spring-cloud-starter-config + true + org.springframework.cloud spring-cloud-function-context diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfiguration.java new file mode 100644 index 000000000..1276618a1 --- /dev/null +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfiguration.java @@ -0,0 +1,51 @@ +/* + * 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.config; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.config.server.config.ConfigServerConfiguration; +import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.config.TraceEnvironmentRepositoryAspect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} that registers instrumentation for Spring Cloud Config Server and + * Client. + * + * @author Marcin Grzejszczak + * @since 3.1.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean({ Tracer.class, ConfigServerProperties.class }) +@ConditionalOnClass(ConfigServerConfiguration.class) +@ConditionalOnProperty(value = "spring.sleuth.config.server.enabled", matchIfMissing = true) +@AutoConfigureAfter(BraveAutoConfiguration.class) +public class TraceSpringCloudConfigAutoConfiguration { + + @Bean + TraceEnvironmentRepositoryAspect traceEnvironmentRepositoryAspect(Tracer tracer) { + return new TraceEnvironmentRepositoryAspect(tracer); + } + +} 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 e46fecde3..b2ebb1d5a 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 @@ -136,6 +136,12 @@ "type": "java.lang.Boolean", "description": "Enable Spring Cloud Task instrumentation.", "defaultValue": true + }, + { + "name": "spring.sleuth.config.server.enabled", + "type": "java.lang.Boolean", + "description": "Enable Spring Cloud Config Server instrumentation.", + "defaultValue": true } ] } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories index 5e902cd8a..d2df0f518 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories @@ -3,6 +3,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncDefaultAutoConfiguration,\ +org.springframework.cloud.sleuth.autoconfig.instrument.config.TraceSpringCloudConfigAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.circuitbreaker.TraceCircuitBreakerAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.rxjava.TraceRxJavaAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.instrument.quartz.TraceQuartzAutoConfiguration,\ diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfigurationTests.java new file mode 100644 index 000000000..8d75ab817 --- /dev/null +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/config/TraceSpringCloudConfigAutoConfigurationTests.java @@ -0,0 +1,39 @@ +/* + * 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.config; + +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.config.TraceEnvironmentRepositoryAspect; + +class TraceSpringCloudConfigAutoConfigurationTests { + + @Test + void should_register_the_aspect() { + new ApplicationContextRunner().withPropertyValues("spring.sleuth.noop.enabled=true") + .withBean(ConfigServerProperties.class) + .withConfiguration(AutoConfigurations.of(TraceNoOpAutoConfiguration.class, + TraceSpringCloudConfigAutoConfiguration.class)) + .run(context -> BDDAssertions.then(context).hasSingleBean(TraceEnvironmentRepositoryAspect.class)); + } + +} diff --git a/spring-cloud-sleuth-instrumentation/pom.xml b/spring-cloud-sleuth-instrumentation/pom.xml index 6aff38d57..356f74fe0 100644 --- a/spring-cloud-sleuth-instrumentation/pom.xml +++ b/spring-cloud-sleuth-instrumentation/pom.xml @@ -72,6 +72,16 @@ spring-integration-core true + + org.springframework.cloud + spring-cloud-config-server + true + + + org.springframework.cloud + spring-cloud-starter-config + true + org.springframework.cloud spring-cloud-function-context diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/config/TraceEnvironmentRepositoryAspect.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/config/TraceEnvironmentRepositoryAspect.java new file mode 100644 index 000000000..93b420f00 --- /dev/null +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/config/TraceEnvironmentRepositoryAspect.java @@ -0,0 +1,54 @@ +/* + * 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.instrument.config; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.Tracer; + +/** + * Aspect wrapping resolution of properties. + * + * @author Marcin Grzejszczak + * @since 3.1.0 + */ +@Aspect +public class TraceEnvironmentRepositoryAspect { + + private final Tracer tracer; + + public TraceEnvironmentRepositoryAspect(Tracer tracer) { + this.tracer = tracer; + } + + @Around("execution (* org.springframework.cloud.config.server.environment.EnvironmentRepository.*(..))") + public Object traceFindEnvironment(final ProceedingJoinPoint pjp) throws Throwable { + Span findOneSpan = this.tracer.nextSpan().name("find"); + findOneSpan.tag("config.environment.class", pjp.getTarget().getClass().getName()); + findOneSpan.tag("config.environment.method", pjp.getSignature().getName()); + try (Tracer.SpanInScope ws = this.tracer.withSpan(findOneSpan.start())) { + return pjp.proceed(); + } + finally { + findOneSpan.end(); + } + } + +} diff --git a/tests/brave/pom.xml b/tests/brave/pom.xml index 8796d03bf..2e21ae75f 100644 --- a/tests/brave/pom.xml +++ b/tests/brave/pom.xml @@ -38,6 +38,7 @@ spring-cloud-sleuth-instrumentation-annotation-tests spring-cloud-sleuth-instrumentation-async-tests spring-cloud-sleuth-instrumentation-baggage-tests + spring-cloud-sleuth-instrumentation-config-server-tests spring-cloud-sleuth-instrumentation-circuitbreaker-tests spring-cloud-sleuth-instrumentation-circuitbreaker-reactive-tests spring-cloud-sleuth-instrumentation-feign-tests diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/pom.xml new file mode 100644 index 000000000..6d6f4eb10 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-config-server-tests + jar + Spring Cloud Sleuth Brave Config Server Instrumentation Tests + Spring Cloud Sleuth Brave Config Server Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests-brave + 3.1.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.boot + spring-boot-starter-test + + + io.zipkin.brave + brave-tests + + + org.awaitility + awaitility + + + + diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/config/ConfigServerIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/config/ConfigServerIntegrationTests.java new file mode 100644 index 000000000..fe1251a31 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/config/ConfigServerIntegrationTests.java @@ -0,0 +1,53 @@ +/* + * 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.brave.instrument.config; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = ConfigServerIntegrationTests.Config.class) +public class ConfigServerIntegrationTests + extends org.springframework.cloud.sleuth.instrument.config.ConfigServerIntegrationTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/resources/application.yml new file mode 100644 index 000000000..f0238e134 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-config-server-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration diff --git a/tests/common/pom.xml b/tests/common/pom.xml index 01c5cdc48..f405ccd78 100644 --- a/tests/common/pom.xml +++ b/tests/common/pom.xml @@ -119,6 +119,11 @@ spring-cloud-starter-loadbalancer true + + org.springframework.cloud + spring-cloud-config-server + true + org.apache.httpcomponents httpclient diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/config/ConfigServerIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/config/ConfigServerIntegrationTests.java new file mode 100644 index 000000000..e27a43140 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/config/ConfigServerIntegrationTests.java @@ -0,0 +1,98 @@ +/* + * 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.instrument.config; + +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.sleuth.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.awaitility.Awaitility.await; + +@ContextConfiguration(classes = ConfigServerIntegrationTests.TestConfig.class) +@TestPropertySource(properties = { "server.port=0", + "spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo" }) +public abstract class ConfigServerIntegrationTests { + + @Autowired + TestSpanHandler spans; + + @Autowired + WebClientService webClientService; + + @LocalServerPort + int port; + + @BeforeEach + public void setup() { + this.spans.clear(); + } + + @Test + public void should_instrument_config_server() { + this.webClientService.call(port); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + then(this.spans.reportedSpans()).as("1 for mvc, 1 for composite env repo and 1 for git env repo") + .hasSize(3); + then(this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId).collect(Collectors.toSet())) + .as("There must be 1 trace id").hasSize(1); + }); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @EnableConfigServer + public static class TestConfig { + + @Bean + WebClientService webClientService() { + return new WebClientService(); + } + + } + + public static class WebClientService { + + private static final Logger log = LoggerFactory.getLogger(WebClientService.class); + + void call(int port) { + log.info("Sending request"); + String result = new RestTemplate().getForObject("http://localhost:" + port + "/master/application.yml", + String.class); + log.info("Got [\n" + result + "\n]"); + } + + } + +}