Fixed custom propagation mode option

with this change when CUSTOM mode is turned on we will search for user provided Propagation bean or else noop

fixes gh-1836
This commit is contained in:
Marcin Grzejszczak
2021-03-16 17:00:56 +01:00
parent 8d9e93fa08
commit 984f62c32d
4 changed files with 151 additions and 7 deletions

View File

@@ -30,6 +30,7 @@ import brave.propagation.TraceContextOrSamplingFlags;
import brave.propagation.aws.AWSPropagation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.sleuth.brave.propagation.PropagationFactorySupplier;
import org.springframework.cloud.sleuth.brave.propagation.PropagationType;
@@ -56,7 +57,7 @@ public class CompositePropagationFactorySupplier implements PropagationFactorySu
@Override
public Propagation.Factory get() {
return new CompositePropagationFactory(
return new CompositePropagationFactory(this.beanFactory,
this.beanFactory.getBeanProvider(BraveBaggageManager.class).getIfAvailable(BraveBaggageManager::new),
this.localFields, this.types);
}
@@ -69,8 +70,8 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
private final List<PropagationType> types;
CompositePropagationFactory(BraveBaggageManager braveBaggageManager, List<String> localFields,
List<PropagationType> types) {
CompositePropagationFactory(BeanFactory beanFactory, BraveBaggageManager braveBaggageManager,
List<String> localFields, List<PropagationType> types) {
this.types = types;
this.mapping.put(PropagationType.AWS, AWSPropagation.FACTORY.get());
// Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex
@@ -79,7 +80,7 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
this.mapping.put(PropagationType.B3,
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build().get());
this.mapping.put(PropagationType.W3C, new W3CPropagation(braveBaggageManager, localFields));
this.mapping.put(PropagationType.CUSTOM, NoOpPropagation.INSTANCE);
this.mapping.put(PropagationType.CUSTOM, new LazyPropagation(beanFactory.getBeanProvider(Propagation.class)));
}
@Override
@@ -116,6 +117,32 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
return StringPropagationAdapter.create(this, keyFactory);
}
@SuppressWarnings("unchecked")
private static final class LazyPropagation implements Propagation<String> {
private final ObjectProvider<Propagation> delegate;
private LazyPropagation(ObjectProvider<Propagation> delegate) {
this.delegate = delegate;
}
@Override
public List<String> keys() {
return this.delegate.getIfAvailable(() -> NoOpPropagation.INSTANCE).keys();
}
@Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
return this.delegate.getIfAvailable(() -> NoOpPropagation.INSTANCE).injector(setter);
}
@Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
return this.delegate.getIfAvailable(() -> NoOpPropagation.INSTANCE).extractor(getter);
}
}
private static class NoOpPropagation implements Propagation<String> {
static final NoOpPropagation INSTANCE = new NoOpPropagation();

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2013-2021 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.bridge;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import brave.internal.codec.HexCodec;
import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider;
import org.springframework.cloud.sleuth.brave.propagation.PropagationType;
import org.springframework.util.StringUtils;
class CompositePropagationFactorySupplierTests {
@Test
void should_pick_custom_registered_propagation_when_custom_mode_picked() {
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
Mockito.when(beanFactory.getBeanProvider(BraveBaggageManager.class))
.thenReturn(new SimpleObjectProvider(new BraveBaggageManager()));
Mockito.when(beanFactory.getBeanProvider(Propagation.class))
.thenReturn(new SimpleObjectProvider(new CustomTracePropagation()));
CompositePropagationFactorySupplier supplier = new CompositePropagationFactorySupplier(beanFactory,
Collections.emptyList(), Collections.singletonList(PropagationType.CUSTOM));
BDDAssertions.then(supplier.get().get().keys()).containsExactly(CustomTraceExtractor.CUSTOM_TRACE_HEADER);
}
}
class CustomTracePropagation extends Propagation.Factory implements Propagation<String> {
public static final List<String> KEYS = Collections.singletonList(CustomTraceExtractor.CUSTOM_TRACE_HEADER);
@Override
public List<String> keys() {
return KEYS;
}
@Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
return (traceContext, request) -> {
String trace = traceContext.traceIdString() + ":" + traceContext.spanIdString();
setter.put(request, CustomTraceExtractor.CUSTOM_TRACE_HEADER, trace);
};
}
@Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
Objects.requireNonNull(getter);
return new CustomTraceExtractor<>(getter);
}
@Override
public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return StringPropagationAdapter.create(this, keyFactory);
}
}
class CustomTraceExtractor<R> implements TraceContext.Extractor<R> {
static final String CUSTOM_TRACE_HEADER = "x-custom-trace";
final Propagation.Getter<R, String> getter;
CustomTraceExtractor(Propagation.Getter<R, String> getter) {
this.getter = getter;
}
@Override
@SuppressWarnings("ReturnCount")
public TraceContextOrSamplingFlags extract(R request) {
String traceString = getter.get(request, CUSTOM_TRACE_HEADER);
if (!StringUtils.hasText(traceString)) {
return TraceContextOrSamplingFlags.EMPTY;
}
String[] trace = traceString.split(":");
if (trace.length != 2) {
return TraceContextOrSamplingFlags.EMPTY;
}
try {
TraceContext traceContext = TraceContext.newBuilder().traceId(HexCodec.lowerHexToUnsignedLong(trace[0]))
.spanId(HexCodec.lowerHexToUnsignedLong(trace[1])).build();
return TraceContextOrSamplingFlags.create(traceContext);
}
catch (NumberFormatException ex) {
return TraceContextOrSamplingFlags.EMPTY;
}
}
}

View File

@@ -80,4 +80,3 @@ public class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
}
}

View File

@@ -27,8 +27,8 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;
/**
* {@link BeanPostProcessor} that ensures that each {@link Feign.Builder} has
* a trace representation of a {@link Client}.
* {@link BeanPostProcessor} that ensures that each {@link Feign.Builder} has a trace
* representation of a {@link Client}.
*
* @author Marcin Grzejszczak
* @since 3.0.2