Merge branch '3.0.x' into 3.1.x

pull/36038/head
Phillip Webb 1 year ago
commit baddf4c857

@ -187,8 +187,7 @@ public class BraveAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
Factory propagationFactory(TracingProperties properties) { Factory propagationFactory(TracingProperties properties) {
return CompositePropagationFactory.create(properties.getPropagation().getEffectiveProducedTypes(), return CompositePropagationFactory.create(properties.getPropagation(), null);
properties.getPropagation().getEffectiveConsumedTypes());
} }
} }
@ -207,10 +206,9 @@ public class BraveAutoConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
BaggagePropagation.FactoryBuilder propagationFactoryBuilder( BaggagePropagation.FactoryBuilder propagationFactoryBuilder(
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) { ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
Factory delegate = CompositePropagationFactory.create(BRAVE_BAGGAGE_MANAGER, CompositePropagationFactory factory = CompositePropagationFactory
this.tracingProperties.getPropagation().getEffectiveProducedTypes(), .create(this.tracingProperties.getPropagation(), BRAVE_BAGGAGE_MANAGER);
this.tracingProperties.getPropagation().getEffectiveConsumedTypes()); FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(factory);
FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate);
baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder; return builder;
} }

@ -19,156 +19,104 @@ package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import brave.internal.propagation.StringPropagationAdapter; import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.B3Propagation; import brave.propagation.B3Propagation;
import brave.propagation.Propagation; import brave.propagation.Propagation;
import brave.propagation.Propagation.Factory;
import brave.propagation.TraceContext; import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags; import brave.propagation.TraceContextOrSamplingFlags;
import graphql.com.google.common.collect.Streams;
import io.micrometer.tracing.BaggageManager; import io.micrometer.tracing.BaggageManager;
import io.micrometer.tracing.brave.bridge.W3CPropagation; import io.micrometer.tracing.brave.bridge.W3CPropagation;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Propagation.PropagationType;
/** /**
* {@link Factory} which supports multiple tracing formats. It is able to configure * {@link brave.propagation.Propagation.Factory Propagation factory} which supports
* different formats for injecting and for extracting. * multiple tracing formats. It is able to configure different formats for injecting and
* for extracting.
* *
* @author Marcin Grzejszczak * @author Marcin Grzejszczak
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Phillip Webb
*/ */
class CompositePropagationFactory extends Propagation.Factory implements Propagation<String> { class CompositePropagationFactory extends Propagation.Factory {
private final Collection<Propagation.Factory> injectorFactories;
private final Collection<Propagation.Factory> extractorFactories;
private final List<Propagation<String>> injectors; private final PropagationFactories injectors;
private final List<Propagation<String>> extractors;
private final boolean supportsJoin; private final PropagationFactories extractors;
private final boolean requires128BitTraceId; private final CompositePropagation propagation;
private final List<String> keys;
CompositePropagationFactory(Collection<Factory> injectorFactories, Collection<Factory> extractorFactories) { CompositePropagationFactory(Collection<Factory> injectorFactories, Collection<Factory> extractorFactories) {
this.injectorFactories = injectorFactories; this.injectors = new PropagationFactories(injectorFactories);
this.extractorFactories = extractorFactories; this.extractors = new PropagationFactories(extractorFactories);
this.injectors = this.injectorFactories.stream().map(Factory::get).toList(); this.propagation = new CompositePropagation(this.injectors, this.extractors);
this.extractors = this.extractorFactories.stream().map(Factory::get).toList();
this.supportsJoin = Stream.concat(this.injectorFactories.stream(), this.extractorFactories.stream())
.allMatch(Factory::supportsJoin);
this.requires128BitTraceId = Stream.concat(this.injectorFactories.stream(), this.extractorFactories.stream())
.anyMatch(Factory::requires128BitTraceId);
this.keys = Stream.concat(this.injectors.stream(), this.extractors.stream())
.flatMap((entry) -> entry.keys().stream())
.distinct()
.toList();
} }
Collection<Factory> getInjectorFactories() { Stream<Factory> getInjectors() {
return this.injectorFactories; return this.injectors.stream();
}
@Override
public List<String> keys() {
return this.keys;
} }
@Override @Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) { public boolean supportsJoin() {
return (traceContext, request) -> { return this.injectors.supportsJoin() && this.extractors.supportsJoin();
for (Propagation<String> injector : this.injectors) {
injector.injector(setter).inject(traceContext, request);
}
};
} }
@Override @Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) { public boolean requires128BitTraceId() {
return (request) -> { return this.injectors.requires128BitTraceId() || this.extractors.requires128BitTraceId();
for (Propagation<String> extractor : this.extractors) {
TraceContextOrSamplingFlags extract = extractor.extractor(getter).extract(request);
if (extract != TraceContextOrSamplingFlags.EMPTY) {
return extract;
}
}
return TraceContextOrSamplingFlags.EMPTY;
};
} }
@Override @Override
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public <K> Propagation<K> create(KeyFactory<K> keyFactory) { public <K> Propagation<K> create(Propagation.KeyFactory<K> keyFactory) {
return StringPropagationAdapter.create(this, keyFactory); return StringPropagationAdapter.create(this.propagation, keyFactory);
}
@Override
public boolean supportsJoin() {
return this.supportsJoin;
}
@Override
public boolean requires128BitTraceId() {
return this.requires128BitTraceId;
} }
@Override @Override
public TraceContext decorate(TraceContext context) { public TraceContext decorate(TraceContext context) {
for (Factory injectorFactory : this.injectorFactories) { return Streams.concat(this.injectors.stream(), this.extractors.stream())
TraceContext decorated = injectorFactory.decorate(context); .map((factory) -> factory.decorate(context))
if (decorated != context) { .filter((decorated) -> decorated != context)
return decorated; .findFirst()
} .orElse(context);
}
for (Factory extractorFactory : this.extractorFactories) {
TraceContext decorated = extractorFactory.decorate(context);
if (decorated != context) {
return decorated;
}
}
return super.decorate(context);
} }
/** /**
* Creates a new {@link CompositePropagationFactory}, which uses the given * Creates a new {@link CompositePropagationFactory}, which uses the given
* {@code injectionTypes} for injection and {@code extractionTypes} for extraction. * {@code injectionTypes} for injection and {@code extractionTypes} for extraction.
* @param properties the propagation properties
* @param baggageManager the baggage manager to use, or {@code null} * @param baggageManager the baggage manager to use, or {@code null}
* @param injectionTypes the propagation types for injection
* @param extractionTypes the propagation types for extraction
* @return the {@link CompositePropagationFactory} * @return the {@link CompositePropagationFactory}
*/ */
static CompositePropagationFactory create(BaggageManager baggageManager, static CompositePropagationFactory create(TracingProperties.Propagation properties, BaggageManager baggageManager) {
Collection<TracingProperties.Propagation.PropagationType> injectionTypes, PropagationFactoryMapper mapper = new PropagationFactoryMapper(baggageManager);
Collection<TracingProperties.Propagation.PropagationType> extractionTypes) { List<Factory> injectors = properties.getEffectiveProducedTypes().stream().map(mapper::map).toList();
List<Factory> injectors = injectionTypes.stream() List<Factory> extractors = properties.getEffectiveConsumedTypes().stream().map(mapper::map).toList();
.map((injection) -> factoryForType(baggageManager, injection))
.toList();
List<Factory> extractors = extractionTypes.stream()
.map((extraction) -> factoryForType(baggageManager, extraction))
.toList();
return new CompositePropagationFactory(injectors, extractors); return new CompositePropagationFactory(injectors, extractors);
} }
/** /**
* Creates a new {@link CompositePropagationFactory}, which uses the given * Mapper used to create a {@link brave.propagation.Propagation.Factory Propagation
* {@code injectionTypes} for injection and {@code extractionTypes} for extraction. * factory} from a {@link PropagationType}.
* @param injectionTypes the propagation types for injection
* @param extractionTypes the propagation types for extraction
* @return the {@link CompositePropagationFactory}
*/ */
static CompositePropagationFactory create(Collection<TracingProperties.Propagation.PropagationType> injectionTypes, private static class PropagationFactoryMapper {
Collection<TracingProperties.Propagation.PropagationType> extractionTypes) {
return create(null, injectionTypes, extractionTypes); private final BaggageManager baggageManager;
PropagationFactoryMapper(BaggageManager baggageManager) {
this.baggageManager = baggageManager;
} }
private static Factory factoryForType(BaggageManager baggageManager, Propagation.Factory map(PropagationType type) {
TracingProperties.Propagation.PropagationType type) {
return switch (type) { return switch (type) {
case B3 -> b3Single(); case B3 -> b3Single();
case B3_MULTI -> b3Multi(); case B3_MULTI -> b3Multi();
case W3C -> w3c(baggageManager); case W3C -> w3c();
}; };
} }
@ -176,7 +124,7 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
* Creates a new B3 propagation factory using a single B3 header. * Creates a new B3 propagation factory using a single B3 header.
* @return the B3 propagation factory * @return the B3 propagation factory
*/ */
private static Factory b3Single() { private Propagation.Factory b3Single() {
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
} }
@ -184,18 +132,93 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
* Creates a new B3 propagation factory using multiple B3 headers. * Creates a new B3 propagation factory using multiple B3 headers.
* @return the B3 propagation factory * @return the B3 propagation factory
*/ */
private static Factory b3Multi() { private Propagation.Factory b3Multi() {
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.MULTI).build(); return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.MULTI).build();
} }
/** /**
* Creates a new W3C propagation factory. * Creates a new W3C propagation factory.
* @param baggageManager baggage manager to use, or {@code null}
* @return the W3C propagation factory * @return the W3C propagation factory
*/ */
private static W3CPropagation w3c(BaggageManager baggageManager) { private Propagation.Factory w3c() {
return (baggageManager != null) ? new W3CPropagation(baggageManager, Collections.emptyList()) return (this.baggageManager != null) ? new W3CPropagation(this.baggageManager, Collections.emptyList())
: new W3CPropagation(); : new W3CPropagation();
} }
}
/**
* A collection of propagation factories.
*/
private static class PropagationFactories {
private final List<Propagation.Factory> factories;
PropagationFactories(Collection<Factory> factories) {
this.factories = List.copyOf(factories);
}
boolean requires128BitTraceId() {
return stream().anyMatch(Propagation.Factory::requires128BitTraceId);
}
boolean supportsJoin() {
return stream().allMatch(Propagation.Factory::supportsJoin);
}
List<Propagation<String>> get() {
return stream().map(Factory::get).toList();
}
Stream<Factory> stream() {
return this.factories.stream();
}
}
/**
* A composite {@link Propagation}.
*/
private static class CompositePropagation implements Propagation<String> {
private final List<Propagation<String>> injectors;
private final List<Propagation<String>> extractors;
private final List<String> keys;
CompositePropagation(PropagationFactories injectorFactories, PropagationFactories extractorFactories) {
this.injectors = injectorFactories.get();
this.extractors = extractorFactories.get();
this.keys = Stream.concat(keys(this.injectors), keys(this.extractors)).distinct().toList();
}
private Stream<String> keys(List<Propagation<String>> propagations) {
return propagations.stream().flatMap((propagation) -> propagation.keys().stream());
}
@Override
public List<String> keys() {
return this.keys;
}
@Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
return (traceContext, request) -> this.injectors.stream()
.map((propagation) -> propagation.injector(setter))
.forEach((injector) -> injector.inject(traceContext, request));
}
@Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
return (request) -> this.extractors.stream()
.map((propagation) -> propagation.extractor(getter))
.map((extractor) -> extractor.extract(request))
.filter(Predicate.not(TraceContextOrSamplingFlags.EMPTY::equals))
.findFirst()
.orElse(TraceContextOrSamplingFlags.EMPTY);
}
}
} }

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -32,6 +33,8 @@ import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.B3Propagator;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Propagation.PropagationType;
/** /**
* {@link TextMapPropagator} which supports multiple tracing formats. It is able to * {@link TextMapPropagator} which supports multiple tracing formats. It is able to
* configure different formats for injecting and for extracting. * configure different formats for injecting and for extracting.
@ -42,9 +45,9 @@ class CompositeTextMapPropagator implements TextMapPropagator {
private final Collection<TextMapPropagator> injectors; private final Collection<TextMapPropagator> injectors;
private final Collection<TextMapPropagator> mutuallyExclusiveExtractors; private final Collection<TextMapPropagator> extractors;
private final Collection<TextMapPropagator> alwaysRunningExtractors; private final TextMapPropagator baggagePropagator;
private final Set<String> fields; private final Set<String> fields;
@ -54,19 +57,24 @@ class CompositeTextMapPropagator implements TextMapPropagator {
* @param mutuallyExclusiveExtractors the mutually exclusive extractors. They are * @param mutuallyExclusiveExtractors the mutually exclusive extractors. They are
* applied in order, and as soon as an extractor extracts a context, the other * applied in order, and as soon as an extractor extracts a context, the other
* extractors after it are no longer invoked * extractors after it are no longer invoked
* @param alwaysRunningExtractors the always running extractors. They always run in * @param baggagePropagator the baggage propagator to use, or {@code null}
* order, regardless of the mutually exclusive extractors or whether the extractor
* before it has already extracted a context
*/ */
CompositeTextMapPropagator(Collection<TextMapPropagator> injectors, CompositeTextMapPropagator(Collection<TextMapPropagator> injectors,
Collection<TextMapPropagator> mutuallyExclusiveExtractors, Collection<TextMapPropagator> mutuallyExclusiveExtractors, TextMapPropagator baggagePropagator) {
Collection<TextMapPropagator> alwaysRunningExtractors) {
this.injectors = injectors; this.injectors = injectors;
this.mutuallyExclusiveExtractors = mutuallyExclusiveExtractors; this.extractors = mutuallyExclusiveExtractors;
this.alwaysRunningExtractors = alwaysRunningExtractors; this.baggagePropagator = baggagePropagator;
this.fields = concat(this.injectors, this.mutuallyExclusiveExtractors, this.alwaysRunningExtractors) Set<String> fields = new LinkedHashSet<>();
.flatMap((entry) -> entry.fields().stream()) fields(this.injectors).forEach(fields::add);
.collect(Collectors.toSet()); fields(this.extractors).forEach(fields::add);
if (baggagePropagator != null) {
fields.addAll(baggagePropagator.fields());
}
this.fields = Collections.unmodifiableSet(fields);
}
private Stream<String> fields(Collection<TextMapPropagator> propagators) {
return propagators.stream().flatMap((propagator) -> propagator.fields().stream());
} }
Collection<TextMapPropagator> getInjectors() { Collection<TextMapPropagator> getInjectors() {
@ -80,11 +88,8 @@ class CompositeTextMapPropagator implements TextMapPropagator {
@Override @Override
public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) { public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
if (context == null || setter == null) { if (context != null && setter != null) {
return; this.injectors.forEach((injector) -> injector.inject(context, carrier, setter));
}
for (TextMapPropagator injector : this.injectors) {
injector.inject(context, carrier, setter);
} }
} }
@ -96,70 +101,61 @@ class CompositeTextMapPropagator implements TextMapPropagator {
if (getter == null) { if (getter == null) {
return context; return context;
} }
Context currentContext = context; Context result = this.extractors.stream()
for (TextMapPropagator extractor : this.mutuallyExclusiveExtractors) { .map((extractor) -> extractor.extract(context, carrier, getter))
Context extractedContext = extractor.extract(currentContext, carrier, getter); .filter((extracted) -> extracted != context)
if (extractedContext != currentContext) { .findFirst()
currentContext = extractedContext; .orElse(context);
break; if (this.baggagePropagator != null) {
} result = this.baggagePropagator.extract(result, carrier, getter);
} }
for (TextMapPropagator extractor : this.alwaysRunningExtractors) { return result;
currentContext = extractor.extract(currentContext, carrier, getter);
}
return currentContext;
}
/**
* Creates a new {@link CompositeTextMapPropagator}, which uses the given
* {@code injectionTypes} for injection and {@code extractionTypes} for extraction.
* @param injectionTypes the propagation types for injection
* @param extractionTypes the propagation types for extraction
* @return the {@link CompositeTextMapPropagator}
*/
static TextMapPropagator create(Collection<TracingProperties.Propagation.PropagationType> injectionTypes,
Collection<TracingProperties.Propagation.PropagationType> extractionTypes) {
return create(null, injectionTypes, extractionTypes);
} }
/** /**
* Creates a new {@link CompositeTextMapPropagator}, which uses the given * Creates a new {@link CompositeTextMapPropagator}, which uses the given
* {@code injectionTypes} for injection and {@code extractionTypes} for extraction. * {@code injectionTypes} for injection and {@code extractionTypes} for extraction.
* @param properties the tracing properties
* @param baggagePropagator the baggage propagator to use, or {@code null} * @param baggagePropagator the baggage propagator to use, or {@code null}
* @param injectionTypes the propagation types for injection
* @param extractionTypes the propagation types for extraction
* @return the {@link CompositeTextMapPropagator} * @return the {@link CompositeTextMapPropagator}
*/ */
static CompositeTextMapPropagator create(TextMapPropagator baggagePropagator, static TextMapPropagator create(TracingProperties.Propagation properties, TextMapPropagator baggagePropagator) {
Collection<TracingProperties.Propagation.PropagationType> injectionTypes, TextMapPropagatorMapper mapper = new TextMapPropagatorMapper(baggagePropagator != null);
Collection<TracingProperties.Propagation.PropagationType> extractionTypes) { List<TextMapPropagator> injectors = properties.getEffectiveProducedTypes()
List<TextMapPropagator> injectors = injectionTypes.stream() .stream()
.map((injection) -> forType(injection, baggagePropagator != null)) .map(mapper::map)
.collect(Collectors.toCollection(ArrayList::new)); .collect(Collectors.toCollection(ArrayList::new));
if (baggagePropagator != null) { if (baggagePropagator != null) {
injectors.add(baggagePropagator); injectors.add(baggagePropagator);
} }
List<TextMapPropagator> extractors = extractionTypes.stream() List<TextMapPropagator> extractors = properties.getEffectiveProducedTypes().stream().map(mapper::map).toList();
.map((extraction) -> forType(extraction, baggagePropagator != null)) return new CompositeTextMapPropagator(injectors, extractors, baggagePropagator);
.toList();
return new CompositeTextMapPropagator(injectors, extractors,
(baggagePropagator != null) ? List.of(baggagePropagator) : Collections.emptyList());
} }
@SafeVarargs /**
private static <T> Stream<T> concat(Collection<T>... collections) { * Mapper used to create a {@link TextMapPropagator} from a {@link PropagationType}.
Stream<T> result = Stream.empty(); */
for (Collection<T> collection : collections) { private static class TextMapPropagatorMapper {
result = Stream.concat(result, collection.stream());
private final boolean baggage;
TextMapPropagatorMapper(boolean baggage) {
this.baggage = baggage;
} }
return result;
TextMapPropagator map(PropagationType type) {
return switch (type) {
case B3 -> b3Single();
case B3_MULTI -> b3Multi();
case W3C -> w3c();
};
} }
/** /**
* Creates a new B3 propagator using a single B3 header. * Creates a new B3 propagator using a single B3 header.
* @return the B3 propagator * @return the B3 propagator
*/ */
private static TextMapPropagator b3Single() { private TextMapPropagator b3Single() {
return B3Propagator.injectingSingleHeader(); return B3Propagator.injectingSingleHeader();
} }
@ -167,28 +163,19 @@ class CompositeTextMapPropagator implements TextMapPropagator {
* Creates a new B3 propagator using multiple B3 headers. * Creates a new B3 propagator using multiple B3 headers.
* @return the B3 propagator * @return the B3 propagator
*/ */
private static TextMapPropagator b3Multi() { private TextMapPropagator b3Multi() {
return B3Propagator.injectingMultiHeaders(); return B3Propagator.injectingMultiHeaders();
} }
/** /**
* Creates a new W3C propagator. * Creates a new W3C propagator.
* @param baggage whether baggage propagation should be supported
* @return the W3C propagator * @return the W3C propagator
*/ */
private static TextMapPropagator w3c(boolean baggage) { private TextMapPropagator w3c() {
if (!baggage) { return (!this.baggage) ? W3CTraceContextPropagator.getInstance() : TextMapPropagator
return W3CTraceContextPropagator.getInstance(); .composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance());
}
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance());
} }
private static TextMapPropagator forType(TracingProperties.Propagation.PropagationType type, boolean baggage) {
return switch (type) {
case B3 -> b3Single();
case B3_MULTI -> b3Multi();
case W3C -> w3c(baggage);
};
} }
} }

