From f6b40385ab9d5b05bfdb0c4a2a01c4c5168c1c06 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 29 Jun 2020 18:35:34 +0200 Subject: [PATCH] Create a SleuthMongoConfiguration class to tag Mongodb as a remote service fixes gh-1627 --- spring-cloud-sleuth-core/pom.xml | 9 ++ .../TraceMongoDbAutoConfiguration.java | 75 ++++++++++++++++ ...itional-spring-configuration-metadata.json | 12 +++ .../main/resources/META-INF/spring.factories | 3 +- .../sleuth/SleuthTestAutoConfiguration.java | 23 +++++ .../TraceMongoDbAutoConfigurationTests.java | 88 +++++++++++++++++++ 6 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java diff --git a/spring-cloud-sleuth-core/pom.xml b/spring-cloud-sleuth-core/pom.xml index 7eee3b105..813696e3f 100644 --- a/spring-cloud-sleuth-core/pom.xml +++ b/spring-cloud-sleuth-core/pom.xml @@ -176,6 +176,11 @@ feign-okhttp true + + org.springframework.boot + spring-boot-starter-data-mongodb + true + org.aspectj aspectjrt @@ -239,6 +244,10 @@ io.zipkin.brave brave-instrumentation-jms + + io.zipkin.brave + brave-instrumentation-mongodb + javax.jms javax.jms-api diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java new file mode 100644 index 000000000..af2a1aa37 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2019 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.mongodb; + +import brave.Tracing; +import brave.mongodb.MongoDBTracing; +import com.mongodb.MongoClientSettings; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoClientSettingsBuilderCustomizer; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} enables MongoDb span information propagation. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(Tracing.class) +@AutoConfigureAfter(TraceAutoConfiguration.class) +@AutoConfigureBefore(MongoAutoConfiguration.class) +@ConditionalOnProperty(value = "spring.sleuth.mongodb.enabled", matchIfMissing = true) +@ConditionalOnClass(MongoClientSettings.Builder.class) +class TraceMongoDbAutoConfiguration { + + @Bean + // for tests + @ConditionalOnMissingBean(TraceMongoClientSettingsBuilderCustomizer.class) + MongoClientSettingsBuilderCustomizer traceMongoClientSettingsBuilderCustomizer( + Tracing tracing) { + return new TraceMongoClientSettingsBuilderCustomizer(tracing); + } + +} + +class TraceMongoClientSettingsBuilderCustomizer + implements MongoClientSettingsBuilderCustomizer { + + private final Tracing tracing; + + TraceMongoClientSettingsBuilderCustomizer(Tracing tracing) { + this.tracing = tracing; + } + + @Override + public void customize(MongoClientSettings.Builder clientSettingsBuilder) { + clientSettingsBuilder.addCommandListener( + MongoDBTracing.create(this.tracing).commandListener()); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 749cecf02..a0332b815 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -66,6 +66,18 @@ "description": "Enable DefaultKafkaHeaderMapper tracing for Kafka.", "defaultValue": true }, + { + "name": "spring.sleuth.quartz.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing for Quartz.", + "defaultValue": true + }, + { + "name": "spring.sleuth.mongodb.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing for MongoDb.", + "defaultValue": true + }, { "name": "spring.sleuth.rpc.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories index 26fc147c9..a6c895d4e 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories @@ -24,7 +24,8 @@ org.springframework.cloud.sleuth.instrument.messaging.TraceSpringMessagingAutoCo org.springframework.cloud.sleuth.instrument.messaging.TraceWebSocketAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.redis.TraceRedisAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.quartz.TraceQuartzAutoConfiguration +org.springframework.cloud.sleuth.instrument.quartz.TraceQuartzAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.mongodb.TraceMongoDbAutoConfiguration # Environment Post Processor org.springframework.boot.env.EnvironmentPostProcessor=\ diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java index 5a6e996a7..742df53c0 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java @@ -16,7 +16,14 @@ package org.springframework.cloud.sleuth; +import com.mongodb.client.MongoClient; +import org.mockito.BDDMockito; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -26,6 +33,22 @@ import org.springframework.context.annotation.Import; @Configuration class SleuthTestAutoConfiguration { + @Configuration + static class TestMongoConfiguration { + + @Bean + @ConditionalOnProperty(value = "test.mongo.mock.enabled", matchIfMissing = true) + MongoClient mongoClient() { + return BDDMockito.mock(MongoClient.class); + } + + @Bean(name = "mongoHealthIndicator") + HealthIndicator mongoHealthIndicator() { + return () -> Health.up().build(); + } + + } + @Configuration @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) static class ReactiveConfiguration { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java new file mode 100644 index 000000000..a812ebfd7 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2020 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.mongodb; + +import brave.Tracing; +import brave.handler.MutableSpan; +import brave.test.TestSpanHandler; +import com.mongodb.MongoClientSettings; +import com.mongodb.event.CommandListener; +import com.mongodb.event.CommandStartedEvent; +import com.mongodb.event.CommandSucceededEvent; +import org.bson.BsonDocument; +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.BDDAssertions.then; + +@SpringBootTest(properties = "test.mongo.mock.enabled=false") +class TraceMongoDbAutoConfigurationTests { + + @Test + void should_record_a_span_when_working_with_mongodb_commands( + @Autowired TestSpanHandler handler) { + then(handler.spans()).isNotEmpty(); + MutableSpan span = handler.get(0); + then(span.traceId()).isNotEmpty(); + then(span.tags()).containsKey("mongodb.command"); + then(span.remoteServiceName()).contains("mongodb"); + } + + @Configuration + @EnableAutoConfiguration + static class TestTraceMongoDbAutoConfiguration { + + @Bean + TestSpanHandler testSpanHandler() { + return new TestSpanHandler(); + } + + @Bean + TraceMongoClientSettingsBuilderCustomizer testMongoClientSettingsBuilderCustomizer( + Tracing tracing) { + return new TestMongoClientSettingsBuilderCustomizer(tracing); + } + + } + +} + +class TestMongoClientSettingsBuilderCustomizer + extends TraceMongoClientSettingsBuilderCustomizer { + + TestMongoClientSettingsBuilderCustomizer(Tracing tracing) { + super(tracing); + } + + @Override + public void customize(MongoClientSettings.Builder clientSettingsBuilder) { + super.customize(clientSettingsBuilder); + CommandListener listener = clientSettingsBuilder.build().getCommandListeners() + .get(0); + listener.commandStarted(new CommandStartedEvent(0, null, "", "", + BDDMockito.mock(BsonDocument.class))); + listener.commandSucceeded(new CommandSucceededEvent(1, null, "", + BDDMockito.mock(BsonDocument.class), 100)); + } + +}