Added baggage support (#440)
without this change there is no support for context propagation with this change whenever you pass the `baggage-...` for http or `baggage_` for messaging headers then such a value will be propagated through your system fixes #237
This commit is contained in:
committed by
GitHub
parent
9ab37c34fa
commit
96df523557
@@ -28,13 +28,13 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class for gathering and reporting statistics about a block of execution.
|
||||
* <p>
|
||||
@@ -73,7 +73,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
*/
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
public class Span {
|
||||
public class Span implements SpanContext {
|
||||
|
||||
public static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
public static final String PROCESS_ID_NAME = "X-Process-Id";
|
||||
@@ -82,6 +82,7 @@ public class Span {
|
||||
public static final String SPAN_NAME_NAME = "X-Span-Name";
|
||||
public static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
|
||||
public static final String SPAN_BAGGAGE_HEADER_PREFIX = "baggage";
|
||||
public static final Set<String> SPAN_HEADERS = new HashSet<>(
|
||||
Arrays.asList(SAMPLED_NAME, PROCESS_ID_NAME, PARENT_ID_NAME, TRACE_ID_NAME,
|
||||
SPAN_ID_NAME, SPAN_NAME_NAME, SPAN_EXPORT_NAME));
|
||||
@@ -146,6 +147,8 @@ public class Span {
|
||||
private final String processId;
|
||||
private final Collection<Log> logs;
|
||||
private final Span savedSpan;
|
||||
@JsonIgnore
|
||||
private final Map<String,String> baggage;
|
||||
|
||||
// Null means we don't know the start tick, so fallback to time
|
||||
@JsonIgnore
|
||||
@@ -176,6 +179,7 @@ public class Span {
|
||||
this.logs = current.logs;
|
||||
this.startNanos = current.startNanos;
|
||||
this.durationMicros = current.durationMicros;
|
||||
this.baggage = current.baggage;
|
||||
this.savedSpan = savedSpan;
|
||||
}
|
||||
|
||||
@@ -209,6 +213,7 @@ public class Span {
|
||||
this.savedSpan = savedSpan;
|
||||
this.tags = new ConcurrentHashMap<>();
|
||||
this.logs = new ConcurrentLinkedQueue<>();
|
||||
this.baggage = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static SpanBuilder builder() {
|
||||
@@ -304,6 +309,40 @@ public class Span {
|
||||
this.logs.add(new Log(System.currentTimeMillis(), event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a baggage item in the Span (and its SpanContext) as a key/value pair.
|
||||
*
|
||||
* Baggage enables powerful distributed context propagation functionality where arbitrary application data can be
|
||||
* carried along the full path of request execution throughout the system.
|
||||
*
|
||||
* Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext.
|
||||
*
|
||||
* Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with
|
||||
* care.
|
||||
*
|
||||
* @return this Span instance, for chaining
|
||||
*/
|
||||
public Span setBaggageItem(String key, String value) {
|
||||
this.baggage.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of the baggage item identified by the given key, or null if no such item could be found
|
||||
*/
|
||||
public String getBaggageItem(String key) {
|
||||
return this.baggage.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<Map.Entry<String,String>> baggageItems() {
|
||||
return this.baggage.entrySet();
|
||||
}
|
||||
|
||||
public final Map<String,String> getBaggage() {
|
||||
return Collections.unmodifiableMap(this.baggage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag data associated with this span (read only)
|
||||
* <p/>
|
||||
@@ -493,6 +532,7 @@ public class Span {
|
||||
private Span savedSpan;
|
||||
private List<Log> logs = new ArrayList<>();
|
||||
private Map<String, String> tags = new LinkedHashMap<>();
|
||||
private Map<String, String> baggage = new LinkedHashMap<>();
|
||||
|
||||
SpanBuilder() {
|
||||
}
|
||||
@@ -554,6 +594,16 @@ public class Span {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(String baggageKey, String baggageValue) {
|
||||
this.baggage.put(baggageKey, baggageValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(Map<String, String> baggage) {
|
||||
this.baggage.putAll(baggage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder spanId(long spanId) {
|
||||
this.spanId = spanId;
|
||||
return this;
|
||||
@@ -585,6 +635,7 @@ public class Span {
|
||||
this.processId, this.savedSpan);
|
||||
span.logs.addAll(this.logs);
|
||||
span.tags.putAll(this.tags);
|
||||
span.baggage.putAll(this.baggage);
|
||||
return span;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/SpanContext.java
|
||||
*
|
||||
* SpanContext represents Span state that must propagate to descendant Spans and across process boundaries.
|
||||
*
|
||||
* SpanContext is logically divided into two pieces: (1) the user-level "Baggage" that propagates across Span
|
||||
* boundaries and (2) any Tracer-implementation-specific fields that are needed to identify or otherwise contextualize
|
||||
* the associated Span instance (e.g., a <trace_id, span_id, sampled> tuple).
|
||||
*
|
||||
* The {@link SpanContext#baggageItems()} returns the map of user-level "Baggage".
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface SpanContext {
|
||||
|
||||
/**
|
||||
* @return all zero or more baggage items propagating along with the associated Span
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*/
|
||||
Iterable<Map.Entry<String, String>> baggageItems();
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/master/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java
|
||||
*
|
||||
* TextMap is a built-in carrier for {@link SpanInjector} and {@link SpanExtractor}. TextMap implementations allows Tracers to
|
||||
* read and write key:value String pairs from arbitrary underlying sources of data.
|
||||
|
||||
@@ -43,6 +43,11 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
|
||||
}
|
||||
setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME);
|
||||
spanBuilder.remote(true);
|
||||
for (Map.Entry<String, String> entry : carrier.entrySet()) {
|
||||
if (entry.getKey().startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER)) {
|
||||
spanBuilder.baggage(unprefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return spanBuilder.build();
|
||||
}
|
||||
|
||||
@@ -58,4 +63,8 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
|
||||
}
|
||||
}
|
||||
|
||||
private String unprefixedKey(String key) {
|
||||
return key.substring(key.indexOf(TraceMessageHeaders.HEADER_DELIMITER) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto
|
||||
else {
|
||||
addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : span.baggageItems()) {
|
||||
textMap.put(prefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void addAnnotations(TraceKeys traceKeys, SpanTextMap spanTextMap, Span span) {
|
||||
@@ -99,4 +102,11 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
private String prefixedKey(String key) {
|
||||
if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER )) {
|
||||
return key;
|
||||
}
|
||||
return Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER + key;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class TraceMessageHeaders {
|
||||
public static final String SPAN_NAME_NAME = "spanName";
|
||||
|
||||
static final String MESSAGE_SENT_FROM_CLIENT = "messageSent";
|
||||
static final String HEADER_DELIMITER = "_";
|
||||
|
||||
private TraceMessageHeaders() {}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor {
|
||||
|
||||
private static final org.apache.commons.logging.Log log = LogFactory.getLog(
|
||||
MethodHandles.lookup().lookupClass());
|
||||
private static final String HEADER_DELIMITER = "-";
|
||||
static final String URI_HEADER = "X-Span-Uri";
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
@@ -83,7 +84,16 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor {
|
||||
if (skip) {
|
||||
span.exportable(false);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : carrier.entrySet()) {
|
||||
if (entry.getKey().startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER)) {
|
||||
span.baggage(unprefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return span.build();
|
||||
}
|
||||
|
||||
private String unprefixedKey(String key) {
|
||||
return key.substring(key.indexOf(HEADER_DELIMITER) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -12,6 +14,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ZipkinHttpSpanInjector implements HttpSpanInjector {
|
||||
|
||||
private static final String HEADER_DELIMITER = "-";
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
@@ -20,22 +24,32 @@ public class ZipkinHttpSpanInjector implements HttpSpanInjector {
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
for (Map.Entry<String, String> entry : span.baggageItems()) {
|
||||
carrier.put(prefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String prefixedKey(String key) {
|
||||
if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER)) {
|
||||
return key;
|
||||
}
|
||||
return Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER + key;
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
private void setHeader(SpanTextMap carrier, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
carrier.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIdHeader(SpanTextMap carrier, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(carrier, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void setHeader(SpanTextMap carrier, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
carrier.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class DefaultTracer implements Tracer {
|
||||
return savedSpan;
|
||||
}
|
||||
|
||||
protected Span createChild(Span parent, String name) {
|
||||
Span createChild(Span parent, String name) {
|
||||
long id = createId();
|
||||
if (parent == null) {
|
||||
Span span = Span.builder().name(name)
|
||||
@@ -154,7 +154,9 @@ public class DefaultTracer implements Tracer {
|
||||
Span span = Span.builder().name(name)
|
||||
.traceId(parent.getTraceId()).parent(parent.getSpanId()).spanId(id)
|
||||
.processId(parent.getProcessId()).savedSpan(parent)
|
||||
.exportable(parent.isExportable()).build();
|
||||
.exportable(parent.isExportable())
|
||||
.baggage(parent.getBaggage())
|
||||
.build();
|
||||
this.spanLogger.logStartedSpan(parent, span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@@ -51,4 +51,10 @@ public class ArrayListSpanAccumulator implements SpanReporter {
|
||||
this.spans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized (this.spans) {
|
||||
this.spans.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -89,6 +88,26 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert everySpanHasABaggage(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
if (!everySpanHasBaggage(baggageKey, baggageValue)) {
|
||||
failWithMessage("Expected spans \n <%s> \nto ALL contain baggage with key "
|
||||
+ "equal to <%s>, and value equal to <%s>", spansToString(), baggageKey, baggageValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert anySpanHasABaggage(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
if (!hasBaggage(baggageKey, baggageValue)) {
|
||||
failWithMessage("Expected spans \n <%s> \nto contain at least one span with baggage key "
|
||||
+ "equal to <%s>, and value equal to <%s>", spansToString(), baggageKey, baggageValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean spanWithKeyTagExists(String tagKey) {
|
||||
for (Span span : this.actual.spans) {
|
||||
if (span.tags().containsKey(tagKey)) {
|
||||
@@ -98,6 +117,37 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean everySpanHasBaggage(String baggageKey, String baggageValue) {
|
||||
boolean exists = false;
|
||||
for (Span span : this.actual.spans) {
|
||||
for (Map.Entry<String, String> baggage : span.baggageItems()) {
|
||||
if (baggage.getKey().equals(baggageKey)) {
|
||||
if (baggage.getValue().equals(baggageValue)) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
private boolean hasBaggage(String baggageKey, String baggageValue) {
|
||||
for (Span span : this.actual.spans) {
|
||||
for (Map.Entry<String, String> baggage : span.baggageItems()) {
|
||||
if (baggage.getKey().equals(baggageKey)) {
|
||||
if (baggage.getValue().equals(baggageValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert hasASpanWithTagEqualTo(String tagKey, String tagValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
@@ -113,7 +163,8 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
|
||||
private String spansToString() {
|
||||
return this.actual.spans.stream().map(span -> "\nSPAN: " + span.toString() + " with name [" + span.getName() + "] " +
|
||||
"\nwith tags " + span.tags() + "\nwith logs " + span.logs()).collect(joining("\n"));
|
||||
"\nwith tags " + span.tags() + "\nwith logs " + span.logs() +
|
||||
"\nwith baggage " + span.getBaggage()).collect(joining("\n"));
|
||||
}
|
||||
|
||||
public ListOfSpansAssert doesNotHaveASpanWithName(String name) {
|
||||
@@ -143,16 +194,12 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
}
|
||||
|
||||
private void printSpans() {
|
||||
try {
|
||||
log.info("Stored spans " + this.objectMapper.writeValueAsString(new ArrayList<>(this.actual.spans)));
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
}
|
||||
log.info("Stored spans " + spansToString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failWithMessage(String errorMessage, Object... arguments) {
|
||||
log.error(errorMessage);
|
||||
log.error(String.format(errorMessage, arguments));
|
||||
super.failWithMessage(errorMessage, arguments);
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,19 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert hasBaggageItem(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
assertThatBaggageContainsKey(baggageKey);
|
||||
String foundValue = this.actual.getBaggageItem(baggageKey);
|
||||
if (!foundValue.equals(baggageValue)) {
|
||||
String message = String.format("Expected span to have the baggage with key <%s> and value <%s>. "
|
||||
+ "Found value for that baggage is <%s>", baggageKey, baggageValue, foundValue);
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert hasATagWithKey(String tagKey) {
|
||||
isNotNull();
|
||||
assertThatTagIsPresent(tagKey);
|
||||
@@ -134,6 +147,15 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatBaggageContainsKey(String baggageKey) {
|
||||
if (!this.actual.getBaggage().containsKey(baggageKey)) {
|
||||
String message = String.format("Expected span to have the baggage with key <%s>. "
|
||||
+ "Found baggage are <%s>", baggageKey, this.actual.getBaggage());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public SpanAssert hasLoggedAnEvent(String event) {
|
||||
isNotNull();
|
||||
if (!this.actual.logs().stream().map(org.springframework.cloud.sleuth.Log::getEvent)
|
||||
|
||||
@@ -1,51 +1,60 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.multiple;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceFilter;
|
||||
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
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.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static com.jayway.awaitility.Awaitility.await;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = MultipleHopsIntegrationTests.Config.class)
|
||||
public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
@SpringBootTest(classes = MultipleHopsIntegrationTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class MultipleHopsIntegrationTests {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired TraceFilter traceFilter;
|
||||
@Autowired ArrayListSpanAccumulator arrayListSpanAccumulator;
|
||||
@Autowired SpanReporter spanReporter;
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(this.traceFilter);
|
||||
@Before
|
||||
public void setup() {
|
||||
this.arrayListSpanAccumulator.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_prepare_spans_for_export() throws Exception {
|
||||
this.mockMvc.perform(get("/greeting")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
this.restTemplate.getForObject("http://localhost:" + this.config.port + "/greeting", String.class);
|
||||
|
||||
await().atMost(5, SECONDS).until(() -> {
|
||||
then(this.arrayListSpanAccumulator.getSpans().stream().map(Span::getName)
|
||||
@@ -55,9 +64,46 @@ public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
});
|
||||
}
|
||||
|
||||
// issue #237 - baggage
|
||||
@Test
|
||||
public void should_propagate_the_baggage() throws Exception {
|
||||
//tag::baggage[]
|
||||
Span initialSpan = this.tracer.createSpan("span");
|
||||
initialSpan.setBaggageItem("foo", "bar");
|
||||
//end::baggage[]
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.put("baggage-baz", Collections.singletonList("baz"));
|
||||
RequestEntity requestEntity = new RequestEntity(headers, HttpMethod.GET,
|
||||
URI.create("http://localhost:" + this.config.port + "/greeting"));
|
||||
this.restTemplate.exchange(requestEntity, String.class);
|
||||
|
||||
await().atMost(5, SECONDS).until(() -> {
|
||||
then(new ListOfSpans(this.arrayListSpanAccumulator.getSpans()))
|
||||
.everySpanHasABaggage("foo", "bar")
|
||||
.anySpanHasABaggage("baz", "baz");
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(initialSpan);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@SpringBootApplication
|
||||
public static class Config {
|
||||
public static class Config implements
|
||||
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
|
||||
int port;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
this.port = event.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean ArrayListSpanAccumulator arrayListSpanAccumulator() {
|
||||
return new ArrayListSpanAccumulator();
|
||||
|
||||
@@ -185,6 +185,38 @@ public class DefaultTracerTests {
|
||||
then(span).isEqualTo(continuedSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPropagateBaggageFromParentToChild() {
|
||||
DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
this.spanNamer, this.spanLogger, this.spanReporter);
|
||||
Span parent = Span.builder().name(IMPORTANT_WORK_1).traceId(1L).spanId(1L)
|
||||
.baggage("foo", "bar").build();
|
||||
Span child = tracer.createSpan("child", parent);
|
||||
|
||||
then(parent).hasBaggageItem("foo", "bar");
|
||||
then(child).hasBaggageItem("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPropagateBaggageToContinuedSpan() {
|
||||
DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
this.spanNamer, this.spanLogger, this.spanReporter);
|
||||
Span parent = Span.builder().name(IMPORTANT_WORK_1).traceId(1L).spanId(1L)
|
||||
.baggage("foo", "bar").build();
|
||||
Span continuedSpan = tracer.continueSpan(parent);
|
||||
|
||||
parent.setBaggageItem("baz1", "baz1");
|
||||
continuedSpan.setBaggageItem("baz2", "baz2");
|
||||
|
||||
then(parent).hasBaggageItem("foo", "bar")
|
||||
.hasBaggageItem("baz1", "baz1")
|
||||
.hasBaggageItem("baz2", "baz2");
|
||||
then(continuedSpan).hasBaggageItem("foo", "bar")
|
||||
.hasBaggageItem("baz1", "baz1")
|
||||
.hasBaggageItem("baz2", "baz2");
|
||||
then(parent).isEqualTo(continuedSpan);
|
||||
}
|
||||
|
||||
private Span assertSpan(List<Span> spans, Long parentId, String name) {
|
||||
List<Span> found = findSpans(spans, parentId);
|
||||
assertThat(found).as("More than one span with parentId %s", parentId).hasSize(1);
|
||||
|
||||
Reference in New Issue
Block a user