From e19e0ca169d3518309dae95f5a79ff8f01bfade1 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 18 May 2020 23:08:08 +0800 Subject: [PATCH] Backports BaggagePropagation to 2.2.x (#1647) This allows integrations of Sleuth to use the same approach for 2.2.x as 3.x: If you have a custom base propagation format, override the `BaggagePropagation.Factory` bean instead of `ExtraFieldsPropagation.Factory` Note: one subtle difference 2.2.x to 3.x is the change in the primary inject format. 2.2.x is ```java return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder() .injectFormat(B3Propagation.Format.MULTI).build()); ``` 3.0 is ```java return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder() .injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build()); ``` per #1607 See https://github.com/spring-cloud/spring-cloud-gcp/issues/2268 --- .../autoconfig/TraceAutoConfiguration.java | 55 +----- .../autoconfig/TraceBaggageConfiguration.java | 180 ++++++++++++++++++ .../SleuthTagPropagationProperties.java | 4 +- ...gurationPropagationCustomizationTests.java | 5 +- .../TraceAutoConfigurationTests.java | 133 +++++++++++-- .../TraceBaggageConfigurationTests.java | 100 ++++++++++ 6 files changed, 402 insertions(+), 75 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java index 7b94d441c..95fb32cc8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java @@ -25,11 +25,8 @@ import brave.Tracer; import brave.Tracing; import brave.TracingCustomizer; import brave.handler.SpanHandler; -import brave.propagation.B3Propagation; import brave.propagation.CurrentTraceContext; import brave.propagation.CurrentTraceContextCustomizer; -import brave.propagation.ExtraFieldCustomizer; -import brave.propagation.ExtraFieldPropagation; import brave.propagation.Propagation; import brave.propagation.ThreadLocalCurrentTraceContext; import brave.sampler.Sampler; @@ -42,7 +39,6 @@ import zipkin2.reporter.Reporter; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -73,7 +69,8 @@ import org.springframework.util.StringUtils; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) @EnableConfigurationProperties(SleuthProperties.class) -@Import({ SleuthLogAutoConfiguration.class, SamplerAutoConfiguration.class }) +@Import({ SleuthLogAutoConfiguration.class, TraceBaggageConfiguration.class, + SamplerAutoConfiguration.class }) // public allows @AutoConfigureAfter(TraceAutoConfiguration) // for components needing Tracing public class TraceAutoConfiguration { @@ -88,9 +85,6 @@ public class TraceAutoConfiguration { */ public static final String DEFAULT_SERVICE_NAME = "default"; - @Autowired(required = false) - ExtraFieldPropagation.FactoryBuilder extraFieldPropagationFactoryBuilder; - @Bean @ConditionalOnMissingBean // NOTE: stable bean name as might be used outside sleuth @@ -138,51 +132,6 @@ public class TraceAutoConfiguration { return new DefaultSpanNamer(); } - @Bean - @ConditionalOnMissingBean - Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties, - List extraFieldCustomizers) { - if (extraFieldCustomizers == null) { - extraFieldCustomizers = Collections.emptyList(); - } - if (sleuthProperties.getBaggageKeys().isEmpty() - && sleuthProperties.getPropagationKeys().isEmpty() - && extraFieldCustomizers.isEmpty() - && this.extraFieldPropagationFactoryBuilder == null - && sleuthProperties.getLocalKeys().isEmpty()) { - return B3Propagation.FACTORY; - } - ExtraFieldPropagation.FactoryBuilder factoryBuilder; - if (this.extraFieldPropagationFactoryBuilder != null) { - factoryBuilder = this.extraFieldPropagationFactoryBuilder; - } - else { - factoryBuilder = ExtraFieldPropagation - .newFactoryBuilder(B3Propagation.FACTORY); - } - if (!sleuthProperties.getBaggageKeys().isEmpty()) { - factoryBuilder = factoryBuilder - // for HTTP - .addPrefixedFields("baggage-", sleuthProperties.getBaggageKeys()) - // for messaging - .addPrefixedFields("baggage_", sleuthProperties.getBaggageKeys()); - } - if (!sleuthProperties.getPropagationKeys().isEmpty()) { - for (String key : sleuthProperties.getPropagationKeys()) { - factoryBuilder = factoryBuilder.addField(key); - } - } - if (!sleuthProperties.getLocalKeys().isEmpty()) { - for (String key : sleuthProperties.getLocalKeys()) { - factoryBuilder = factoryBuilder.addRedactedField(key); - } - } - for (ExtraFieldCustomizer customizer : extraFieldCustomizers) { - customizer.customize(factoryBuilder); - } - return factoryBuilder.build(); - } - @Bean CurrentTraceContext sleuthCurrentTraceContext(CurrentTraceContext.Builder builder, @Nullable List scopeDecorators, diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java new file mode 100644 index 000000000..241bf550a --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java @@ -0,0 +1,180 @@ +/* + * 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.autoconfig; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import brave.baggage.BaggageField; +import brave.baggage.BaggagePropagation; +import brave.baggage.BaggagePropagationConfig.SingleBaggageField; +import brave.baggage.BaggagePropagationCustomizer; +import brave.propagation.B3Propagation; +import brave.propagation.ExtraFieldCustomizer; +import brave.propagation.ExtraFieldPropagation; +import brave.propagation.Propagation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; + +/** + * {@link Configuration} for {@link BaggagePropagation}. + *

+ * + * @author Spencer Gibb + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SleuthProperties.class) +class TraceBaggageConfiguration { + + static final Log logger = LogFactory.getLog(TraceBaggageConfiguration.class); + + static final String LOCAL_KEYS = "spring.sleuth.local-keys"; + static final String BAGGAGE_KEYS = "spring.sleuth.baggage-keys"; + static final String PROPAGATION_KEYS = "spring.sleuth.propagation-keys"; + + // These List beans allow us to get deprecated property values, regardless of + // if they were comma or yaml encoded. This keeps them out of SleuthBaggageProperties + + @Bean(BAGGAGE_KEYS) + @ConfigurationProperties(BAGGAGE_KEYS) + List baggageKeys() { + return new ArrayList<>(); + } + + @Bean(LOCAL_KEYS) + @ConfigurationProperties(LOCAL_KEYS) + List localKeys() { + return new ArrayList<>(); + } + + @Bean(PROPAGATION_KEYS) + @ConfigurationProperties(PROPAGATION_KEYS) + List propagationKeys() { + return new ArrayList<>(); + } + + /** + * To override the underlying context format, override this bean and set the delegate + * to what you need. {@link BaggagePropagation.FactoryBuilder} will unwrap itself if + * no fields are configured. + */ + @Bean + @ConditionalOnMissingBean + BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() { + // Default for spring-messaging is on 2.2.x is MULTI, though 3.x it is + // SINGLE_NO_PARENT spring-cloud/spring-cloud-sleuth#1607 + return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder() + .injectFormat(B3Propagation.Format.MULTI).build()); + } + + Propagation.Factory sleuthPropagation( + ExtraFieldPropagation.FactoryBuilder extraFieldPropagationFactoryBuilder, + List baggageKeys, List localKeys, + List propagationKeys, + @Nullable List extraFieldCustomizers) { + if (extraFieldCustomizers == null) { + extraFieldCustomizers = Collections.emptyList(); + } + ExtraFieldPropagation.FactoryBuilder factoryBuilder; + if (extraFieldPropagationFactoryBuilder != null) { + factoryBuilder = extraFieldPropagationFactoryBuilder; + } + else { + factoryBuilder = ExtraFieldPropagation + .newFactoryBuilder(B3Propagation.FACTORY); + } + if (!baggageKeys.isEmpty()) { + factoryBuilder + // for HTTP + .addPrefixedFields("baggage-", baggageKeys) + // for messaging + .addPrefixedFields("baggage_", baggageKeys); + } + for (String key : propagationKeys) { + factoryBuilder.addField(key); + } + for (String key : localKeys) { + factoryBuilder.addRedactedField(key); + } + for (ExtraFieldCustomizer customizer : extraFieldCustomizers) { + customizer.customize(factoryBuilder); + } + return factoryBuilder.build(); + } + + @Bean + @ConditionalOnMissingBean + Propagation.Factory sleuthPropagation( + @Nullable ExtraFieldPropagation.FactoryBuilder extraFieldPropagationFactoryBuilder, + @Nullable List extraFieldCustomizers, + BaggagePropagation.FactoryBuilder factoryBuilder, + @Qualifier(BAGGAGE_KEYS) List baggageKeys, + @Qualifier(LOCAL_KEYS) List localKeys, + @Qualifier(PROPAGATION_KEYS) List propagationKeys, + @Nullable List baggagePropagationCustomizers) { + + boolean useDeprecated = false; + if (extraFieldPropagationFactoryBuilder != null) { + logger.warn("ExtraFieldPropagation.FactoryBuilder is deprecated. " + + "Please switch to BaggagePropagation.FactoryBuilder"); + useDeprecated = true; + } + if (extraFieldCustomizers != null) { + logger.warn("ExtraFieldCustomizer is deprecated. " + + "Please switch to BaggagePropagationCustomizer"); + useDeprecated = true; + } + if (useDeprecated) { + return sleuthPropagation(extraFieldPropagationFactoryBuilder, localKeys, + propagationKeys, baggageKeys, extraFieldCustomizers); + } + + for (String fieldName : localKeys) { + factoryBuilder.add(SingleBaggageField.local(BaggageField.create(fieldName))); + } + + for (String fieldName : propagationKeys) { + factoryBuilder.add(SingleBaggageField.remote(BaggageField.create(fieldName))); + } + + for (String key : baggageKeys) { + factoryBuilder.add(SingleBaggageField.newBuilder(BaggageField.create(key)) + .addKeyName("baggage-" + key) // for HTTP + .addKeyName("baggage_" + key) // for messaging + .build()); + } + + if (baggagePropagationCustomizers != null) { + for (BaggagePropagationCustomizer customizer : baggagePropagationCustomizers) { + customizer.customize(factoryBuilder); + } + } + return factoryBuilder.build(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/propagation/SleuthTagPropagationProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/propagation/SleuthTagPropagationProperties.java index ff482e091..24e586788 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/propagation/SleuthTagPropagationProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/propagation/SleuthTagPropagationProperties.java @@ -32,8 +32,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; public class SleuthTagPropagationProperties { /** - * Enables a {@link TagPropagationSpanHandler} that adds extra propagated fields to - * span tags. + * Enables a {@link TagPropagationFinishedSpanHandler} that adds extra propagated + * fields to span tags. */ private boolean enabled = true; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java index a8f1a839a..62883f2b5 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java @@ -35,8 +35,7 @@ import org.springframework.context.support.GenericApplicationContext; public class TraceAutoConfigurationPropagationCustomizationTests { // Default for spring-messaging is on 2.2.x is MULTI, though 3.x it is - // SINGLE_NO_PARENT - // spring-cloud/spring-cloud-sleuth#1607 + // SINGLE_NO_PARENT spring-cloud/spring-cloud-sleuth#1607 Propagation.Factory defaultB3Propagation = B3Propagation.newFactoryBuilder() .injectFormat(Format.MULTI).build(); @@ -56,7 +55,7 @@ public class TraceAutoConfigurationPropagationCustomizationTests { this.contextRunner.withPropertyValues("spring.sleuth.baggage-keys=my-baggage") .run((context) -> { BDDAssertions.then(context.getBean(Propagation.Factory.class)) - .hasFieldOrPropertyWithValue("delegate.delegate", + .hasFieldOrPropertyWithValue("delegate", B3Propagation.FACTORY); }); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java index f61bf5902..a294edecb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java @@ -16,9 +16,19 @@ package org.springframework.cloud.sleuth.autoconfig; +import java.util.List; + +import brave.Tracing; +import brave.baggage.BaggageField; +import brave.baggage.BaggagePropagation; +import brave.baggage.BaggagePropagationConfig.SingleBaggageField; +import brave.baggage.BaggagePropagationCustomizer; import brave.propagation.B3Propagation; +import brave.propagation.B3SinglePropagation; import brave.propagation.ExtraFieldPropagation; import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; import brave.sampler.RateLimitingSampler; import brave.sampler.Sampler; import io.micrometer.core.instrument.MeterRegistry; @@ -30,6 +40,7 @@ import zipkin2.reporter.Reporter; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -119,17 +130,60 @@ public class TraceAutoConfigurationTests { })); } + @Test + public void should_use_baggageBean() { + this.contextRunner.withUserConfiguration(WithBaggageBeans.class, Baggage.class) + .run((context -> { + final Baggage bean = context.getBean(Baggage.class); + BDDAssertions.then(bean.fields).containsOnly( + BaggageField.create("country-code"), + BaggageField.create("x-vcap-request-id")); + })); + } + + @Test + public void should_combine_baggage_beans_and_properties() { + this.contextRunner.withPropertyValues("spring.sleuth.local-keys=bp") + .withUserConfiguration(WithBaggageBeans.class, Baggage.class) + .run((context -> { + final Baggage bean = context.getBean(Baggage.class); + BDDAssertions.then(bean.fields).containsOnly( + BaggageField.create("country-code"), + BaggageField.create("x-vcap-request-id"), + BaggageField.create("bp")); + })); + } + + @Test + public void should_use_baggagePropagationFactoryBuilder_bean() { + // BaggagePropagation.FactoryBuilder unwraps itself if there are no baggage fields + // defined + this.contextRunner + .withUserConfiguration(WithBaggagePropagationFactoryBuilderBean.class) + .run((context -> BDDAssertions + .then(context.getBean(Propagation.Factory.class)) + .isSameAs(B3SinglePropagation.FACTORY))); + } + @Test public void should_use_local_keys_from_properties() { - this.contextRunner.withUserConfiguration(WithLocalKeys.class).run((context -> { - final Propagation.Factory bean = context.getBean(Propagation.Factory.class); - BDDAssertions.then(bean).isInstanceOf(ExtraFieldPropagation.Factory.class); - })); + this.contextRunner.withPropertyValues("spring.sleuth.local-keys=bp") + .run((context -> { + final Propagation.Factory bean = context + .getBean(Propagation.Factory.class); + TraceContext ctx = bean.decorate( + TraceContext.newBuilder().traceId(1L).spanId(2L).build()); + BaggageField bp = BaggageField.create("bp"); + bp.updateValue(ctx, "accounting"); + + // If this works, it is configured! + BDDAssertions.then(bp.getValue(ctx)).isEqualTo("accounting"); + })); } @Test public void should_use_extraFieldPropagationFactoryBuilder_bean() { - this.contextRunner + this.contextRunner.withPropertyValues("spring.sleuth.local-keys=bp") .withUserConfiguration(WithExtraFieldPropagationFactoryBuilderBean.class) .run((context -> { final Propagation.Factory bean = context @@ -139,6 +193,63 @@ public class TraceAutoConfigurationTests { })); } + /** + * {@link BaggagePropagation.FactoryBuilder} is new: 2.2.x should prefer older on + * conflict. + */ + @Test + public void should_prefer_extraFieldPropagationFactoryBuilder_bean() { + this.contextRunner + .withUserConfiguration(WithExtraFieldPropagationFactoryBuilderBean.class) + .withUserConfiguration(WithBaggagePropagationFactoryBuilderBean.class) + .run((context -> BDDAssertions + .then(context.getBean(Propagation.Factory.class)) + .isInstanceOf(ExtraFieldPropagation.Factory.class))); + } + + @Configuration + static class Baggage { + + List fields; + + @Autowired + Baggage(Tracing tracing) { + // When predefined baggage fields exist, the result != + // TraceContextOrSamplingFlags.EMPTY + TraceContextOrSamplingFlags emptyExtraction = tracing.propagation() + .extractor((c, k) -> null).extract(Boolean.TRUE); + fields = BaggageField.getAll(emptyExtraction); + } + + } + + @Configuration + static class WithBaggageBeans { + + @Bean + BaggagePropagationCustomizer countryCode() { + return fb -> fb + .add(SingleBaggageField.remote(BaggageField.create("country-code"))); + } + + @Bean + BaggagePropagationCustomizer requestId() { + return fb -> fb.add( + SingleBaggageField.remote(BaggageField.create("x-vcap-request-id"))); + } + + } + + @Configuration + static class WithBaggagePropagationFactoryBuilderBean { + + @Bean + BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilderBean() { + return BaggagePropagation.newFactoryBuilder(B3SinglePropagation.FACTORY); + } + + } + @Configuration static class WithMeterRegistry { @@ -169,18 +280,6 @@ public class TraceAutoConfigurationTests { } - @Configuration - static class WithLocalKeys { - - @Bean - SleuthProperties sleuthProperties() { - final SleuthProperties sleuthProperties = new SleuthProperties(); - sleuthProperties.getLocalKeys().add("test-key"); - return sleuthProperties; - } - - } - @Configuration static class WithExtraFieldPropagationFactoryBuilderBean { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java new file mode 100644 index 000000000..0821fbd14 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java @@ -0,0 +1,100 @@ +/* + * 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.autoconfig; + +import brave.baggage.BaggageField; +import brave.baggage.BaggagePropagationConfig.SingleBaggageField; +import brave.baggage.BaggagePropagationCustomizer; +import brave.propagation.Propagation; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.assertj.core.api.ListAssert; +import org.assertj.core.groups.Tuple; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class TraceBaggageConfigurationTests { + + static final String[] EMPTY_ARRAY = {}; + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceBaggageConfiguration.class)); + + @Test + public void shouldCreateLocalFields() { + this.contextRunner.withPropertyValues("spring.sleuth.local-keys=bp") + .run((context) -> assertThatBaggageFieldNameToKeyNames(context) + .containsOnly(tuple("bp", EMPTY_ARRAY))); + } + + static ListAssert assertThatBaggageFieldNameToKeyNames( + AssertableApplicationContext context) { + return assertThat(context.getBean(Propagation.Factory.class)) + .extracting("configs").asInstanceOf(InstanceOfAssertFactories.ARRAY) + .extracting("field.name", "keyNames.toArray") + .asInstanceOf(InstanceOfAssertFactories.list(Tuple.class)); + } + + @Test + public void shouldCreateRemoteFields() { + this.contextRunner + .withPropertyValues( + "spring.sleuth.propagation-keys=x-vcap-request-id,country-code") + .run((context) -> assertThatBaggageFieldNameToKeyNames(context) + .containsOnly( + tuple("x-vcap-request-id", + new String[] { "x-vcap-request-id" }), + tuple("country-code", new String[] { "country-code" }))); + } + + @Test + public void shouldCreateBaggageFields() { + this.contextRunner.withPropertyValues("spring.sleuth.baggage-keys=country-code") + .run((context) -> assertThatBaggageFieldNameToKeyNames(context) + .containsOnly(tuple("country-code", new String[] { + "baggage-country-code", "baggage_country-code" }))); + } + + @Test + public void canCreateBaggageFieldsWithJavaConfig() { + this.contextRunner.withUserConfiguration(CustomBaggageConfiguration.class) + .run((context) -> assertThatBaggageFieldNameToKeyNames(context) + .containsOnly(tuple("country-code", new String[] { + "baggage-country-code", "baggage_country-code" }))); + } + + @Configuration + static class CustomBaggageConfiguration { + + @Bean + BaggagePropagationCustomizer countryCodeBaggageConfig() { + return fb -> fb.add( + SingleBaggageField.newBuilder(BaggageField.create("country-code")) + .addKeyName("baggage-country-code") + .addKeyName("baggage_country-code").build()); + } + + } + +}