diff --git a/.travis.yml b/.travis.yml index 2afd757c..d402cb07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,12 +30,9 @@ install: jobs: include: - stage: test - env: [ NAME=greenwhich ] + env: [ NAME=hoxton ] script: make default - - env: [ NAME=finchley ] - script: make finchley - - env: [ NAME=classpath-no-dependencies ] script: PROFILES=nodeps make classpath diff --git a/Makefile b/Makefile index f97977a1..e1769ba1 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,8 @@ ALL: default classpath default: @./mvnw clean install -finchley: - @./mvnw clean test -Dversion.org.springframework.boot=2.0.3.RELEASE -Dversion.org.springframework.cloud-spring-cloud-dependencies=Finchley.RELEASE +greenwich: + @./mvnw clean test -Dversion.org.springframework.boot=2.1.5.RELEASE -Dversion.org.springframework.cloud-spring-cloud-dependencies=Greenwich.SR1 classpath: @for profile in $(PROFILES) ; do \ diff --git a/README.md b/README.md index 78f16e1a..bb81c478 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ It contains auto-configurations which instruments and trace following Spring Boo * Hystrix * JMS * JDBC +* Kafka * Mongo * Zuul * Reactor @@ -27,14 +28,16 @@ The following table shows versions with compatible [Spring Cloud](http://project opentracing-spring-cloud version | OpenTracing API | Spring Cloud version --- | --- | --- -0.3.x | 0.32.0 | `Finchley`, `Greenwhich` -0.2.x | 0.31.0 | `Finchley`, `Greenwhich` +0.5.x | 0.33.0 | `Hoxton` +0.4.x | 0.32.0 | `Hoxton` +0.3.x | 0.32.0 | `Greenwich` +0.2.x | 0.31.0 | `Finchley`, `Greenwich` 0.1.x | 0.31.0 | `Dalston`, `Edgware` ## Comparison to `spring-cloud-sleuth` -This project is similar to [spring-cloud-sleuth](https://github.com/spring-cloud/spring-cloud-sleuth), -both provide out of the box tracing solution for Spring Boot/Cloud. Some of the instrumentations in this +This project is similar to [spring-cloud-sleuth](https://github.com/spring-cloud/spring-cloud-sleuth), +both provide out of the box tracing solution for Spring Boot/Cloud. Some of the instrumentations in this package are based on original `sleuth` work. However there are a couple of differences: @@ -44,15 +47,15 @@ However there are a couple of differences: ## Note on dependencies -It's worth noting that the although OpenTracing Spring Cloud contains code for instrumenting a wealth of Spring projects, +It's worth noting that the although OpenTracing Spring Cloud contains code for instrumenting a wealth of Spring projects, it however does not pull those dependencies automatically, marking them as optional dependencies instead. That means that for example a simple Spring Boot REST API application can include OpenTracing Spring Cloud without the fear -of polluting the classpath with Spring Cloud dependencies that are otherwise unneeded +of polluting the classpath with Spring Cloud dependencies that are otherwise unneeded ## Configuration -The preferred way to use this library is via vendored starters. These starters use +The preferred way to use this library is via vendored starters. These starters use instrumentations from this library and expose specific tracer configuration in Spring native way: @@ -76,6 +79,30 @@ public io.opentracing.Tracer tracer() { } ``` +### Properties + +Property| Default| Description +------------- | ------------- | ------------- +opentracing.spring.cloud.reactor.enabled|true|Enable Reactor tracing. +opentracing.spring.cloud.async.enabled|true|Enable tracing for @Async, Executor and WebAsyncTask/Callable. +opentracing.spring.cloud.log.enabled|true|Add standard logging output to tracing system. +opentracing.spring.cloud.scheduled.enabled|true|Enable @Scheduled tracing. +opentracing.spring.cloud.feign.enabled|true|Enable Feign tracing. +opentracing.spring.cloud.gateway.enabled|true|Enable Gateway tracing. +opentracing.spring.cloud.hystrix.strategy.enabled|true|Enable Propagation of spans across threads using in Hystrix command tracing. +opentracing.spring.cloud.jdbc.enabled|true|Enable JDBC tracing. +opentracing.spring.cloud.jms.enabled|true|Enable JMS tracing. +opentracing.spring.cloud.kafka.enabled|true|Enable Kafka tracing. +opentracing.spring.cloud.mongo.enabled|true|Enable MongoDB tracing. +opentracing.spring.cloud.reactor.enabled|true|Enable Reactor tracing. +opentracing.spring.cloud.rxjava.enabled|true|Enable RxJava tracing. +opentracing.spring.cloud.websocket.enabled|true|Enable Websocket tracing. +opentracing.spring.cloud.zuul.enabled|true|Enable Zuul tracing. +opentracing.spring.cloud.redis.enabled|true|Enable Redis tracing. +opentracing.spring.cloud.redis.prefixOperationName|""|Set a prefix for each Redis operation, e.g: MyPrefix.SET. +opentracing.spring.cloud.jdbc.withActiveSpanOnly|false|Only trace JDBC calls if they are part of an active Span. +opentracing.spring.cloud.jdbc.ignoreStatements|null|Set of JDBC statements to not trace. + ## Development Maven checkstyle plugin is used to maintain consistent code style based on [Google Style Guides](https://github.com/google/styleguide) diff --git a/instrument-starters/opentracing-spring-cloud-aop/pom.xml b/instrument-starters/opentracing-spring-cloud-aop/pom.xml new file mode 100644 index 00000000..02a99dfa --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-aop/pom.xml @@ -0,0 +1,33 @@ + + + + 4.0.0 + + io.opentracing.contrib + opentracing-spring-cloud-parent + 0.5.8-SNAPSHOT + ../../ + + + opentracing-spring-cloud-aop + + + ${project.basedir}/../../ + + + + diff --git a/instrument-starters/opentracing-spring-cloud-aop/src/main/java/io/opentracing/contrib/spring/cloud/aop/Traced.java b/instrument-starters/opentracing-spring-cloud-aop/src/main/java/io/opentracing/contrib/spring/cloud/aop/Traced.java new file mode 100644 index 00000000..dfe3555a --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-aop/src/main/java/io/opentracing/contrib/spring/cloud/aop/Traced.java @@ -0,0 +1,29 @@ +/** + * Copyright 2017-2018 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Traced { + + String component() default "traced"; + + String operationName() default ""; + +} diff --git a/instrument-starters/opentracing-spring-cloud-core/pom.xml b/instrument-starters/opentracing-spring-cloud-core/pom.xml index 3d9dcb7c..0e0c734d 100644 --- a/instrument-starters/opentracing-spring-cloud-core/pom.xml +++ b/instrument-starters/opentracing-spring-cloud-core/pom.xml @@ -1,7 +1,7 @@ + + 4.0.0 + + io.opentracing.contrib + opentracing-spring-cloud-parent + 0.5.10-SNAPSHOT + ../../ + + + opentracing-spring-cloud-gateway-starter + + + ${project.basedir}/../../ + + + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + + org.springframework + spring-web + true + + + + org.springframework.cloud + spring-cloud-gateway-core + true + + + + io.opentracing.contrib + opentracing-spring-web-starter + ${version.io.opentracing.contrib-opentracing-spring-web} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-web + test + + + io.opentracing.contrib + opentracing-spring-cloud-core + test + + + + diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/AbstractHttpHeadersFilter.java b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/AbstractHttpHeadersFilter.java new file mode 100644 index 00000000..12821096 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/AbstractHttpHeadersFilter.java @@ -0,0 +1,44 @@ +/** + * Copyright 2017-2019 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.gateway; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * AbstractHttpHeadersFilter + * + * @author Weichao Li (liweichao0102@gmail.com) + * @since 2019/9/28 + */ +public abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter { + + protected static final String SPAN_ATTRIBUTE = Span.class.getName(); + + protected static final String ROUTE_ATTRIBUTE = ServerWebExchangeUtils.class.getName() + ".gatewayRoute"; + + protected final Tracer tracer; + + protected AbstractHttpHeadersFilter(Tracer tracer) { + this.tracer = tracer; + } + + public String path(ServerHttpRequest.Builder request) { + return request.build().getPath().value(); + } + +} diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/GatewayTracingAutoConfiguration.java b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/GatewayTracingAutoConfiguration.java new file mode 100644 index 00000000..3db7acb7 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/GatewayTracingAutoConfiguration.java @@ -0,0 +1,54 @@ +/** + * Copyright 2017-2019 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.gateway; + +import io.opentracing.Tracer; +import io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration; +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.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * GatewayTracingAutoConfiguration + * + * @author Weichao Li (liweichao0102@gmail.com) + * @since 2019/9/27 + */ +@Configuration +@ConditionalOnWebApplication +@AutoConfigureAfter(TracerAutoConfiguration.class) +@ConditionalOnClass(GatewayFilterChain.class) +@ConditionalOnProperty(name = "opentracing.spring.cloud.gateway.enabled", havingValue = "true", matchIfMissing = true) +public class GatewayTracingAutoConfiguration { + + @Bean + @ConditionalOnClass(HttpHeadersFilter.class) + @ConditionalOnBean(Tracer.class) + HttpHeadersFilter traceRequestHttpHeadersFilter(Tracer tracer) { + return new TraceRequestHttpHeadersFilter(tracer); + } + + @Bean + @ConditionalOnClass(HttpHeadersFilter.class) + @ConditionalOnBean(Tracer.class) + HttpHeadersFilter traceResponseHttpHeadersFilter(Tracer tracer) { + return new TraceResponseHttpHeadersFilter(tracer); + } +} diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceRequestHttpHeadersFilter.java b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceRequestHttpHeadersFilter.java new file mode 100644 index 00000000..19e0ebe5 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceRequestHttpHeadersFilter.java @@ -0,0 +1,97 @@ +/** + * Copyright 2017-2019 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.gateway; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.contrib.spring.web.client.HttpHeadersCarrier; +import io.opentracing.propagation.Format; +import io.opentracing.tag.Tags; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; + + + +/** + * TraceRequestHttpHeadersFilter + * + * @author Weichao Li (liweichao0102@gmail.com) + * @since 2019/9/26 + */ +final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { + + private final Logger log = LoggerFactory.getLogger(TraceRequestHttpHeadersFilter.class); + + private static final String ROUTE_ID = "route.id"; + + private static final String COMPONENT = "java-spring-cloud-gateway"; + + protected TraceRequestHttpHeadersFilter(Tracer tracer) { + super(tracer); + } + + @Override + public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { + log.debug("Will instrument spring cloud gateway the HTTP request headers"); + ServerHttpRequest.Builder builder = exchange.getRequest().mutate(); + Span span = this.tracer.buildSpan(path(builder)) + .asChildOf(tracer.activeSpan()) + .withTag(Tags.COMPONENT.getKey(), COMPONENT) + .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) + .withTag(ROUTE_ID, getRouteId(exchange)) + .start(); + log.debug("Client span {} created for the request. New headers are {}", span, builder.build().getHeaders().toSingleValueMap()); + exchange.getAttributes().put(SPAN_ATTRIBUTE, span); + HttpHeaders headersWithInput = new HttpHeaders(); + try { + this.tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new HttpHeadersCarrier(headersWithInput)); + } catch (Exception ignore) { + log.error("TraceRequestHttpHeadersFilter error", ignore); + } + headersWithInput.addAll(input); + addHeadersWithInput(builder, headersWithInput); + return headersWithInput; + } + + private String getRouteId(ServerWebExchange exchange) { + String routeId = "unknown"; + Route route = exchange.getAttribute(ROUTE_ATTRIBUTE); + if (Objects.nonNull(route)) { + return route.getId(); + } + return routeId; + } + + private void addHeadersWithInput(ServerHttpRequest.Builder builder, + HttpHeaders headersWithInput) { + for (Map.Entry> entry : builder.build().getHeaders() + .entrySet()) { + String key = entry.getKey(); + List value = entry.getValue(); + headersWithInput.put(key, value); + } + } + + @Override + public boolean supports(Type type) { + return type.equals(Type.REQUEST); + } +} diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceResponseHttpHeadersFilter.java b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceResponseHttpHeadersFilter.java new file mode 100644 index 00000000..10fa2d0b --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/java/io/opentracing/contrib/spring/cloud/gateway/TraceResponseHttpHeadersFilter.java @@ -0,0 +1,59 @@ +/** + * Copyright 2017-2019 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.gateway; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.web.server.ServerWebExchange; + + + +/** + * TraceResponseHttpHeadersFilter + * + * @author Weichao Li (liweichao0102@gmail.com) + * @since 2019/9/28 + */ +public class TraceResponseHttpHeadersFilter extends AbstractHttpHeadersFilter { + + private final Logger log = LoggerFactory.getLogger(TraceResponseHttpHeadersFilter.class); + + protected TraceResponseHttpHeadersFilter(Tracer tracer) { + super(tracer); + } + + @Override + public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { + Object storedSpan = exchange.getAttribute(SPAN_ATTRIBUTE); + if (storedSpan == null) { + return input; + } + log.debug("Will instrument the response"); + Span span = (Span) storedSpan; + if (Objects.nonNull(span)) { + span.finish(); + } + log.debug("The response was handled for span " + storedSpan); + return input; + } + + @Override + public boolean supports(Type type) { + return type.equals(Type.RESPONSE); + } +} diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 00000000..73899c38 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "opentracing.spring.cloud.gateway.enabled", + "type": "java.lang.Boolean", + "description": "Add standard logging output to tracing system.", + "defaultValue": true + } + ] +} diff --git a/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/spring.factories b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..1943fde3 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-gateway-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.opentracing.contrib.spring.cloud.gateway.GatewayTracingAutoConfiguration diff --git a/instrument-starters/opentracing-spring-cloud-hystrix-starter/pom.xml b/instrument-starters/opentracing-spring-cloud-hystrix-starter/pom.xml index 7cf20934..9a39faa8 100644 --- a/instrument-starters/opentracing-spring-cloud-hystrix-starter/pom.xml +++ b/instrument-starters/opentracing-spring-cloud-hystrix-starter/pom.xml @@ -1,7 +1,7 @@ + + + opentracing-spring-cloud-parent + io.opentracing.contrib + 0.5.10-SNAPSHOT + ../../ + + 4.0.0 + + opentracing-spring-cloud-kafka-starter + + + ${project.basedir}/../.. + + + + + org.springframework.boot + spring-boot-autoconfigure + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + + io.opentracing.contrib + opentracing-kafka-spring + + + * + * + + + + + io.opentracing.contrib + opentracing-kafka-client + + + * + * + + + + + + org.springframework.kafka + spring-kafka + true + + + + org.springframework + spring-aspects + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${version.maven-surefire-plugin} + + always + + + + + diff --git a/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfiguration.java b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfiguration.java new file mode 100644 index 00000000..b5bf2b15 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfiguration.java @@ -0,0 +1,81 @@ +/** + * Copyright 2017-2020 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.kafka; + +import io.opentracing.Tracer; +import io.opentracing.contrib.kafka.spring.TracingConsumerFactory; +import io.opentracing.contrib.kafka.spring.TracingKafkaAspect; +import io.opentracing.contrib.kafka.spring.TracingProducerFactory; +import io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration; +import org.springframework.aop.framework.ProxyFactoryBean; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.ProducerFactory; + +@Configuration +@ConditionalOnClass(ProducerFactory.class) +@ConditionalOnBean(Tracer.class) +@AutoConfigureAfter(TracerAutoConfiguration.class) +@ConditionalOnProperty(name = "opentracing.spring.cloud.kafka.enabled", havingValue = "true", matchIfMissing = true) +class KafkaAutoConfiguration { + + @Bean + public BeanPostProcessor kafkaProducerPostProcessor(Tracer tracer) { + return new BeanPostProcessor() { + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ProducerFactory && !(bean instanceof TracingProducerFactory)) { + return new TracingProducerFactory((ProducerFactory)bean, tracer); + } + return bean; + } + }; + } + + @Bean + public BeanPostProcessor kafkaConsumerPostProcessor(Tracer tracer) { + return new BeanPostProcessor() { + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ConsumerFactory && !(bean instanceof TracingConsumerFactory)) { + return new TracingConsumerFactory((ConsumerFactory) bean, tracer); + } + return bean; + } + }; + } + + @Bean + @ConditionalOnClass(ProxyFactoryBean.class) + public TracingKafkaAspect tracingKafkaAspect(Tracer tracer) { + return new TracingKafkaAspect(tracer); + } +} diff --git a/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 00000000..471d9053 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "opentracing.spring.cloud.kafka.enabled", + "type": "java.lang.Boolean", + "description": "Enable Kafka tracing.", + "defaultValue": true + } + ] +} diff --git a/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/spring.factories b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..37d6bfb4 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.opentracing.contrib.spring.cloud.kafka.KafkaAutoConfiguration diff --git a/instrument-starters/opentracing-spring-cloud-kafka-starter/src/test/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfigurationTest.java b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/test/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfigurationTest.java new file mode 100644 index 00000000..76cde9e2 --- /dev/null +++ b/instrument-starters/opentracing-spring-cloud-kafka-starter/src/test/java/io/opentracing/contrib/spring/cloud/kafka/KafkaAutoConfigurationTest.java @@ -0,0 +1,159 @@ +/** + * Copyright 2017-2020 The OpenTracing 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 + * + * http://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 io.opentracing.contrib.spring.cloud.kafka; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import io.opentracing.Tracer; +import io.opentracing.contrib.kafka.spring.TracingConsumerFactory; +import io.opentracing.contrib.kafka.spring.TracingProducerFactory; +import java.util.Map; +import java.util.Properties; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.common.serialization.Deserializer; +import org.junit.Test; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.ProducerFactory; + +public class KafkaAutoConfigurationTest { + + @Test + public void loadKafkaTracingProducerFactory() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TracerConfig.class, FakeKafkaConfig.class, KafkaAutoConfiguration.class); + context.refresh(); + ProducerFactory tracingProducerFactory = context.getBean(ProducerFactory.class); + assertTrue(tracingProducerFactory instanceof TracingProducerFactory); + } + + @Test + public void loadNormalProducerFactoryWhenDisabled() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TracerConfig.class, FakeKafkaConfig.class, KafkaAutoConfiguration.class); + TestPropertyValues.of("opentracing.spring.cloud.kafka.enabled:false").applyTo(context); + context.refresh(); + ProducerFactory tracingProducerFactory = context.getBean(ProducerFactory.class); + assertFalse(tracingProducerFactory instanceof TracingProducerFactory); + } + + @Test + public void loadTracingConsumerFactory() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TracerConfig.class, FakeKafkaConfig.class, KafkaAutoConfiguration.class); + context.refresh(); + ConsumerFactory tracingConsumerFactory = context.getBean(ConsumerFactory.class); + assertTrue(tracingConsumerFactory instanceof TracingConsumerFactory); + } + + @Test + public void loadNormalConsumerFactoryWhenDisabled() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TracerConfig.class, FakeKafkaConfig.class, KafkaAutoConfiguration.class); + TestPropertyValues.of("opentracing.spring.cloud.kafka.enabled:false").applyTo(context); + context.refresh(); + ConsumerFactory consumerFactory = context.getBean(ConsumerFactory.class); + assertFalse(consumerFactory instanceof TracingConsumerFactory); + } + + @Test + public void loadNormalConsumerFactoryWhenTracerNotPresent() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(FakeKafkaConfig.class, KafkaAutoConfiguration.class); + context.refresh(); + ConsumerFactory consumerFactory = context.getBean(ConsumerFactory.class); + assertFalse(consumerFactory instanceof TracingConsumerFactory); + } + + + + + @Configuration + static class TracerConfig { + + @Bean + public Tracer tracer() { + return mock(Tracer.class); + } + } + + static class FakeKafkaConfig { + + @Bean + public ProducerFactory producerFactory() { + return new ProducerFactory() { + @Override + public Producer createProducer() { + return null; + } + }; + } + + @Bean + public ConsumerFactory consumerFactory() { + return new ConsumerFactory() { + @Override + public Consumer createConsumer() { + return null; + } + + @Override + public Consumer createConsumer(String clientIdSuffix) { + return null; + } + + @Override + public Consumer createConsumer(String groupId, String clientIdSuffix) { + return null; + } + + @Override + public Consumer createConsumer(String s, String s1, String s2) { + return null; + } + + @Override + public Consumer createConsumer(String groupId, String clientIdPrefix, String clientIdSuffix, Properties properties) { + return null; + } + + @Override + public boolean isAutoCommit() { + return false; + } + + @Override + public Map getConfigurationProperties() { + return null; + } + + @Override + public Deserializer getKeyDeserializer() { + return null; + } + + @Override + public Deserializer getValueDeserializer() { + return null; + } + }; + } + } +} diff --git a/instrument-starters/opentracing-spring-cloud-mongo-starter/pom.xml b/instrument-starters/opentracing-spring-cloud-mongo-starter/pom.xml index 70900a49..06c632b3 100644 --- a/instrument-starters/opentracing-spring-cloud-mongo-starter/pom.xml +++ b/instrument-starters/opentracing-spring-cloud-mongo-starter/pom.xml @@ -1,7 +1,7 @@ - 2.1.5.RELEASE - Greenwich.SR1 + 2.2.0.RELEASE + Hoxton.SR1 3.0.0 - 2.0.3 + 2.2.0 1.3.1 1.7.25 - 3.2.8.RELEASE - + 3.9.0 1.1-rev-1 @@ -113,6 +117,11 @@ + + ${project.groupId} + opentracing-spring-cloud-aop + ${project.version} + ${project.groupId} opentracing-spring-cloud-core @@ -133,6 +142,11 @@ opentracing-spring-cloud-jms-starter ${project.version} + + ${project.groupId} + opentracing-spring-cloud-kafka-starter + ${project.version} + ${project.groupId} opentracing-spring-cloud-feign-starter @@ -173,6 +187,11 @@ opentracing-spring-cloud-zuul-starter ${project.version} + + ${project.groupId} + opentracing-spring-cloud-gateway-starter + ${project.version} + io.opentracing @@ -207,9 +226,19 @@ io.opentracing.contrib - opentracing-jms-1 + opentracing-jms-2 ${version.io.opentracing.contrib-opentracing-spring-jms} + + io.opentracing.contrib + opentracing-kafka-spring + ${version.io.opentracing.contrib-opentracing-kafka-spring} + + + io.opentracing.contrib + opentracing-kafka-client + ${version.io.opentracing.contrib-opentracing-kafka-spring} + io.opentracing.contrib opentracing-spring-tracer-configuration-starter @@ -265,6 +294,11 @@ pom import + + org.assertj + assertj-core + ${version.assertj} + @@ -301,7 +335,7 @@ jfrog-snapshots - http://oss.jfrog.org/artifactory/oss-snapshot-local + https://oss.jfrog.org/artifactory/oss-snapshot-local