diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java index 6df060570..285f4aafc 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java @@ -91,7 +91,7 @@ public class BraveAutoConfiguration { CurrentTraceContext currentTraceContext, Sampler sampler, SleuthProperties sleuthProperties, @Nullable List spanHandlers, @Nullable List tracingCustomizers) { Tracing.Builder builder = Tracing.newBuilder().sampler(sampler) - .localServiceName(StringUtils.isEmpty(serviceName) ? DEFAULT_SERVICE_NAME : serviceName) + .localServiceName(!StringUtils.hasText(serviceName) ? DEFAULT_SERVICE_NAME : serviceName) .propagationFactory(factory).currentTraceContext(currentTraceContext) .traceId128Bit(sleuthProperties.isTraceId128()).supportsJoin(sleuthProperties.isSupportsJoin()); if (spanHandlers != null) { diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveBaggageConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveBaggageConfiguration.java index 87d5aec14..ced1f1a6c 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveBaggageConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveBaggageConfiguration.java @@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; import org.springframework.cloud.sleuth.brave.propagation.PropagationFactorySupplier; +import org.springframework.cloud.sleuth.brave.propagation.PropagationType; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -125,9 +126,10 @@ class BraveBaggageConfiguration { @Bean @ConditionalOnMissingBean - Propagation.Factory sleuthPropagation(BaggagePropagation.FactoryBuilder factoryBuilder, + Propagation.Factory sleuthPropagationWithB3Baggage(BaggagePropagation.FactoryBuilder factoryBuilder, @Qualifier(BAGGAGE_KEYS) List baggageKeys, @Qualifier(LOCAL_KEYS) List localKeys, @Qualifier(PROPAGATION_KEYS) List propagationKeys, SleuthBaggageProperties sleuthBaggageProperties, + SleuthPropagationProperties sleuthPropagationProperties, PropagationFactorySupplier supplier, @Nullable List baggagePropagationCustomizers) { Set localFields = redirectOldPropertyToNew(LOCAL_KEYS, localKeys, "spring.sleuth.baggage.local-fields", @@ -159,7 +161,13 @@ class BraveBaggageConfiguration { customizer.customize(factoryBuilder); } } - return factoryBuilder.build(); + Propagation.Factory delegate = factoryBuilder.build(); + Propagation.Factory factoryFromSupplier = supplier.get(); + final boolean hasB3 = sleuthPropagationProperties.getType().contains(PropagationType.B3); + if (hasB3) { + return delegate; + } + return new BaggageFactoryWrapper(delegate, factoryFromSupplier); } static Set redirectOldPropertyToNew(String oldProperty, List oldValue, String newProperty, @@ -290,4 +298,65 @@ class BraveBaggageConfiguration { } + static class BaggageFactoryWrapper extends Propagation.Factory { + + private final Propagation.Factory delegate; + + private final Propagation.Factory factoryFromSupplier; + + BaggageFactoryWrapper(Propagation.Factory delegate, Propagation.Factory factoryFromSupplier) { + this.delegate = delegate; + this.factoryFromSupplier = factoryFromSupplier; + } + + @Override + public boolean supportsJoin() { + return delegate.supportsJoin(); + } + + @Override + public boolean requires128BitTraceId() { + return delegate.requires128BitTraceId(); + } + + @Override + public Propagation get() { + Propagation propagation = delegate.get(); + return delegateWithoutB3Baggage(factoryFromSupplier, propagation); + } + + @Override + public TraceContext decorate(TraceContext context) { + return delegate.decorate(context); + } + + @Override + public Propagation create(Propagation.KeyFactory keyFactory) { + Propagation propagation = delegate.create(keyFactory); + return delegateWithoutB3Baggage(factoryFromSupplier, propagation); + } + + private Propagation delegateWithoutB3Baggage(Propagation.Factory factoryFromSupplier, + Propagation propagation) { + return new Propagation() { + @Override + public List keys() { + return propagation.keys(); + } + + @Override + public TraceContext.Injector injector(Setter setter) { + // We don't want to inject baggage in the Brave way + return factoryFromSupplier.get().injector((Setter) setter); + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + return propagation.extractor(getter); + } + }; + } + + }; + } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelPropagationConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelPropagationConfiguration.java index 7b7536f72..ce7d1fac8 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelPropagationConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelPropagationConfiguration.java @@ -33,8 +33,13 @@ import org.springframework.cloud.sleuth.BaggageManager; import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; import org.springframework.cloud.sleuth.otel.propagation.BaggageTextMapPropagator; import org.springframework.cloud.sleuth.otel.propagation.CompositeTextMapPropagator; +import org.springframework.cloud.sleuth.otel.propagation.PropagationType; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.AnnotatedTypeMetadata; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -74,7 +79,7 @@ class OtelPropagationConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(name = "spring.sleuth.otel.propagation.sleuth-baggage.enabled", matchIfMissing = true) + @Conditional(B3PresentOrPropertyEnabledCondition.class) static class BaggagePropagatorConfiguration { @Bean @@ -84,4 +89,16 @@ class OtelPropagationConfiguration { } + static class B3PresentOrPropertyEnabledCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String type = context.getEnvironment().getProperty("spring.sleuth.propagation.type", "").toLowerCase(); + boolean sleuthBaggageEnabled = context.getEnvironment() + .getProperty("spring.sleuth.otel.propagation.sleuth-baggage.enabled", Boolean.class, false); + return type.contains(PropagationType.B3.toString().toLowerCase()) || sleuthBaggageEnabled; + } + + } + } diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml index 894241490..89811d145 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml @@ -84,6 +84,16 @@ org.awaitility awaitility + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/W3CBaggageTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/W3CBaggageTests.java new file mode 100644 index 000000000..84872fa8c --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/W3CBaggageTests.java @@ -0,0 +1,55 @@ +/* + * 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.brave.baggage; + +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; + +/** + * @author Taras Danylchuk + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ContextConfiguration(classes = W3CBaggageTests.Config.class) +public class W3CBaggageTests extends org.springframework.cloud.sleuth.baggage.W3CBaggageTests { + + @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/common/src/main/java/org/springframework/cloud/sleuth/baggage/W3CBaggageTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/W3CBaggageTests.java new file mode 100644 index 000000000..8ec9334dd --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/W3CBaggageTests.java @@ -0,0 +1,99 @@ +/* + * 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.baggage; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cloud.sleuth.BaggageInScope; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.Tracer; +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; + +/** + * @author Taras Danylchuk + */ +@ContextConfiguration(classes = W3CBaggageTests.TestConfig.class) +@TestPropertySource(properties = { "spring.sleuth.baggage.remote-fields[0]=foo", "spring.sleuth.propagation.type=W3C" }) +public abstract class W3CBaggageTests { + + @Autowired + Tracer tracer; + + @Autowired + RestTemplate restTemplate; + + @Autowired + MockWebServer mockWebServer; + + @Test + @SuppressWarnings("unchecked") + public void shouldProduceOnlyW3cBaggageEntries() throws InterruptedException { + this.mockWebServer.enqueue(new MockResponse().setBody("hello")); + Span span = this.tracer.nextSpan(); + + try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(span.start())) { + try (BaggageInScope bs = this.tracer.createBaggage("foo").set("bar")) { + // when + this.restTemplate.getForObject(this.mockWebServer.url("/baggage").toString(), String.class); + + // then + RecordedRequest request = this.mockWebServer.takeRequest(1, TimeUnit.SECONDS); + Map> map = request.getHeaders().toMultimap(); + BDDAssertions.then(map).doesNotContainKey("foo"); + List baggage = map.get("baggage"); + BDDAssertions.then(baggage.stream().anyMatch(s -> s.contains("foo=bar"))).isTrue(); + } + finally { + span.end(); + } + } + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + static class TestConfig { + + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + MockWebServer mockWebServer() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + return mockWebServer; + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml index 186ac86b5..21316e837 100644 --- a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml @@ -90,6 +90,16 @@ org.awaitility awaitility + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/MultipleHopsIntegrationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/MultipleHopsIntegrationTests.java index cfad2ca4a..90abc146d 100644 --- a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/MultipleHopsIntegrationTests.java +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/MultipleHopsIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.context.ApplicationListener; 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 static java.util.Arrays.asList; import static org.assertj.core.api.BDDAssertions.then; @@ -42,6 +43,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @SpringBootTest(webEnvironment = RANDOM_PORT) @ContextConfiguration(classes = MultipleHopsIntegrationTests.Config.class) +@TestPropertySource(properties = "spring.sleuth.otel.propagation.sleuth-baggage.enabled=true") public class MultipleHopsIntegrationTests extends org.springframework.cloud.sleuth.baggage.multiple.MultipleHopsIntegrationTests { diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/W3CBaggageTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/W3CBaggageTests.java new file mode 100644 index 000000000..64aba3a78 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/bridge/W3CBaggageTests.java @@ -0,0 +1,49 @@ +/* + * 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.otel.bridge; + +import io.opentelemetry.sdk.trace.samplers.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Taras Danylchuk + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ContextConfiguration(classes = W3CBaggageTests.Config.class) +public class W3CBaggageTests extends org.springframework.cloud.sleuth.baggage.W3CBaggageTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.alwaysOn(); + } + + } + +}