@ -195,9 +195,7 @@ public class OpenTelemetryAutoConfiguration {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
BaggageTextMapPropagator baggagePropagator = new BaggageTextMapPropagator(remoteFields, BaggageTextMapPropagator baggagePropagator = new BaggageTextMapPropagator(remoteFields,
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList())); new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()));
return CompositeTextMapPropagator.create(baggagePropagator, return CompositeTextMapPropagator.create(this.tracingProperties.getPropagation(), baggagePropagator);
this.tracingProperties.getPropagation().getEffectiveProducedTypes(),
this.tracingProperties.getPropagation().getEffectiveConsumedTypes());
} }
@Bean @Bean
@ -216,8 +214,7 @@ public class OpenTelemetryAutoConfiguration {
@Bean @Bean
TextMapPropagator textMapPropagator(TracingProperties properties) { TextMapPropagator textMapPropagator(TracingProperties properties) {
return CompositeTextMapPropagator.create(properties.getPropagation().getEffectiveProducedTypes(), return CompositeTextMapPropagator.create(properties.getPropagation(), null);
properties.getPropagation().getEffectiveConsumedTypes());
} }
} }

@ -222,10 +222,7 @@ public class TracingProperties {
* @return the effective context propagation types produced by the application * @return the effective context propagation types produced by the application
*/ */
List<PropagationType> getEffectiveProducedTypes() { List<PropagationType> getEffectiveProducedTypes() {
if (this.type != null) { return (this.type != null) ? this.type : this.produce;
return this.type;
}
return this.produce;
} }
/** /**
@ -234,10 +231,7 @@ public class TracingProperties {
* @return the effective context propagation types consumed by the application * @return the effective context propagation types consumed by the application
*/ */
List<PropagationType> getEffectiveConsumedTypes() { List<PropagationType> getEffectiveConsumedTypes() {
if (this.type != null) { return (this.type != null) ? this.type : this.consume;
return this.type;
}
return this.consume;
} }
/** /**

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import brave.Span; import brave.Span;
import brave.SpanCustomizer; import brave.SpanCustomizer;
@ -134,8 +135,8 @@ class BraveAutoConfigurationTests {
this.contextRunner.run((context) -> { this.contextRunner.run((context) -> {
assertThat(context).hasBean("propagationFactory"); assertThat(context).hasBean("propagationFactory");
Factory factory = context.getBean(Factory.class); Factory factory = context.getBean(Factory.class);
List<Factory> injectors = getInjectors(factory); Stream<Class<?>> injectors = getInjectors(factory).stream().map(Object::getClass);
assertThat(injectors).extracting(Factory::getClass).containsExactly(W3CPropagation.class); assertThat(injectors).containsExactly(W3CPropagation.class);
assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class); assertThat(context).hasSingleBean(BaggagePropagation.FactoryBuilder.class);
}); });
} }
@ -168,8 +169,8 @@ class BraveAutoConfigurationTests {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> { this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> {
assertThat(context).hasBean("propagationFactory"); assertThat(context).hasBean("propagationFactory");
Factory factory = context.getBean(Factory.class); Factory factory = context.getBean(Factory.class);
List<Factory> injectors = getInjectors(factory); Stream<Class<?>> injectors = getInjectors(factory).stream().map(Object::getClass);
assertThat(injectors).extracting(Factory::getClass).containsExactly(W3CPropagation.class); assertThat(injectors).containsExactly(W3CPropagation.class);
assertThat(context).doesNotHaveBean(BaggagePropagation.FactoryBuilder.class); assertThat(context).doesNotHaveBean(BaggagePropagation.FactoryBuilder.class);
}); });
} }
@ -321,7 +322,7 @@ class BraveAutoConfigurationTests {
private List<Factory> getInjectors(Factory factory) { private List<Factory> getInjectors(Factory factory) {
assertThat(factory).as("factory").isNotNull(); assertThat(factory).as("factory").isNotNull();
if (factory instanceof CompositePropagationFactory compositePropagationFactory) { if (factory instanceof CompositePropagationFactory compositePropagationFactory) {
return compositePropagationFactory.getInjectorFactories().stream().toList(); return compositePropagationFactory.getInjectors().toList();
} }
Assertions.fail("Expected CompositePropagationFactory, found %s".formatted(factory.getClass())); Assertions.fail("Expected CompositePropagationFactory, found %s".formatted(factory.getClass()));
throw new AssertionError("Unreachable"); throw new AssertionError("Unreachable");

@ -25,6 +25,7 @@ import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.Propagation; import brave.propagation.Propagation;
import brave.propagation.TraceContext; import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags; import brave.propagation.TraceContextOrSamplingFlags;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -39,63 +40,69 @@ import static org.mockito.BDDMockito.given;
*/ */
class CompositePropagationFactoryTests { class CompositePropagationFactoryTests {
@Test
void returnsAllKeys() {
CompositePropagationFactory factory = new CompositePropagationFactory(List.of(field("a")), List.of(field("b")));
assertThat(factory.keys()).containsExactly("a", "b");
}
@Test @Test
void supportsJoin() { void supportsJoin() {
Propagation.Factory supportsJoin = Mockito.mock(Propagation.Factory.class); Propagation.Factory supported = Mockito.mock(Propagation.Factory.class);
given(supportsJoin.supportsJoin()).willReturn(true); given(supported.supportsJoin()).willReturn(true);
given(supportsJoin.get()).willReturn(new DummyPropagation("a")); given(supported.get()).willReturn(new DummyPropagation("a"));
Propagation.Factory doesNotSupportsJoin = Mockito.mock(Propagation.Factory.class); Propagation.Factory unsupported = Mockito.mock(Propagation.Factory.class);
given(doesNotSupportsJoin.supportsJoin()).willReturn(false); given(unsupported.supportsJoin()).willReturn(false);
given(doesNotSupportsJoin.get()).willReturn(new DummyPropagation("a")); given(unsupported.get()).willReturn(new DummyPropagation("a"));
CompositePropagationFactory factory = new CompositePropagationFactory(List.of(supportsJoin), CompositePropagationFactory factory = new CompositePropagationFactory(List.of(supported), List.of(unsupported));
List.of(doesNotSupportsJoin));
assertThat(factory.supportsJoin()).isFalse(); assertThat(factory.supportsJoin()).isFalse();
} }
@Test @Test
void requires128BitTraceId() { void requires128BitTraceId() {
Propagation.Factory requires128BitTraceId = Mockito.mock(Propagation.Factory.class); Propagation.Factory required = Mockito.mock(Propagation.Factory.class);
given(requires128BitTraceId.requires128BitTraceId()).willReturn(true); given(required.requires128BitTraceId()).willReturn(true);
given(requires128BitTraceId.get()).willReturn(new DummyPropagation("a")); given(required.get()).willReturn(new DummyPropagation("a"));
Propagation.Factory doesNotRequire128BitTraceId = Mockito.mock(Propagation.Factory.class); Propagation.Factory notRequired = Mockito.mock(Propagation.Factory.class);
given(doesNotRequire128BitTraceId.requires128BitTraceId()).willReturn(false); given(notRequired.requires128BitTraceId()).willReturn(false);
given(doesNotRequire128BitTraceId.get()).willReturn(new DummyPropagation("a")); given(notRequired.get()).willReturn(new DummyPropagation("a"));
CompositePropagationFactory factory = new CompositePropagationFactory(List.of(requires128BitTraceId), CompositePropagationFactory factory = new CompositePropagationFactory(List.of(required), List.of(notRequired));
List.of(doesNotRequire128BitTraceId));
assertThat(factory.requires128BitTraceId()).isTrue(); assertThat(factory.requires128BitTraceId()).isTrue();
} }
@Nested
static class CompostePropagationTests {
@Test
void keys() {
CompositePropagationFactory factory = new CompositePropagationFactory(List.of(field("a")),
List.of(field("b")));
Propagation<String> propagation = factory.get();
assertThat(propagation.keys()).containsExactly("a", "b");
}
@Test @Test
void inject() { void inject() {
CompositePropagationFactory factory = new CompositePropagationFactory(List.of(field("a"), field("b")), CompositePropagationFactory factory = new CompositePropagationFactory(List.of(field("a"), field("b")),
List.of(field("c"))); List.of(field("c")));
Propagation<String> propagation = factory.get();
TraceContext context = context(); TraceContext context = context();
Map<String, String> request = new HashMap<>(); Map<String, String> request = new HashMap<>();
factory.injector(new MapSetter()).inject(context, request); propagation.injector(new MapSetter()).inject(context, request);
assertThat(request).containsOnly(entry("a", "a-value"), entry("b", "b-value")); assertThat(request).containsOnly(entry("a", "a-value"), entry("b", "b-value"));
} }
@Test @Test
void extractorStopsAfterSuccessfulExtraction() { void extractorWhenDelegateExtractsReturnsExtraction() {
CompositePropagationFactory factory = new CompositePropagationFactory(Collections.emptyList(), CompositePropagationFactory factory = new CompositePropagationFactory(Collections.emptyList(),
List.of(field("a"), field("b"))); List.of(field("a"), field("b")));
Propagation<String> propagation = factory.get();
Map<String, String> request = Map.of("a", "a-value", "b", "b-value"); Map<String, String> request = Map.of("a", "a-value", "b", "b-value");
TraceContextOrSamplingFlags context = factory.extractor(new MapGetter()).extract(request); TraceContextOrSamplingFlags context = propagation.extractor(new MapGetter()).extract(request);
assertThat(context.context().extra()).containsExactly("a"); assertThat(context.context().extra()).containsExactly("a");
} }
@Test @Test
void returnsEmptyContextWhenNoExtractorMatches() { void extractorWhenWhenNoExtractorMatchesReturnsEmptyContext() {
CompositePropagationFactory factory = new CompositePropagationFactory(Collections.emptyList(), CompositePropagationFactory factory = new CompositePropagationFactory(Collections.emptyList(),
Collections.emptyList()); Collections.emptyList());
Propagation<String> propagation = factory.get();
Map<String, String> request = Collections.emptyMap(); Map<String, String> request = Collections.emptyMap();
TraceContextOrSamplingFlags context = factory.extractor(new MapGetter()).extract(request); TraceContextOrSamplingFlags context = propagation.extractor(new MapGetter()).extract(request);
assertThat(context.context()).isNull(); assertThat(context.context()).isNull();
} }
@ -107,6 +114,8 @@ class CompositePropagationFactoryTests {
return new DummyPropagation(field); return new DummyPropagation(field);
} }
}
private static final class MapSetter implements Propagation.Setter<Map<String, String>, String> { private static final class MapSetter implements Propagation.Setter<Map<String, String>, String> {
@Override @Override

@ -51,14 +51,14 @@ class CompositeTextMapPropagatorTests {
@Test @Test
void collectsAllFields() { void collectsAllFields() {
CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(List.of(field("a")), List.of(field("b")), CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(List.of(field("a")), List.of(field("b")),
List.of(field("c"))); field("c"));
assertThat(propagator.fields()).containsExactly("a", "b", "c"); assertThat(propagator.fields()).containsExactly("a", "b", "c");
} }
@Test @Test
void injectAllFields() { void injectAllFields() {
CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(List.of(field("a"), field("b")), CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(List.of(field("a"), field("b")),
Collections.emptyList(), Collections.emptyList()); Collections.emptyList(), null);
TextMapSetter<Object> setter = setter(); TextMapSetter<Object> setter = setter();
Object carrier = carrier(); Object carrier = carrier();
propagator.inject(context(), carrier, setter); propagator.inject(context(), carrier, setter);
@ -68,9 +68,9 @@ class CompositeTextMapPropagatorTests {
} }
@Test @Test
void extractMutuallyExclusive() { void extractWithoutBaggagePropagator() {
CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(Collections.emptyList(), CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(Collections.emptyList(),
List.of(field("a"), field("b")), Collections.emptyList()); List.of(field("a"), field("b")), null);
Context context = context(); Context context = context();
Map<String, String> carrier = Map.of("a", "a-value", "b", "b-value"); Map<String, String> carrier = Map.of("a", "a-value", "b", "b-value");
context = propagator.extract(context, carrier, new MapTextMapGetter()); context = propagator.extract(context, carrier, new MapTextMapGetter());
@ -81,9 +81,9 @@ class CompositeTextMapPropagatorTests {
} }
@Test @Test
void extractAlwaysRunning() { void extractWithBaggagePropagator() {
CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(Collections.emptyList(), CompositeTextMapPropagator propagator = new CompositeTextMapPropagator(Collections.emptyList(),
List.of(field("a"), field("b")), List.of(field("c"))); List.of(field("a"), field("b")), field("c"));
Context context = context(); Context context = context();
Map<String, String> carrier = Map.of("a", "a-value", "b", "b-value", "c", "c-value"); Map<String, String> carrier = Map.of("a", "a-value", "b", "b-value", "c", "c-value");
context = propagator.extract(context, carrier, new MapTextMapGetter()); context = propagator.extract(context, carrier, new MapTextMapGetter());

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import io.micrometer.tracing.SpanCustomizer; import io.micrometer.tracing.SpanCustomizer;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
@ -177,9 +178,8 @@ class OpenTelemetryAutoConfigurationTests {
void shouldSupplyB3PropagationIfPropagationPropertySet() { void shouldSupplyB3PropagationIfPropagationPropertySet() {
this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> { this.contextRunner.withPropertyValues("management.tracing.propagation.type=B3").run((context) -> {
TextMapPropagator propagator = context.getBean(TextMapPropagator.class); TextMapPropagator propagator = context.getBean(TextMapPropagator.class);
List<TextMapPropagator> injectors = getInjectors(propagator); Stream<Class<?>> injectors = getInjectors(propagator).stream().map(Object::getClass);
assertThat(injectors).extracting(TextMapPropagator::getClass) assertThat(injectors).containsExactly(B3Propagator.class, BaggageTextMapPropagator.class);
.containsExactly(B3Propagator.class, BaggageTextMapPropagator.class);
}); });
} }
@ -189,8 +189,8 @@ class OpenTelemetryAutoConfigurationTests {
.withPropertyValues("management.tracing.propagation.type=B3", "management.tracing.baggage.enabled=false") .withPropertyValues("management.tracing.propagation.type=B3", "management.tracing.baggage.enabled=false")
.run((context) -> { .run((context) -> {
TextMapPropagator propagator = context.getBean(TextMapPropagator.class); TextMapPropagator propagator = context.getBean(TextMapPropagator.class);
List<TextMapPropagator> injectors = getInjectors(propagator); Stream<Class<?>> injectors = getInjectors(propagator).stream().map(Object::getClass);
assertThat(injectors).extracting(TextMapPropagator::getClass).containsExactly(B3Propagator.class); assertThat(injectors).containsExactly(B3Propagator.class);
}); });
} }
@ -211,9 +211,8 @@ class OpenTelemetryAutoConfigurationTests {
void shouldSupplyW3CPropagationWithoutBaggageWhenDisabled() { void shouldSupplyW3CPropagationWithoutBaggageWhenDisabled() {
this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> { this.contextRunner.withPropertyValues("management.tracing.baggage.enabled=false").run((context) -> {
TextMapPropagator propagator = context.getBean(TextMapPropagator.class); TextMapPropagator propagator = context.getBean(TextMapPropagator.class);
List<TextMapPropagator> injectors = getInjectors(propagator); Stream<Class<?>> injectors = getInjectors(propagator).stream().map(Object::getClass);
assertThat(injectors).extracting(TextMapPropagator::getClass) assertThat(injectors).containsExactly(W3CTraceContextPropagator.class);
.containsExactly(W3CTraceContextPropagator.class);
}); });
} }

@ -2,19 +2,15 @@
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config" <hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config hazelcast-client-config-5.0.xsd"> xsi:schemaLocation="http://www.hazelcast.com/schema/client-config hazelcast-client-config-5.0.xsd">
<instance-name>spring-boot</instance-name> <instance-name>spring-boot</instance-name>
<connection-strategy> <connection-strategy>
<connection-retry> <connection-retry>
<cluster-connect-timeout-millis>60000</cluster-connect-timeout-millis> <cluster-connect-timeout-millis>60000</cluster-connect-timeout-millis>
</connection-retry> </connection-retry>
</connection-strategy> </connection-strategy>
<network> <network>
<cluster-members> <cluster-members>
<address>${address}</address> <address>${address}</address>
</cluster-members> </cluster-members>
</network> </network>
</hazelcast-client> </hazelcast-client>

@ -135,7 +135,6 @@ class ConfigurationPropertiesBinder {
: new IgnoreTopLevelConverterNotFoundBindHandler(); : new IgnoreTopLevelConverterNotFoundBindHandler();
} }
@SuppressWarnings("unchecked")
private List<Validator> getValidators(Bindable<?> target) { private List<Validator> getValidators(Bindable<?> target) {
List<Validator> validators = new ArrayList<>(3); List<Validator> validators = new ArrayList<>(3);
if (this.configurationPropertiesValidator != null) { if (this.configurationPropertiesValidator != null) {
@ -144,15 +143,23 @@ class ConfigurationPropertiesBinder {
if (this.jsr303Present && target.getAnnotation(Validated.class) != null) { if (this.jsr303Present && target.getAnnotation(Validated.class) != null) {
validators.add(getJsr303Validator()); validators.add(getJsr303Validator());
} }
if (target.getValue() != null) { Validator selfValidator = getSelfValidator(target);
if (target.getValue().get() instanceof Validator validator) { if (selfValidator != null) {
validators.add(validator); validators.add(selfValidator);
} }
return validators;
} }
else if (Validator.class.isAssignableFrom(target.getType().resolve())) {
validators.add(new SelfValidatingConstructorBoundBindableValidator((Bindable<? extends Validator>) target)); private Validator getSelfValidator(Bindable<?> target) {
if (target.getValue() != null) {
Object value = target.getValue().get();
return (value instanceof Validator validator) ? validator : null;
} }
return validators; Class<?> type = target.getType().resolve();
if (Validator.class.isAssignableFrom(type)) {
return new SelfValidatingConstructorBoundBindableValidator(type);
}
return null;
} }
private Validator getJsr303Validator() { private Validator getJsr303Validator() {
@ -263,15 +270,15 @@ class ConfigurationPropertiesBinder {
*/ */
static class SelfValidatingConstructorBoundBindableValidator implements Validator { static class SelfValidatingConstructorBoundBindableValidator implements Validator {
private final Bindable<? extends Validator> bindable; private final Class<?> type;
SelfValidatingConstructorBoundBindableValidator(Bindable<? extends Validator> bindable) { SelfValidatingConstructorBoundBindableValidator(Class<?> type) {
this.bindable = bindable; this.type = type;
} }
@Override @Override
public boolean supports(Class<?> clazz) { public boolean supports(Class<?> candidate) {
return clazz.isAssignableFrom(this.bindable.getType().resolve()); return candidate.isAssignableFrom(this.type);
} }
@Override @Override

@ -392,11 +392,9 @@ class JavaBeanBinder implements DataObjectBinder {
} }
private boolean isUninitializedKotlinProperty(Exception ex) { private boolean isUninitializedKotlinProperty(Exception ex) {
if (ex instanceof InvocationTargetException ite) { return (ex instanceof InvocationTargetException invocationTargetException)
return "kotlin.UninitializedPropertyAccessException" && "kotlin.UninitializedPropertyAccessException"
.equals(ite.getTargetException().getClass().getName()); .equals(invocationTargetException.getTargetException().getClass().getName());
}
return false;
} }
boolean isSettable() { boolean isSettable() {

@ -16,6 +16,7 @@
package org.springframework.boot.image.assertions; package org.springframework.boot.image.assertions;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -106,8 +107,14 @@ public class ContainerConfigAssert extends AbstractAssert<ContainerConfigAssert,
return this.actual.extractingJsonPathArrayValue("$.processes[?(@.type=='%s')]", type) return this.actual.extractingJsonPathArrayValue("$.processes[?(@.type=='%s')]", type)
.singleElement() .singleElement()
.extracting("command", "args") .extracting("command", "args")
.flatMap((list) -> (list != null) ? ((List<?>) list).stream().map(Objects::toString).toList() .flatMap(this::getArgs);
: Collections.emptyList()); }
private Collection<String> getArgs(Object obj) {
if (obj instanceof List<?> list) {
return list.stream().map(Objects::toString).toList();
}
return Collections.emptyList();
} }
} }

Loading…
Cancel
Save