Text map introduction (#439)
without this change we had to treat every mean of injecting and extracting span information separately with this change all means of communications are abstracted. That way you configure things once only. part of #237
This commit is contained in:
committed by
GitHub
parent
43ae6dd7e3
commit
83012a5aa6
@@ -636,11 +636,11 @@ a modified file in the correct place. Just commit it and push the change.
|
||||
If you don't have an IDE preference we would recommend that you use
|
||||
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
http://eclipse.org[Eclipse] when working with the code. We use the
|
||||
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
|
||||
http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue as long as they use Maven 3.3.3 or better.
|
||||
|
||||
==== Importing into eclipse with m2eclipse
|
||||
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
|
||||
We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||
marketplace".
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
/**
|
||||
* Contract for extracting tracing headers from a {@link SpanTextMap}
|
||||
* via HTTP headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface HttpSpanExtractor extends SpanExtractor<SpanTextMap> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
/**
|
||||
* Contract for injecting tracing headers from a {@link SpanTextMap}
|
||||
* via HTTP headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface HttpSpanInjector extends SpanInjector<SpanTextMap> {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface SpanTextMap extends Iterable<Map.Entry<String, String>> {
|
||||
/**
|
||||
* Gets an iterator over arbitrary key:value pairs from the TextMapReader.
|
||||
*
|
||||
* @return entries in the TextMap backing store; note that for some Formats, the iterator may include entries that
|
||||
* were never injected by a Tracer implementation (e.g., unrelated HTTP headers)
|
||||
*/
|
||||
Iterator<Map.Entry<String,String>> iterator();
|
||||
|
||||
/**
|
||||
* Puts a key:value pair into the TextMapWriter's backing store.
|
||||
*
|
||||
* @param key a String, possibly with constraints dictated by the particular Format this TextMap is paired with
|
||||
* @param value a String, possibly with constraints dictated by the particular Format this TextMap is paired with
|
||||
*/
|
||||
void put(String key, String value);
|
||||
}
|
||||
@@ -5,17 +5,14 @@ import java.lang.invoke.MethodHandles;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
import org.springframework.messaging.support.ExecutorChannelInterceptor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -40,12 +37,12 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final SpanExtractor<Message<?>> spanExtractor;
|
||||
private final SpanInjector<MessageBuilder<?>> spanInjector;
|
||||
private final MessagingSpanTextMapExtractor spanExtractor;
|
||||
private final MessagingSpanTextMapInjector spanInjector;
|
||||
|
||||
protected AbstractTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanExtractor<Message<?>> spanExtractor,
|
||||
SpanInjector<MessageBuilder<?>> spanInjector) {
|
||||
MessagingSpanTextMapExtractor spanExtractor,
|
||||
MessagingSpanTextMapInjector spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.spanExtractor = spanExtractor;
|
||||
@@ -60,7 +57,7 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
protected SpanInjector<MessageBuilder<?>> getSpanInjector() {
|
||||
protected MessagingSpanTextMapInjector getSpanInjector() {
|
||||
return this.spanInjector;
|
||||
}
|
||||
|
||||
@@ -68,9 +65,9 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
* Returns a span given the message and a channel. Returns {@code null} if ids are
|
||||
* missing.
|
||||
*/
|
||||
protected Span buildSpan(Message<?> message) {
|
||||
protected Span buildSpan(SpanTextMap carrier) {
|
||||
try {
|
||||
return this.spanExtractor.joinTrace(message);
|
||||
return this.spanExtractor.joinTrace(carrier);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
|
||||
/**
|
||||
* Default implementation for messaging
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtractor {
|
||||
|
||||
@Override
|
||||
public Span joinTrace(SpanTextMap textMap) {
|
||||
Map<String, String> carrier = TextMapUtil.asMap(textMap);
|
||||
if (!hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME)
|
||||
|| !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME)) {
|
||||
return null;
|
||||
// TODO: Consider throwing IllegalArgumentException;
|
||||
}
|
||||
return extractSpanFromHeaders(carrier, Span.builder());
|
||||
}
|
||||
|
||||
private Span extractSpanFromHeaders(Map<String, String> carrier, Span.SpanBuilder builder) {
|
||||
Span.SpanBuilder spanBuilder = builder;
|
||||
long traceId = Span
|
||||
.hexToId(carrier.get(TraceMessageHeaders.TRACE_ID_NAME));
|
||||
long spanId = Span.hexToId(carrier.get(TraceMessageHeaders.SPAN_ID_NAME));
|
||||
spanBuilder = spanBuilder.traceId(traceId).spanId(spanId);
|
||||
spanBuilder.exportable(
|
||||
Span.SPAN_SAMPLED.equals(carrier.get(TraceMessageHeaders.SAMPLED_NAME)));
|
||||
String processId = carrier.get(TraceMessageHeaders.PROCESS_ID_NAME);
|
||||
String spanName = carrier.get(TraceMessageHeaders.SPAN_NAME_NAME);
|
||||
if (spanName != null) {
|
||||
spanBuilder.name(spanName);
|
||||
}
|
||||
if (processId != null) {
|
||||
spanBuilder.processId(processId);
|
||||
}
|
||||
setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME);
|
||||
spanBuilder.remote(true);
|
||||
return spanBuilder.build();
|
||||
}
|
||||
|
||||
boolean hasHeader(Map<String, String> message, String name) {
|
||||
return message.containsKey(name);
|
||||
}
|
||||
|
||||
private void setParentIdIfApplicable(Map<String, String> carrier, Span.SpanBuilder spanBuilder,
|
||||
String spanParentIdHeader) {
|
||||
String parentId = carrier.get(spanParentIdHeader);
|
||||
if (parentId != null) {
|
||||
spanBuilder.parent(Span.hexToId(parentId));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation for messaging
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjector {
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public HeaderBasedMessagingInjector(TraceKeys traceKeys) {
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
Map<String, String> map = TextMapUtil.asMap(carrier);
|
||||
if (span == null) {
|
||||
if (!isSampled(map, TraceMessageHeaders.SAMPLED_NAME)) {
|
||||
carrier.put(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
addHeaders(span, carrier);
|
||||
}
|
||||
|
||||
private boolean isSampled(Map<String, String> initialMessage, String sampledHeaderName) {
|
||||
return Span.SPAN_SAMPLED.equals(initialMessage.get(sampledHeaderName));
|
||||
}
|
||||
|
||||
private void addHeaders(Span span, SpanTextMap textMap) {
|
||||
addHeader(textMap, TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
addHeader(textMap, TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
if (span.isExportable()) {
|
||||
addAnnotations(this.traceKeys, textMap, span);
|
||||
Long parentId = getFirst(span.getParents());
|
||||
if (parentId != null) {
|
||||
addHeader(textMap, TraceMessageHeaders.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
}
|
||||
addHeader(textMap, TraceMessageHeaders.SPAN_NAME_NAME, span.getName());
|
||||
addHeader(textMap, TraceMessageHeaders.PROCESS_ID_NAME, span.getProcessId());
|
||||
addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_SAMPLED);
|
||||
}
|
||||
else {
|
||||
addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAnnotations(TraceKeys traceKeys, SpanTextMap spanTextMap, Span span) {
|
||||
Map<String, String> map = TextMapUtil.asMap(spanTextMap);
|
||||
for (String name : traceKeys.getMessage().getHeaders()) {
|
||||
if (map.containsKey(name)) {
|
||||
String key = traceKeys.getMessage().getPrefix() + name.toLowerCase();
|
||||
Object value = map.get(name);
|
||||
if (value == null) {
|
||||
value = "null";
|
||||
}
|
||||
// TODO: better way to serialize?
|
||||
tagIfEntryMissing(span, key, value.toString());
|
||||
}
|
||||
}
|
||||
addPayloadAnnotations(traceKeys, map, span);
|
||||
}
|
||||
|
||||
private void addPayloadAnnotations(TraceKeys traceKeys, Map<String, String> map, Span span) {
|
||||
if (map.containsKey(traceKeys.getMessage().getPayload().getType())) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(),
|
||||
map.get(traceKeys.getMessage().getPayload().getType()));
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(),
|
||||
map.get(traceKeys.getMessage().getPayload().getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
private void tagIfEntryMissing(Span span, String key, String value) {
|
||||
if (!span.tags().containsKey(key)) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addHeader(SpanTextMap textMap, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
textMap.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Long getFirst(List<Long> parents) {
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,15 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.integration.channel.ChannelInterceptorAware;
|
||||
import org.springframework.integration.channel.interceptor.VetoCapableInterceptor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -32,9 +28,10 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||
*/
|
||||
class IntegrationTraceChannelInterceptor extends TraceChannelInterceptor implements VetoCapableInterceptor {
|
||||
|
||||
|
||||
public IntegrationTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanExtractor<Message<?>> spanExtractor,
|
||||
SpanInjector<MessageBuilder<?>> spanInjector) {
|
||||
MessagingSpanTextMapExtractor spanExtractor,
|
||||
MessagingSpanTextMapInjector spanInjector) {
|
||||
super(tracer, traceKeys, spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Span.SpanBuilder;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Creates a {@link SpanBuilder} from {@link Message}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class MessagingSpanExtractor implements SpanExtractor<Message<?>> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final Random random;
|
||||
|
||||
public MessagingSpanExtractor(Random random) {
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span joinTrace(Message<?> carrier) {
|
||||
if ((!hasHeader(carrier, Span.TRACE_ID_NAME)
|
||||
|| !hasHeader(carrier, Span.SPAN_ID_NAME))
|
||||
&& (!hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME)
|
||||
|| !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME))) {
|
||||
return null;
|
||||
// TODO: Consider throwing IllegalArgumentException;
|
||||
}
|
||||
if (hasHeader(carrier, Span.TRACE_ID_NAME)
|
||||
|| hasHeader(carrier, Span.SPAN_ID_NAME)) {
|
||||
log.warn("Deprecated trace headers detected. Please upgrade Sleuth to 1.1 "
|
||||
+ "or start sending headers present in the TraceMessageHeaders class");
|
||||
return extractSpanFromOldHeaders(carrier, Span.builder());
|
||||
}
|
||||
return extractSpanFromNewHeaders(carrier, Span.builder());
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
private Span extractSpanFromOldHeaders(Message<?> carrier, SpanBuilder spanBuilder) {
|
||||
return extractSpanFromHeaders(carrier, spanBuilder, Span.TRACE_ID_NAME, Span.SPAN_ID_NAME,
|
||||
Span.SAMPLED_NAME, Span.PROCESS_ID_NAME, Span.SPAN_NAME_NAME, Span.PARENT_ID_NAME);
|
||||
}
|
||||
|
||||
private Span extractSpanFromNewHeaders(Message<?> carrier, SpanBuilder spanBuilder) {
|
||||
return extractSpanFromHeaders(carrier, spanBuilder, TraceMessageHeaders.TRACE_ID_NAME,
|
||||
TraceMessageHeaders.SPAN_ID_NAME, TraceMessageHeaders.SAMPLED_NAME,
|
||||
TraceMessageHeaders.PROCESS_ID_NAME, TraceMessageHeaders.SPAN_NAME_NAME,
|
||||
TraceMessageHeaders.PARENT_ID_NAME);
|
||||
}
|
||||
|
||||
private Span extractSpanFromHeaders(Message<?> carrier, SpanBuilder spanBuilder,
|
||||
String traceIdHeader, String spanIdHeader, String spanSampledHeader,
|
||||
String spanProcessIdHeader, String spanNameHeader, String spanParentIdHeader) {
|
||||
long traceId = Span
|
||||
.hexToId(getHeader(carrier, traceIdHeader));
|
||||
long spanId = hasHeader(carrier, spanIdHeader)
|
||||
? Span.hexToId(getHeader(carrier, spanIdHeader))
|
||||
: this.random.nextLong();
|
||||
spanBuilder = spanBuilder.traceId(traceId).spanId(spanId);
|
||||
spanBuilder.exportable(
|
||||
Span.SPAN_SAMPLED.equals(getHeader(carrier, spanSampledHeader)));
|
||||
String processId = getHeader(carrier, spanProcessIdHeader);
|
||||
String spanName = getHeader(carrier, spanNameHeader);
|
||||
if (spanName != null) {
|
||||
spanBuilder.name(spanName);
|
||||
}
|
||||
if (processId != null) {
|
||||
spanBuilder.processId(processId);
|
||||
}
|
||||
setParentIdIfApplicable(carrier, spanBuilder, spanParentIdHeader);
|
||||
spanBuilder.remote(true);
|
||||
return spanBuilder.build();
|
||||
}
|
||||
|
||||
String getHeader(Message<?> message, String name) {
|
||||
return getHeader(message, name, String.class);
|
||||
}
|
||||
|
||||
<T> T getHeader(Message<?> message, String name, Class<T> type) {
|
||||
return message.getHeaders().get(name, type);
|
||||
}
|
||||
|
||||
boolean hasHeader(Message<?> message, String name) {
|
||||
return message.getHeaders().containsKey(name);
|
||||
}
|
||||
|
||||
private void setParentIdIfApplicable(Message<?> carrier, SpanBuilder spanBuilder,
|
||||
String spanParentIdHeader) {
|
||||
String parentId = getHeader(carrier, spanParentIdHeader);
|
||||
if (parentId != null) {
|
||||
spanBuilder.parent(Span.hexToId(parentId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Creates a {@link Span.SpanBuilder} from {@link Message}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class MessagingSpanInjector implements SpanInjector<MessageBuilder<?>> {
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public MessagingSpanInjector(TraceKeys traceKeys) {
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Span span, MessageBuilder<?> carrier) {
|
||||
Message<?> initialMessage = carrier.build();
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor
|
||||
.getMutableAccessor(initialMessage);
|
||||
if (span == null) {
|
||||
if (!isSampled(initialMessage, Span.SAMPLED_NAME) ||
|
||||
!isSampled(initialMessage, TraceMessageHeaders.SAMPLED_NAME)) {
|
||||
// Backwards compatibility
|
||||
accessor.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
accessor.setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
carrier.setHeaders(accessor);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
addOldHeaders(span, initialMessage, accessor, headers);
|
||||
addNewHeaders(span, initialMessage, accessor, headers);
|
||||
accessor.copyHeaders(headers);
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
for (String name : headers.keySet()) {
|
||||
nativeAccessor.setNativeHeader(name, headers.get(name));
|
||||
}
|
||||
}
|
||||
carrier.setHeaders(accessor);
|
||||
}
|
||||
|
||||
private boolean isSampled(Message<?> initialMessage, String sampledHeaderName) {
|
||||
return Span.SPAN_SAMPLED
|
||||
.equals(initialMessage.getHeaders().get(sampledHeaderName));
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
private void addOldHeaders(Span span, Message<?> initialMessage,
|
||||
MessageHeaderAccessor accessor, Map<String, String> headers) {
|
||||
addHeaders(span, initialMessage, accessor, headers, Span.TRACE_ID_NAME,
|
||||
Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SPAN_NAME_NAME, Span.PROCESS_ID_NAME,
|
||||
Span.SAMPLED_NAME, TraceMessageHeaders.OLD_SPAN_HEADER);
|
||||
}
|
||||
|
||||
private void addNewHeaders(Span span, Message<?> initialMessage,
|
||||
MessageHeaderAccessor accessor, Map<String, String> headers) {
|
||||
addHeaders(span, initialMessage, accessor, headers, TraceMessageHeaders.TRACE_ID_NAME,
|
||||
TraceMessageHeaders.SPAN_ID_NAME, TraceMessageHeaders.PARENT_ID_NAME, TraceMessageHeaders.SPAN_NAME_NAME,
|
||||
TraceMessageHeaders.PROCESS_ID_NAME, TraceMessageHeaders.SAMPLED_NAME, TraceMessageHeaders.SPAN_HEADER);
|
||||
}
|
||||
|
||||
private void addHeaders(Span span, Message<?> initialMessage,
|
||||
MessageHeaderAccessor accessor, Map<String, String> headers, String traceIdHeader,
|
||||
String spanIdHeader, String parentIdHeader, String spanNameHeader, String processIdHeader,
|
||||
String spanSampledHeader, String spanHeader) {
|
||||
addHeader(headers, traceIdHeader, Span.idToHex(span.getTraceId()));
|
||||
addHeader(headers, spanIdHeader, Span.idToHex(span.getSpanId()));
|
||||
if (span.isExportable()) {
|
||||
addAnnotations(this.traceKeys, initialMessage, span);
|
||||
Long parentId = getFirst(span.getParents());
|
||||
if (parentId != null) {
|
||||
addHeader(headers, parentIdHeader, Span.idToHex(parentId));
|
||||
}
|
||||
addHeader(headers, spanNameHeader, span.getName());
|
||||
addHeader(headers, processIdHeader, span.getProcessId());
|
||||
addHeader(headers, spanSampledHeader, Span.SPAN_SAMPLED);
|
||||
}
|
||||
else {
|
||||
addHeader(headers, spanSampledHeader, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
accessor.setHeader(spanHeader, span);
|
||||
}
|
||||
|
||||
private void addAnnotations(TraceKeys traceKeys, Message<?> message, Span span) {
|
||||
for (String name : traceKeys.getMessage().getHeaders()) {
|
||||
if (message.getHeaders().containsKey(name)) {
|
||||
String key = traceKeys.getMessage().getPrefix() + name.toLowerCase();
|
||||
Object value = message.getHeaders().get(name);
|
||||
if (value == null) {
|
||||
value = "null";
|
||||
}
|
||||
// TODO: better way to serialize?
|
||||
tagIfEntryMissing(span, key, value.toString());
|
||||
}
|
||||
}
|
||||
addPayloadAnnotations(traceKeys, message.getPayload(), span);
|
||||
}
|
||||
|
||||
private void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) {
|
||||
if (payload != null) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(),
|
||||
payload.getClass().getCanonicalName());
|
||||
if (payload instanceof String) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(),
|
||||
String.valueOf(((String) payload).length()));
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(),
|
||||
String.valueOf(((byte[]) payload).length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tagIfEntryMissing(Span span, String key, String value) {
|
||||
if (!span.tags().containsKey(key)) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addHeader(Map<String, String> headers, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
headers.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Long getFirst(List<Long> parents) {
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for extracting tracing headers from a {@link SpanTextMap}
|
||||
* via message headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface MessagingSpanTextMapExtractor extends SpanExtractor<SpanTextMap> {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* Contract for injecting tracing headers from a {@link SpanTextMap}
|
||||
* via message headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface MessagingSpanTextMapInjector extends SpanInjector<SpanTextMap> {
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link MessageBuilder}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class MessagingTextMap implements SpanTextMap {
|
||||
|
||||
private final MessageBuilder delegate;
|
||||
|
||||
public MessagingTextMap(MessageBuilder delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : this.delegate.build().getHeaders()
|
||||
.entrySet()) {
|
||||
map.put(entry.getKey(), String.valueOf(entry.getValue()));
|
||||
}
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void put(String key, String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return;
|
||||
}
|
||||
Message<?> initialMessage = this.delegate.build();
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor
|
||||
.getMutableAccessor(initialMessage);
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put(key, value);
|
||||
accessor.copyHeaders(headers);
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
for (String name : headers.keySet()) {
|
||||
nativeAccessor.setNativeHeader(name, headers.get(name));
|
||||
}
|
||||
}
|
||||
this.delegate.copyHeaders(accessor.toMessageHeaders());
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import org.springframework.cloud.sleuth.Log;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
@@ -41,21 +39,21 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
|
||||
public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanExtractor<Message<?>> spanExtractor,
|
||||
SpanInjector<MessageBuilder<?>> spanInjector) {
|
||||
MessagingSpanTextMapExtractor spanExtractor,
|
||||
MessagingSpanTextMapInjector spanInjector) {
|
||||
super(tracer, traceKeys, spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||
Span spanFromHeader = getSpanFromHeader(message);
|
||||
if (containsServerReceived(spanFromHeader)) {
|
||||
spanFromHeader.logEvent(Span.SERVER_SEND);
|
||||
} else if (spanFromHeader != null) {
|
||||
spanFromHeader.logEvent(Span.CLIENT_RECV);
|
||||
Span currentSpan = getTracer().getCurrentSpan();
|
||||
if (containsServerReceived(currentSpan)) {
|
||||
currentSpan.logEvent(Span.SERVER_SEND);
|
||||
} else if (currentSpan != null) {
|
||||
currentSpan.logEvent(Span.CLIENT_RECV);
|
||||
}
|
||||
addErrorTag(ex);
|
||||
getTracer().close(spanFromHeader);
|
||||
getTracer().close(currentSpan);
|
||||
}
|
||||
|
||||
private boolean containsServerReceived(Span span) {
|
||||
@@ -72,22 +70,18 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message);
|
||||
Span parentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan()
|
||||
: buildSpan(message);
|
||||
: buildSpan(new MessagingTextMap(messageBuilder));
|
||||
String name = getMessageChannelName(channel);
|
||||
Span span = startSpan(parentSpan, name, message);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message);
|
||||
// Backwards compatibility
|
||||
if (message.getHeaders().containsKey(TraceMessageHeaders.OLD_MESSAGE_SENT_FROM_CLIENT) ||
|
||||
message.getHeaders().containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) {
|
||||
if (message.getHeaders().containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) {
|
||||
span.logEvent(Span.SERVER_RECV);
|
||||
} else {
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
// Backwards compatibility
|
||||
messageBuilder.setHeader(TraceMessageHeaders.OLD_MESSAGE_SENT_FROM_CLIENT, true);
|
||||
messageBuilder.setHeader(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT, true);
|
||||
}
|
||||
getSpanInjector().inject(span, messageBuilder);
|
||||
getSpanInjector().inject(span, new MessagingTextMap(messageBuilder));
|
||||
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(message);
|
||||
headers.copyHeaders(messageBuilder.build().getHeaders());
|
||||
return new GenericMessage<Object>(message.getPayload(), headers.getMessageHeaders());
|
||||
@@ -97,9 +91,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
if (span != null) {
|
||||
return getTracer().createSpan(name, span);
|
||||
}
|
||||
// Backwards compatibility
|
||||
if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(Span.SAMPLED_NAME)) ||
|
||||
Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) {
|
||||
if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) {
|
||||
return getTracer().createSpan(name, NeverSampler.INSTANCE);
|
||||
}
|
||||
return getTracer().createSpan(name);
|
||||
@@ -108,7 +100,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
@Override
|
||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler) {
|
||||
Span spanFromHeader = getSpanFromHeader(message);
|
||||
Span spanFromHeader = getTracer().getCurrentSpan();
|
||||
if (spanFromHeader!= null) {
|
||||
spanFromHeader.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
@@ -119,7 +111,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
@Override
|
||||
public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler, Exception ex) {
|
||||
Span spanFromHeader = getSpanFromHeader(message);
|
||||
Span spanFromHeader = getTracer().getCurrentSpan();
|
||||
if (spanFromHeader!= null) {
|
||||
spanFromHeader.logEvent(Span.SERVER_SEND);
|
||||
addErrorTag(ex);
|
||||
@@ -133,19 +125,4 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
private Span getSpanFromHeader(Message<?> message) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
Object object = message.getHeaders().get(TraceMessageHeaders.OLD_SPAN_HEADER);
|
||||
if (object instanceof Span) {
|
||||
return (Span) object;
|
||||
}
|
||||
object = message.getHeaders().get(TraceMessageHeaders.SPAN_HEADER);
|
||||
if (object instanceof Span) {
|
||||
return (Span) object;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,9 +33,6 @@ public class TraceMessageHeaders {
|
||||
public static final String TRACE_ID_NAME = "spanTraceId";
|
||||
public static final String SPAN_NAME_NAME = "spanName";
|
||||
|
||||
static final String OLD_SPAN_HEADER = "X-Current-Span";
|
||||
static final String OLD_MESSAGE_SENT_FROM_CLIENT = "X-Message-Sent";
|
||||
static final String SPAN_HEADER = "currentSpan";
|
||||
static final String MESSAGE_SENT_FROM_CLIENT = "messageSent";
|
||||
|
||||
private TraceMessageHeaders() {}
|
||||
|
||||
@@ -20,13 +20,11 @@ import java.util.Random;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* AutoConfiguration containing Span extractor and injector for messaging. Will be reused
|
||||
@@ -41,12 +39,14 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||
public class TraceSpanMessagingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpanExtractor<Message<?>> messagingSpanExtractor(Random random) {
|
||||
return new MessagingSpanExtractor(random);
|
||||
@ConditionalOnMissingBean
|
||||
public MessagingSpanTextMapExtractor messagingSpanExtractor() {
|
||||
return new HeaderBasedMessagingExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<MessageBuilder<?>> messagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new MessagingSpanInjector(traceKeys);
|
||||
@ConditionalOnMissingBean
|
||||
public MessagingSpanTextMapInjector messagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new HeaderBasedMessagingInjector(traceKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -56,8 +52,8 @@ public class TraceSpringIntegrationAutoConfiguration {
|
||||
@Bean
|
||||
@GlobalChannelInterceptor(patterns = "${spring.sleuth.integration.patterns:*}")
|
||||
public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer,
|
||||
TraceKeys traceKeys, Random random, SpanExtractor<Message<?>> spanExtractor,
|
||||
SpanInjector<MessageBuilder<?>> spanInjector) {
|
||||
TraceKeys traceKeys, Random random, MessagingSpanTextMapExtractor spanExtractor,
|
||||
MessagingSpanTextMapInjector spanInjector) {
|
||||
return new IntegrationTraceChannelInterceptor(tracer, traceKeys, spanExtractor,
|
||||
spanInjector);
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapExtractor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration;
|
||||
@@ -43,9 +41,9 @@ public class TraceWebSocketAutoConfiguration
|
||||
@Autowired
|
||||
TraceKeys traceKeys;
|
||||
@Autowired
|
||||
SpanExtractor<Message<?>> spanExtractor;
|
||||
MessagingSpanTextMapExtractor spanExtractor;
|
||||
@Autowired
|
||||
SpanInjector<MessageBuilder<?>> spanInjector;
|
||||
MessagingSpanTextMapInjector spanInjector;
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Span.SpanBuilder;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Creates a {@link SpanBuilder} from {@link HttpServletRequest}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class HttpServletRequestExtractor implements SpanExtractor<HttpServletRequest> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
private final Pattern skipPattern;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public HttpServletRequestExtractor(Pattern skipPattern) {
|
||||
this.skipPattern = skipPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span joinTrace(HttpServletRequest carrier) {
|
||||
if (carrier.getHeader(Span.TRACE_ID_NAME) == null) {
|
||||
// can't build a Span without trace id
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(carrier);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(carrier.getHeader(Span.SAMPLED_NAME));
|
||||
long traceId = Span
|
||||
.hexToId(carrier.getHeader(Span.TRACE_ID_NAME));
|
||||
long spanId = spanId(carrier, traceId);
|
||||
return buildParentSpan(carrier, uri, skip, traceId, spanId);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private long spanId(HttpServletRequest carrier, long traceId) {
|
||||
String spanId = carrier.getHeader(Span.SPAN_ID_NAME);
|
||||
if (spanId == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request is missing a span id but it has a trace id. We'll assume that this is "
|
||||
+ "a root span with span id equal to trace id");
|
||||
}
|
||||
return traceId;
|
||||
} else {
|
||||
return Span.hexToId(spanId);
|
||||
}
|
||||
}
|
||||
|
||||
private Span buildParentSpan(HttpServletRequest carrier, String uri, boolean skip,
|
||||
long traceId, long spanId) {
|
||||
SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
|
||||
String processId = carrier.getHeader(Span.PROCESS_ID_NAME);
|
||||
String parentName = carrier.getHeader(Span.SPAN_NAME_NAME);
|
||||
if (StringUtils.hasText(parentName)) {
|
||||
span.name(parentName);
|
||||
}
|
||||
else {
|
||||
span.name(HTTP_COMPONENT + ":/parent" + uri);
|
||||
}
|
||||
if (StringUtils.hasText(processId)) {
|
||||
span.processId(processId);
|
||||
}
|
||||
if (carrier.getHeader(Span.PARENT_ID_NAME) != null) {
|
||||
span.parent(Span
|
||||
.hexToId(carrier.getHeader(Span.PARENT_ID_NAME)));
|
||||
}
|
||||
span.remote(true);
|
||||
if (skip) {
|
||||
span.exportable(false);
|
||||
}
|
||||
return span.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link HttpServletRequest}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class HttpServletRequestTextMap implements SpanTextMap {
|
||||
|
||||
private final HttpServletRequest delegate;
|
||||
private final Map<String, String> additionalHeaders = new HashMap<>();
|
||||
|
||||
HttpServletRequestTextMap(HttpServletRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
this.additionalHeaders.put(ZipkinHttpSpanExtractor.URI_HEADER,
|
||||
urlPathHelper.getPathWithinApplication(delegate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
Enumeration<String> headerNames = this.delegate.getHeaderNames();
|
||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
||||
String name = headerNames.nextElement();
|
||||
map.put(name, this.delegate.getHeader(name));
|
||||
}
|
||||
map.putAll(this.additionalHeaders);
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
this.additionalHeaders.put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -15,23 +15,23 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
@@ -88,20 +88,20 @@ public class TraceFilter extends GenericFilterBean {
|
||||
private final TraceKeys traceKeys;
|
||||
private final Pattern skipPattern;
|
||||
private final SpanReporter spanReporter;
|
||||
private final SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
private final HttpSpanExtractor spanExtractor;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
HttpSpanExtractor spanExtractor,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), spanReporter,
|
||||
spanExtractor, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern,
|
||||
SpanReporter spanReporter, SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
SpanReporter spanReporter, HttpSpanExtractor spanExtractor,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
@@ -288,7 +288,7 @@ public class TraceFilter extends GenericFilterBean {
|
||||
}
|
||||
return spanFromRequest;
|
||||
}
|
||||
Span parent = this.spanExtractor.joinTrace(request);
|
||||
Span parent = this.spanExtractor.joinTrace(new HttpServletRequestTextMap(request));
|
||||
if (parent != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found a parent span " + parent + " in the request");
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -30,7 +29,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.embedded.FilterRegistrationBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
@@ -90,10 +90,7 @@ public class TraceWebAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean traceWebFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector, TraceFilter traceFilter) {
|
||||
public FilterRegistrationBean traceWebFilter(TraceFilter traceFilter) {
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(traceFilter);
|
||||
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
|
||||
filterRegistrationBean.setOrder(TraceFilter.ORDER);
|
||||
@@ -103,16 +100,22 @@ public class TraceWebAutoConfiguration {
|
||||
@Bean
|
||||
public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
HttpSpanExtractor spanExtractor,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(),
|
||||
spanReporter, spanExtractor, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor(
|
||||
SkipPatternProvider skipPatternProvider) {
|
||||
return new HttpServletRequestExtractor(skipPatternProvider.skipPattern());
|
||||
@ConditionalOnMissingBean
|
||||
public HttpSpanExtractor httpSpanExtractor(@Value("${spring.sleuth.web.skipPattern:}") String skipPattern) {
|
||||
return new ZipkinHttpSpanExtractor(Pattern.compile(skipPattern));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpSpanInjector httpSpanInjector() {
|
||||
return new ZipkinHttpSpanInjector();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation, compatible with Zipkin propagation.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class ZipkinHttpSpanExtractor implements HttpSpanExtractor {
|
||||
|
||||
private static final org.apache.commons.logging.Log log = LogFactory.getLog(
|
||||
MethodHandles.lookup().lookupClass());
|
||||
static final String URI_HEADER = "X-Span-Uri";
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
private final Pattern skipPattern;
|
||||
|
||||
public ZipkinHttpSpanExtractor(Pattern skipPattern) {
|
||||
this.skipPattern = skipPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span joinTrace(SpanTextMap textMap) {
|
||||
Map<String, String> carrier = TextMapUtil.asMap(textMap);
|
||||
if (carrier.get(Span.TRACE_ID_NAME) == null) {
|
||||
// can't build a Span without trace id
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String uri = carrier.get(URI_HEADER);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(carrier.get(Span.SAMPLED_NAME));
|
||||
long traceId = Span
|
||||
.hexToId(carrier.get(Span.TRACE_ID_NAME));
|
||||
long spanId = spanId(carrier, traceId);
|
||||
return buildParentSpan(carrier, uri, skip, traceId, spanId);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private long spanId(Map<String, String> carrier, long traceId) {
|
||||
String spanId = carrier.get(Span.SPAN_ID_NAME);
|
||||
if (spanId == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request is missing a span id but it has a trace id. We'll assume that this is "
|
||||
+ "a root span with span id equal to trace id");
|
||||
}
|
||||
return traceId;
|
||||
} else {
|
||||
return Span.hexToId(spanId);
|
||||
}
|
||||
}
|
||||
|
||||
private Span buildParentSpan(Map<String, String> carrier, String uri, boolean skip,
|
||||
long traceId, long spanId) {
|
||||
Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
|
||||
String processId = carrier.get(Span.PROCESS_ID_NAME);
|
||||
String parentName = carrier.get(Span.SPAN_NAME_NAME);
|
||||
if (StringUtils.hasText(parentName)) {
|
||||
span.name(parentName);
|
||||
}
|
||||
else {
|
||||
span.name(HTTP_COMPONENT + ":/parent" + uri);
|
||||
}
|
||||
if (StringUtils.hasText(processId)) {
|
||||
span.processId(processId);
|
||||
}
|
||||
if (carrier.containsKey(Span.PARENT_ID_NAME)) {
|
||||
span.parent(Span
|
||||
.hexToId(carrier.get(Span.PARENT_ID_NAME)));
|
||||
}
|
||||
span.remote(true);
|
||||
if (skip) {
|
||||
span.exportable(false);
|
||||
}
|
||||
return span.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link HttpSpanInjector}, compatible with Zipkin propagation.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class ZipkinHttpSpanInjector implements HttpSpanInjector {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setIdHeader(carrier, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
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) && !entryPresent(carrier, name)) {
|
||||
carrier.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIdHeader(SpanTextMap carrier, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(carrier, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean entryPresent(SpanTextMap carrier, String name) {
|
||||
for (Map.Entry<String, String> entry : carrier) {
|
||||
if (entry.getKey().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,8 +21,8 @@ import java.net.URI;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.http.HttpRequest;
|
||||
@@ -38,11 +38,11 @@ abstract class AbstractTraceHttpRequestInterceptor {
|
||||
protected static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected final Tracer tracer;
|
||||
protected final SpanInjector<HttpRequest> spanInjector;
|
||||
protected final HttpSpanInjector spanInjector;
|
||||
protected final HttpTraceKeysInjector keysInjector;
|
||||
|
||||
protected AbstractTraceHttpRequestInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector, HttpTraceKeysInjector keysInjector) {
|
||||
HttpSpanInjector spanInjector, HttpTraceKeysInjector keysInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
this.keysInjector = keysInjector;
|
||||
@@ -56,7 +56,7 @@ abstract class AbstractTraceHttpRequestInterceptor {
|
||||
URI uri = request.getURI();
|
||||
String spanName = uriScheme(uri) + ":" + uri.getPath();
|
||||
Span newSpan = this.tracer.createSpan(spanName);
|
||||
this.spanInjector.inject(newSpan, request);
|
||||
this.spanInjector.inject(newSpan, new HttpRequestTextMap(request));
|
||||
addRequestTags(request);
|
||||
newSpan.logEvent(Span.CLIENT_SEND);
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web.client;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link HttpRequest}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class HttpRequestInjector implements SpanInjector<HttpRequest> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpRequest carrier) {
|
||||
setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setIdHeader(carrier, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
private void setHeader(HttpRequest request, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !request.getHeaders().containsKey(name)) {
|
||||
request.getHeaders().add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIdHeader(HttpRequest request, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(request, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web.client;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link HttpRequest}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class HttpRequestTextMap implements SpanTextMap {
|
||||
|
||||
private final HttpRequest delegate;
|
||||
|
||||
HttpRequestTextMap(HttpRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
final Iterator<Map.Entry<String, List<String>>> iterator = this.delegate.getHeaders()
|
||||
.entrySet().iterator();
|
||||
return new Iterator<Map.Entry<String, String>>() {
|
||||
@Override public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override public Map.Entry<String, String> next() {
|
||||
Map.Entry<String, List<String>> next = iterator.next();
|
||||
List<String> value = next.getValue();
|
||||
return new AbstractMap.SimpleEntry<>(next.getKey(), value.isEmpty() ? "" : value.get(0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return;
|
||||
}
|
||||
this.delegate.getHeaders().put(key, Collections.singletonList(value));
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,11 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.AsyncClientHttpRequest;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
@@ -56,7 +55,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
* @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory)
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
HttpSpanInjector spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
@@ -70,7 +69,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
* has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor}
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory();
|
||||
this.asyncDelegate = simpleClientHttpRequestFactory;
|
||||
@@ -78,7 +77,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
}
|
||||
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
HttpSpanInjector spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate,
|
||||
ClientHttpRequestFactory syncDelegate,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
@@ -41,7 +41,7 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||
public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterceptor
|
||||
implements ClientHttpRequestInterceptor {
|
||||
|
||||
public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
public TraceRestTemplateInterceptor(Tracer tracer, HttpSpanInjector spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@@ -22,14 +22,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
@@ -54,7 +53,7 @@ public class TraceWebAsyncClientAutoConfiguration {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired private HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
@Autowired private SpanInjector<HttpRequest> spanInjector;
|
||||
@Autowired private HttpSpanInjector spanInjector;
|
||||
@Autowired(required = false) private ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
@Autowired(required = false) private AsyncClientHttpRequestFactory asyncClientHttpRequestFactory;
|
||||
|
||||
|
||||
@@ -26,13 +26,12 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@@ -54,16 +53,11 @@ public class TraceWebClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
HttpSpanInjector spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TraceRestTemplateInterceptor(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<HttpRequest> httpRequestSpanInjector() {
|
||||
return new HttpRequestInjector();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TraceInterceptorConfiguration {
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web.client.feign;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import feign.Request;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link Request} via {@link AtomicReference}
|
||||
* since {@link Request} is immutable.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class FeignRequestInjector implements SpanInjector<AtomicReference<Request>> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, AtomicReference<Request> carrier) {
|
||||
String method = carrier.get().method();
|
||||
String url = carrier.get().url();
|
||||
Map<String, Collection<String>> headers = new HashMap<>(carrier.get().headers());
|
||||
byte[] body = carrier.get().body();
|
||||
Charset charset = carrier.get().charset();
|
||||
if (span == null) {
|
||||
setHeader(headers, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
carrier.set(Request.create(method, url, headers, body, charset));
|
||||
return;
|
||||
}
|
||||
setHeader(headers, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(headers, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(headers, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
setHeader(headers, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
Long parentId = getParentId(span);
|
||||
if (parentId != null) {
|
||||
setHeader(headers, Span.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
}
|
||||
setHeader(headers, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
carrier.set(Request.create(method, url, headers, body, charset));
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
protected void setHeader(Map<String, Collection<String>> headers, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !headers.containsKey(name)) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(value);
|
||||
headers.put(name, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.web.client.feign;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import feign.Request;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link AtomicReference<Request>}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class FeignRequestTextMap implements SpanTextMap {
|
||||
|
||||
private final AtomicReference<Request> delegate;
|
||||
|
||||
FeignRequestTextMap(AtomicReference<Request> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
final Iterator<Map.Entry<String, Collection<String>>> iterator = this.delegate.get().headers().entrySet().iterator();
|
||||
return new Iterator<Map.Entry<String, String>>() {
|
||||
@Override public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override public Map.Entry<String, String> next() {
|
||||
Map.Entry<String, Collection<String>> next = iterator.next();
|
||||
Collection<String> value = next.getValue();
|
||||
return new AbstractMap.SimpleEntry<>(next.getKey(), value.isEmpty() ? "" : value.iterator().next());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return;
|
||||
}
|
||||
String method = this.delegate.get().method();
|
||||
String url = this.delegate.get().url();
|
||||
Map<String, Collection<String>> headers = new HashMap<>(this.delegate.get().headers());
|
||||
byte[] body = this.delegate.get().body();
|
||||
Charset charset = this.delegate.get().charset();
|
||||
addHeader(key, value, headers);
|
||||
this.delegate.set(Request.create(method, url, headers, body, charset));
|
||||
}
|
||||
|
||||
private void addHeader(String key, String value,
|
||||
Map<String, Collection<String>> headers) {
|
||||
if (!headers.containsKey(key)) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(value);
|
||||
headers.put(key, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,15 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
|
||||
/**
|
||||
* A Feign Client that closes a Span if there is no response body. In other cases Span
|
||||
@@ -49,7 +50,7 @@ class TraceFeignClient implements Client {
|
||||
private HttpTraceKeysInjector keysInjector;
|
||||
private final BeanFactory beanFactory;
|
||||
private Tracer tracer;
|
||||
private final FeignRequestInjector spanInjector = new FeignRequestInjector();
|
||||
private HttpSpanInjector spanInjector;
|
||||
|
||||
TraceFeignClient(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
@@ -70,7 +71,7 @@ class TraceFeignClient implements Client {
|
||||
}
|
||||
try {
|
||||
AtomicReference<Request> feignRequest = new AtomicReference<>(request);
|
||||
this.spanInjector.inject(span, feignRequest);
|
||||
spanInjector().inject(span, new FeignRequestTextMap(feignRequest));
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
addRequestTags(request);
|
||||
Request modifiedRequest = feignRequest.get();
|
||||
@@ -102,17 +103,24 @@ class TraceFeignClient implements Client {
|
||||
*/
|
||||
private void addRequestTags(Request request) {
|
||||
URI uri = URI.create(request.url());
|
||||
getKeysInjector().addRequestTags(uri.toString(), uri.getHost(), uri.getPath(),
|
||||
keysInjector().addRequestTags(uri.toString(), uri.getHost(), uri.getPath(),
|
||||
request.method(), request.headers());
|
||||
}
|
||||
|
||||
private HttpTraceKeysInjector getKeysInjector() {
|
||||
private HttpTraceKeysInjector keysInjector() {
|
||||
if (this.keysInjector == null) {
|
||||
this.keysInjector = this.beanFactory.getBean(HttpTraceKeysInjector.class);
|
||||
}
|
||||
return this.keysInjector;
|
||||
}
|
||||
|
||||
private HttpSpanInjector spanInjector() {
|
||||
if (this.spanInjector == null) {
|
||||
this.spanInjector = this.beanFactory.getBean(HttpSpanInjector.class);
|
||||
}
|
||||
return this.spanInjector;
|
||||
}
|
||||
|
||||
private void closeSpan(Span span) {
|
||||
if (span != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
@@ -37,9 +43,20 @@ class ApacheHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequest
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(RequestBuilder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.setHeader(name, value);
|
||||
}
|
||||
protected SpanTextMap toSpanTextMap(final RequestBuilder context) {
|
||||
return new SpanTextMap() {
|
||||
@Override public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Header header : context.build().getAllHeaders()) {
|
||||
map.put(header.getName(), header.getValue());
|
||||
}
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override public void put(String key, String value) {
|
||||
context.addHeader(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import okhttp3.Request;
|
||||
@@ -38,9 +44,21 @@ class OkHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCust
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(Request.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.addHeader(name, value);
|
||||
}
|
||||
protected SpanTextMap toSpanTextMap(final Request.Builder context) {
|
||||
return new SpanTextMap() {
|
||||
@Override public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : context.build().headers().toMultimap().entrySet()) {
|
||||
if (!entry.getValue().isEmpty()) {
|
||||
map.put(entry.getKey(), entry.getValue().get(0));
|
||||
}
|
||||
}
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override public void put(String key, String value) {
|
||||
context.addHeader(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.zuul;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link RequestContext}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class RequestContextInjector implements SpanInjector<RequestContext> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, RequestContext carrier) {
|
||||
Map<String, String> requestHeaders = carrier.getZuulRequestHeaders();
|
||||
if (span == null) {
|
||||
setHeader(requestHeaders, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
setHeader(requestHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
setHeader(requestHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(requestHeaders, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(requestHeaders, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(requestHeaders, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(requestHeaders, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, String> request, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !request.containsKey(name)) {
|
||||
request.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, String> request, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(request, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* http://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.instrument.zuul;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
|
||||
/**
|
||||
* A {@link SpanTextMap} abstraction over {@link RequestContext}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class RequestContextTextMap implements SpanTextMap {
|
||||
|
||||
private final RequestContext carrier;
|
||||
|
||||
RequestContextTextMap(RequestContext carrier) {
|
||||
this.carrier = carrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
return this.carrier.getZuulRequestHeaders().entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
this.carrier.getZuulRequestHeaders().put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
@@ -38,9 +42,16 @@ class RestClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCustom
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(HttpRequest.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
protected SpanTextMap toSpanTextMap(final HttpRequest.Builder context) {
|
||||
context.build().getHttpHeaders();
|
||||
return new SpanTextMap() {
|
||||
@Override public Iterator<Map.Entry<String, String>> iterator() {
|
||||
return context.build().getHttpHeaders().getAllHeaders().iterator();
|
||||
}
|
||||
|
||||
@Override public void put(String key, String value) {
|
||||
context.header(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
@@ -30,14 +31,14 @@ import org.springframework.cloud.sleuth.Tracer;
|
||||
* into their respective context. The only difference is how those contexts set the headers.
|
||||
* In order to add a new implementation of the {@link RibbonRequestCustomizer} it's
|
||||
* necessary only to provide the {@link RibbonRequestCustomizer#accepts(Class)} method
|
||||
* with the context class name and {@link SpanInjectingRibbonRequestCustomizer#setHeader(Object, String, String)}
|
||||
* with the context class name and {@link SpanInjectingRibbonRequestCustomizer#toSpanTextMap(Object)}
|
||||
* to tell Sleuth how to set a header using the particular library.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
abstract class SpanInjectingRibbonRequestCustomizer<T> implements RibbonRequestCustomizer<T>,
|
||||
SpanInjector<T> {
|
||||
SpanInjector<SpanTextMap> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@@ -50,30 +51,30 @@ abstract class SpanInjectingRibbonRequestCustomizer<T> implements RibbonRequestC
|
||||
@Override
|
||||
public void customize(T context) {
|
||||
Span span = getCurrentSpan();
|
||||
inject(span, context);
|
||||
inject(span, toSpanTextMap(context));
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span in the RibbonRequestCustomizer is" + span);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract SpanTextMap toSpanTextMap(T context);
|
||||
|
||||
@Override
|
||||
public void inject(Span span, T carrier) {
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
carrier.put(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
carrier.put(Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(carrier, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(carrier, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
carrier.put(Span.SPAN_NAME_NAME, span.getName());
|
||||
if (getParentId(span) != null) {
|
||||
setHeader(carrier, Span.PARENT_ID_NAME,
|
||||
Span.idToHex(getParentId(span)));
|
||||
carrier.put(Span.PARENT_ID_NAME, Span.idToHex(getParentId(span)));
|
||||
}
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME,
|
||||
span.getProcessId());
|
||||
carrier.put(Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
@@ -84,6 +85,4 @@ abstract class SpanInjectingRibbonRequestCustomizer<T> implements RibbonRequestC
|
||||
private Span getCurrentSpan() {
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
abstract void setHeader(T builder, String name, String value);
|
||||
}
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
|
||||
import com.netflix.zuul.ExecutionStatus;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.ZuulFilterResult;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceRequestAttributes;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A pre request {@link ZuulFilter} that sets tracing related headers on the request
|
||||
* from the current span. We're doing so to ensure tracing propagates to the next hop.
|
||||
@@ -45,10 +46,10 @@ public class TracePreZuulFilter extends ZuulFilter {
|
||||
private static final String ZUUL_COMPONENT = "zuul";
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<RequestContext> spanInjector;
|
||||
private final HttpSpanInjector spanInjector;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
public TracePreZuulFilter(Tracer tracer, SpanInjector<RequestContext> spanInjector,
|
||||
public TracePreZuulFilter(Tracer tracer, HttpSpanInjector spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
@@ -76,7 +77,7 @@ public class TracePreZuulFilter extends ZuulFilter {
|
||||
markRequestAsHandled(ctx);
|
||||
Span newSpan = this.tracer.createSpan(span.getName(), span);
|
||||
newSpan.tag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ZUUL_COMPONENT);
|
||||
this.spanInjector.inject(newSpan, ctx);
|
||||
this.spanInjector.inject(newSpan, new RequestContextTextMap(ctx));
|
||||
this.httpTraceKeysInjector.addRequestTags(newSpan, URI.create(ctx.getRequest().getRequestURI()), ctx.getRequest().getMethod());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("New Zuul Span is " + newSpan + "");
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -24,7 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
@@ -33,9 +36,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import okhttp3.Request;
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
@@ -55,7 +55,7 @@ public class TraceZuulAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TracePreZuulFilter tracePreZuulFilter(Tracer tracer,
|
||||
SpanInjector<RequestContext> spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TracePreZuulFilter(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@@ -65,11 +65,6 @@ public class TraceZuulAutoConfiguration {
|
||||
return new TracePostZuulFilter(tracer, traceKeys);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<RequestContext> requestContextSpanInjector() {
|
||||
return new RequestContextInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceRibbonCommandFactoryBeanPostProcessor traceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new TraceRibbonCommandFactoryBeanPostProcessor(beanFactory);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.cloud.sleuth.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Utility class related to {@link org.springframework.cloud.sleuth.SpanTextMap}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public final class TextMapUtil {
|
||||
|
||||
private TextMapUtil() {}
|
||||
|
||||
public static Map<String, String> asMap(Iterable<Map.Entry<String, String>> iterable) {
|
||||
Map<String, String> map = new TreeMap<>(new Comparator<String>() {
|
||||
@Override public int compare(String o1, String o2) {
|
||||
return o1.toLowerCase().compareTo(o2.toLowerCase());
|
||||
}
|
||||
});
|
||||
for (Map.Entry<String, String> entry : iterable) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async.issues.issue410;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -63,6 +64,8 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
@TestPropertySource(properties = {"ribbon.eureka.enabled=false", "feign.hystrix.enabled=false", "server.port=0"})
|
||||
public class Issue410Tests {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Autowired Environment environment;
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired AsyncTask asyncTask;
|
||||
@@ -71,6 +74,7 @@ public class Issue410Tests {
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_tasks_running_without_a_pool() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
log.info("Starting test");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/without_pool", String.class);
|
||||
|
||||
@@ -87,6 +91,7 @@ public class Issue410Tests {
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_tasks_running_with_a_pool() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
log.info("Starting test");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/with_pool", String.class);
|
||||
|
||||
@@ -106,6 +111,7 @@ public class Issue410Tests {
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_completable_futures_with_executor() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
log.info("Starting test");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/completable", String.class);
|
||||
|
||||
@@ -125,6 +131,7 @@ public class Issue410Tests {
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_completable_futures_with_task_scheduler() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
log.info("Starting test");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/taskScheduler", String.class);
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -32,24 +30,23 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
public class MessagingSpanExtractorTests {
|
||||
MessagingSpanExtractor extractor = new MessagingSpanExtractor(new Random());
|
||||
HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor();
|
||||
|
||||
@Test
|
||||
public void should_return_null_if_trace_or_span_is_missing() {
|
||||
Message message = MessageBuilder.createMessage("", headers());
|
||||
then(this.extractor.joinTrace(message)).isNull();
|
||||
then(this.extractor.joinTrace(
|
||||
new MessagingTextMap(MessageBuilder.withPayload("")))).isNull();
|
||||
|
||||
message = MessageBuilder.createMessage("", headers("trace"));
|
||||
then(this.extractor.joinTrace(message)).isNull();
|
||||
then(this.extractor.joinTrace(
|
||||
new MessagingTextMap(MessageBuilder.withPayload("").copyHeaders(headers("trace"))))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_random_traceid_if_header_value_is_invalid() {
|
||||
Message message = MessageBuilder.createMessage("",
|
||||
headers("invalid", randomId()));
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(message);
|
||||
this.extractor.joinTrace(
|
||||
new MessagingTextMap(MessageBuilder.withPayload("")
|
||||
.copyHeaders(headers("invalid", randomId()))));
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
@@ -58,11 +55,10 @@ public class MessagingSpanExtractorTests {
|
||||
|
||||
@Test
|
||||
public void should_set_random_spanid_if_header_value_is_invalid() {
|
||||
Message message = MessageBuilder.createMessage("",
|
||||
headers(randomId(), "invalid"));
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(message);
|
||||
this.extractor.joinTrace(
|
||||
new MessagingTextMap(MessageBuilder.withPayload("")
|
||||
.copyHeaders(headers(randomId(), "invalid"))));
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
@@ -71,21 +67,16 @@ public class MessagingSpanExtractorTests {
|
||||
|
||||
@Test
|
||||
public void should_not_throw_exception_if_parent_id_is_invalid() {
|
||||
Message message = MessageBuilder.createMessage("",
|
||||
headers(randomId(), randomId(), "invalid"));
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(message);
|
||||
this.extractor.joinTrace(
|
||||
new MessagingTextMap(MessageBuilder.withPayload("")
|
||||
.copyHeaders(headers(randomId(), randomId(), "invalid"))));
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
}
|
||||
}
|
||||
|
||||
private MessageHeaders headers() {
|
||||
return headers(null, null, null);
|
||||
}
|
||||
|
||||
private MessageHeaders headers(String traceId) {
|
||||
return headers(traceId, null, null);
|
||||
}
|
||||
@@ -97,13 +88,13 @@ public class MessagingSpanExtractorTests {
|
||||
private MessageHeaders headers(String traceId, String spanId, String parentId) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (StringUtils.hasText(traceId)) {
|
||||
map.put(Span.TRACE_ID_NAME, traceId);
|
||||
map.put(TraceMessageHeaders.TRACE_ID_NAME, traceId);
|
||||
}
|
||||
if (StringUtils.hasText(spanId)) {
|
||||
map.put(Span.SPAN_ID_NAME, spanId);
|
||||
map.put(TraceMessageHeaders.SPAN_ID_NAME, spanId);
|
||||
}
|
||||
if (StringUtils.hasText(parentId)) {
|
||||
map.put(Span.PARENT_ID_NAME, parentId);
|
||||
map.put(TraceMessageHeaders.PARENT_ID_NAME, parentId);
|
||||
}
|
||||
return new MessageHeaders(map);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
@@ -29,15 +26,16 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class MessagingSpanInjectorTests {
|
||||
|
||||
private TraceKeys traceKeys = new TraceKeys();
|
||||
private MessagingSpanInjector messagingSpanInjector = new MessagingSpanInjector(
|
||||
this.traceKeys);
|
||||
private HeaderBasedMessagingInjector spanInjector = new HeaderBasedMessagingInjector(new TraceKeys());
|
||||
|
||||
@Test
|
||||
public void spanHeadersAdded() {
|
||||
@@ -45,9 +43,9 @@ public class MessagingSpanInjectorTests {
|
||||
Message<?> message = new GenericMessage<>("Hello World");
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message);
|
||||
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
this.spanInjector.inject(span, new MessagingTextMap(messageBuilder));
|
||||
|
||||
assertThat(messageBuilder.build().getHeaders()).containsKey(Span.SPAN_ID_NAME);
|
||||
assertThat(messageBuilder.build().getHeaders()).containsKey(TraceMessageHeaders.SPAN_ID_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -55,9 +53,9 @@ public class MessagingSpanInjectorTests {
|
||||
Span span = spanWithStringPayloadType();
|
||||
MessageBuilder<?> messageBuilder = messageWithIntegerPayloadType();
|
||||
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
this.spanInjector.inject(span, new MessagingTextMap(messageBuilder));
|
||||
|
||||
assertThat(messageBuilder.build().getHeaders()).containsKeys(Span.SPAN_ID_NAME,
|
||||
assertThat(messageBuilder.build().getHeaders()).containsKeys(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
"message/payload-type");
|
||||
assertThat(span).hasATag("message/payload-type", "java.lang.String");
|
||||
}
|
||||
@@ -83,14 +81,14 @@ public class MessagingSpanInjectorTests {
|
||||
MessageBuilder<String> messageBuilder = MessageBuilder
|
||||
.fromMessage(messageToBuild);
|
||||
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
this.spanInjector.inject(span, new MessagingTextMap(messageBuilder));
|
||||
|
||||
Message<String> message = messageBuilder.build();
|
||||
assertThat(message.getHeaders())
|
||||
.containsKey(NativeMessageHeaderAccessor.NATIVE_HEADERS);
|
||||
MessageHeaderAccessor natives = NativeMessageHeaderAccessor
|
||||
.getMutableAccessor(message);
|
||||
assertThat(natives.getMessageHeaders()).containsKey(Span.SPAN_ID_NAME);
|
||||
assertThat(natives.getMessageHeaders()).containsKey(TraceMessageHeaders.SPAN_ID_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -41,6 +37,7 @@ import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterce
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -54,6 +51,10 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@@ -102,6 +103,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
this.tracedChannel.unsubscribe(this);
|
||||
this.ignoredChannel.unsubscribe(this);
|
||||
@@ -111,10 +113,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
@Test
|
||||
public void nonExportableSpanCreation() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
|
||||
.setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
|
||||
assertNotNull("message was null", this.message);
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
then(this.span.isExportable()).isFalse();
|
||||
@@ -123,7 +125,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
@Test
|
||||
public void messageHeadersStillMutable() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
|
||||
.setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
|
||||
assertNotNull("message was null", this.message);
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(this.message, MessageHeaderAccessor.class);
|
||||
assertNotNull("Message header accessor should be still available", accessor);
|
||||
@@ -131,64 +133,18 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
|
||||
@Test
|
||||
public void parentSpanIncluded() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.TRACE_ID_NAME, Span.idToHex(10L))
|
||||
.setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
long traceId = Span
|
||||
.hexToId(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
|
||||
then(traceId).isEqualTo(10L);
|
||||
then(spanId).isNotEqualTo(20L);
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
// #332
|
||||
@Test
|
||||
public void shouldSendNewAndOldHeadersWhenNewHeadersWerePassed() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(10L))
|
||||
.setHeader(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String newSpanId = thenNewSpanIdEqualsOldSpanId();
|
||||
thenNewTraceIdEqualsOldTraceId();
|
||||
then(newSpanId).isNotEqualTo(20L);
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
private String thenNewSpanIdEqualsOldSpanId() {
|
||||
String newSpanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
then(newSpanId).isNotNull();
|
||||
String oldSpanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
then(oldSpanId).isEqualTo(newSpanId);
|
||||
return newSpanId;
|
||||
}
|
||||
|
||||
// #332
|
||||
@Test
|
||||
public void shouldSendNewAndOldHeadersWhenOldHeadersWerePassed() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.TRACE_ID_NAME, Span.idToHex(10L))
|
||||
.setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String newSpanId = thenNewSpanIdEqualsOldSpanId();
|
||||
thenNewTraceIdEqualsOldTraceId();
|
||||
then(newSpanId).isNotEqualTo(20L);
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
private void thenNewTraceIdEqualsOldTraceId() {
|
||||
long traceId = Span.hexToId(this.message.getHeaders()
|
||||
.get(TraceMessageHeaders.TRACE_ID_NAME, String.class));
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
long traceId = Span
|
||||
.hexToId(this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class));
|
||||
then(traceId).isEqualTo(10L);
|
||||
long oldTraceId = Span
|
||||
.hexToId(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
|
||||
then(oldTraceId).isEqualTo(traceId);
|
||||
then(spanId).isNotEqualTo(20L);
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,10 +152,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi").build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
|
||||
String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class);
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||
then(traceId).isNotNull();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
}
|
||||
@@ -208,16 +164,18 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
public void shouldLogClientReceivedClientSentEventWhenTheMessageIsSentAndReceived() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi").build());
|
||||
|
||||
then(this.span.logs()).extracting("event").contains(Span.CLIENT_SEND,
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
then(this.accumulator.getSpans().get(0).logs()).extracting("event").contains(Span.CLIENT_SEND,
|
||||
Span.CLIENT_RECV);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogServerReceivedServerSentEventWhenTheMessageIsPropagatedToTheNextListener() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader("X-Message-Sent", true).build());
|
||||
.setHeader(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT, true).build());
|
||||
|
||||
then(this.span.logs()).extracting("event").contains(Span.SERVER_RECV,
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
then(this.accumulator.getSpans().get(0).logs()).extracting("event").contains(Span.SERVER_RECV,
|
||||
Span.SERVER_SEND);
|
||||
}
|
||||
|
||||
@@ -228,10 +186,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.tracer.close(span);
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
|
||||
String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class);
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||
then(traceId).isNotNull();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
}
|
||||
@@ -245,10 +203,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.tracer.close(span);
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNotNull();
|
||||
|
||||
String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class);
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||
then(traceId).isNotNull();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
}
|
||||
@@ -280,10 +238,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.ignoredChannel.send(MessageBuilder.withPayload("hi").build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||
then(spanId).isNull();
|
||||
|
||||
String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class);
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||
then(traceId).isNull();
|
||||
|
||||
then(this.accumulator.getSpans()).isEmpty();
|
||||
@@ -295,8 +253,8 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
String hex128Bits = "463ac35c9f6413ad48485a3953bb6124";
|
||||
String lower64Bits = "48485a3953bb6124";
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.TRACE_ID_NAME, hex128Bits)
|
||||
.setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
.setHeader(TraceMessageHeaders.TRACE_ID_NAME, hex128Bits)
|
||||
.setHeader(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
then(this.message).isNotNull();
|
||||
|
||||
long traceId = Span.hexToId(this.message.getHeaders()
|
||||
|
||||
@@ -77,15 +77,15 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
assertNotNull("message was null", message);
|
||||
|
||||
Long spanId = Span
|
||||
.hexToId(message.getHeaders().get(Span.SPAN_ID_NAME, String.class));
|
||||
.hexToId(message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class));
|
||||
assertNotEquals("spanId was equal to parent's id", expectedSpanId, spanId);
|
||||
|
||||
long traceId = Span
|
||||
.hexToId(message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
|
||||
.hexToId(message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class));
|
||||
assertNotNull("traceId was null", traceId);
|
||||
|
||||
Long parentId = Span
|
||||
.hexToId(message.getHeaders().get(Span.PARENT_ID_NAME, String.class));
|
||||
.hexToId(message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME, String.class));
|
||||
assertEquals("parentId was not equal to parent's id", expectedSpanId, parentId);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -34,7 +36,7 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
public class HttpServletRequestExtractorTests {
|
||||
|
||||
@Mock HttpServletRequest request;
|
||||
HttpServletRequestExtractor extractor = new HttpServletRequestExtractor(
|
||||
ZipkinHttpSpanExtractor extractor = new ZipkinHttpSpanExtractor(
|
||||
Pattern.compile(""));
|
||||
|
||||
@Before
|
||||
@@ -45,7 +47,7 @@ public class HttpServletRequestExtractorTests {
|
||||
|
||||
@Test
|
||||
public void should_return_null_if_there_is_no_trace_id() {
|
||||
then(extractor.joinTrace(request)).isNull();
|
||||
then(extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,7 +55,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,7 +65,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -75,7 +77,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,12 +85,14 @@ public class HttpServletRequestExtractorTests {
|
||||
String hex128Bits = "463ac35c9f6413ad48485a3953bb6124";
|
||||
String lower64Bits = "48485a3953bb6124";
|
||||
|
||||
BDDMockito.given(this.request.getHeaderNames())
|
||||
.willReturn(new Vector<>(Arrays.asList(Span.TRACE_ID_NAME, Span.SPAN_ID_NAME)).elements());
|
||||
BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME))
|
||||
.willReturn(hex128Bits);
|
||||
BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME))
|
||||
.willReturn(lower64Bits);
|
||||
|
||||
Span span = this.extractor.joinTrace(this.request);
|
||||
Span span = this.extractor.joinTrace(new HttpServletRequestTextMap(this.request));
|
||||
|
||||
then(span.getTraceId()).isEqualTo(Span.hexToId(lower64Bits));
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -46,7 +47,10 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
@@ -83,8 +87,7 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
int port;
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
@Bean HttpSpanInjector customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
|
||||
@@ -113,22 +116,22 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
}
|
||||
|
||||
// tag::injector[]
|
||||
static class CustomHttpServletResponseSpanInjector
|
||||
implements SpanInjector<HttpServletResponse> {
|
||||
static class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
super.inject(span, carrier);
|
||||
carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
}
|
||||
}
|
||||
|
||||
static class HttpResponseInjectingTraceFilter extends GenericFilterBean {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpServletResponse> spanInjector;
|
||||
private final HttpSpanInjector spanInjector;
|
||||
|
||||
public HttpResponseInjectingTraceFilter(Tracer tracer, SpanInjector<HttpServletResponse> spanInjector) {
|
||||
public HttpResponseInjectingTraceFilter(Tracer tracer, HttpSpanInjector spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
@@ -137,9 +140,32 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
Span currentSpan = this.tracer.getCurrentSpan();
|
||||
this.spanInjector.inject(currentSpan, response);
|
||||
this.spanInjector.inject(currentSpan, new HttpServletResponseTextMap(response));
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
class HttpServletResponseTextMap implements SpanTextMap {
|
||||
|
||||
private final HttpServletResponse delegate;
|
||||
|
||||
HttpServletResponseTextMap(HttpServletResponse delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String header : this.delegate.getHeaderNames()) {
|
||||
map.put(header, this.delegate.getHeader(header));
|
||||
}
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value) {
|
||||
this.delegate.addHeader(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::injector[]
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -23,16 +28,19 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.util.TextMapUtil;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -43,15 +51,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.jayway.awaitility.Awaitility.await;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -59,11 +60,11 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class TraceFilterCustomExtractorTests {
|
||||
@Autowired Random random;
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
@Autowired CustomRestController customRestController;
|
||||
@Autowired ArrayListSpanAccumulator accumulator;
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -73,23 +74,23 @@ public class TraceFilterCustomExtractorTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void should_create_a_valid_span_from_custom_headers() {
|
||||
long spanId = this.random.nextLong();
|
||||
long traceId = this.random.nextLong();
|
||||
RequestEntity<?> requestEntity = RequestEntity
|
||||
.get(URI.create("http://localhost:" + this.config.port + "/headers"))
|
||||
.header("correlationId", Span.idToHex(traceId))
|
||||
.header("mySpanId", Span.idToHex(spanId)).build();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity,
|
||||
Map.class);
|
||||
final Span newSpan = this.tracer.createSpan("new_span");
|
||||
ResponseEntity<Map> responseEntity = null;
|
||||
try {
|
||||
RequestEntity<?> requestEntity = RequestEntity
|
||||
.get(URI.create("http://localhost:" + this.config.port + "/headers"))
|
||||
.build();
|
||||
responseEntity = this.restTemplate.exchange(requestEntity, Map.class);
|
||||
|
||||
} finally {
|
||||
this.tracer.close(newSpan);
|
||||
}
|
||||
await().atMost(5, SECONDS).until(() -> then(this.accumulator.getSpans().stream().filter(
|
||||
span -> span.getSpanId() == spanId).findFirst().get())
|
||||
.hasTraceIdEqualTo(traceId));
|
||||
then(responseEntity.getBody())
|
||||
.containsEntry("correlationid", Span.idToHex(traceId))
|
||||
.containsEntry("myspanid", Span.idToHex(spanId))
|
||||
span -> span.getSpanId() == newSpan.getSpanId()).findFirst().get())
|
||||
.hasTraceIdEqualTo(newSpan.getTraceId()));
|
||||
BDDAssertions.then(responseEntity.getBody())
|
||||
.containsEntry("correlationid", Span.idToHex(newSpan.getTraceId()))
|
||||
.containsKey("myspanid")
|
||||
.as("input request headers");
|
||||
}
|
||||
|
||||
@@ -101,9 +102,13 @@ public class TraceFilterCustomExtractorTests {
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
@Primary
|
||||
SpanExtractor<HttpServletRequest> customHttpServletRequestSpanExtractor() {
|
||||
return new CustomHttpServletRequestSpanExtractor();
|
||||
HttpSpanInjector customHttpSpanInjector() {
|
||||
return new CustomHttpSpanInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpSpanExtractor customHttpSpanExtractor() {
|
||||
return new CustomHttpSpanExtractor();
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@@ -134,24 +139,34 @@ public class TraceFilterCustomExtractorTests {
|
||||
}
|
||||
|
||||
// tag::extractor[]
|
||||
static class CustomHttpServletRequestSpanExtractor
|
||||
implements SpanExtractor<HttpServletRequest> {
|
||||
static class CustomHttpSpanExtractor implements HttpSpanExtractor {
|
||||
|
||||
@Override
|
||||
public Span joinTrace(HttpServletRequest carrier) {
|
||||
long traceId = Span.hexToId(carrier.getHeader("correlationId"));
|
||||
long spanId = Span.hexToId(carrier.getHeader("mySpanId"));
|
||||
@Override public Span joinTrace(SpanTextMap carrier) {
|
||||
Map<String, String> map = TextMapUtil.asMap(carrier);
|
||||
long traceId = Span.hexToId(map.get("correlationid"));
|
||||
long spanId = Span.hexToId(map.get("myspanid"));
|
||||
// extract all necessary headers
|
||||
Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId);
|
||||
// build rest of the Span
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomHttpSpanInjector implements HttpSpanInjector {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
carrier.put("correlationId", Span.idToHex(span.getTraceId()));
|
||||
carrier.put("mySpanId", Span.idToHex(span.getSpanId()));
|
||||
}
|
||||
}
|
||||
// end::extractor[]
|
||||
|
||||
@RestController
|
||||
static class CustomRestController {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
@RequestMapping("/headers")
|
||||
public Map<String, String> headers(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
||||
@@ -73,7 +73,7 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
@Test
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new ZipkinHttpSpanExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
keysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
@@ -85,7 +85,7 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, generator.nextLong())
|
||||
.header(Span.TRACE_ID_NAME, generator.nextLong()).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new ZipkinHttpSpanExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
keysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
@@ -66,7 +67,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Mock SpanLogger spanLogger;
|
||||
ArrayListSpanAccumulator spanReporter = new ArrayListSpanAccumulator();
|
||||
SpanExtractor<HttpServletRequest> spanExtractor = new HttpServletRequestExtractor(Pattern
|
||||
HttpSpanExtractor spanExtractor = new ZipkinHttpSpanExtractor(Pattern
|
||||
.compile(TraceFilter.DEFAULT_SKIP_PATTERN));
|
||||
|
||||
private Tracer tracer;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
@@ -67,7 +68,7 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), this.spanAccumulator);
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(),
|
||||
new TraceRestTemplateInterceptor(this.tracer, new ZipkinHttpSpanInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()))));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
@@ -70,7 +71,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), this.spanAccumulator);
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(),
|
||||
new TraceRestTemplateInterceptor(this.tracer, new ZipkinHttpSpanInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()))));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@@ -32,10 +32,12 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
@@ -76,6 +78,8 @@ public class FeignRetriesTests {
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
BDDMockito.given(this.beanFactory.getBean(HttpTraceKeysInjector.class))
|
||||
.willReturn(new HttpTraceKeysInjector(this.tracer, new TraceKeys()));
|
||||
BDDMockito.given(this.beanFactory.getBean(HttpSpanInjector.class))
|
||||
.willReturn(new ZipkinHttpSpanInjector());
|
||||
BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(this.tracer);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,13 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.HttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
@@ -69,6 +71,8 @@ public class TraceFeignClientTests {
|
||||
ExceptionUtils.setFail(true);
|
||||
BDDMockito.given(this.beanFactory.getBean(HttpTraceKeysInjector.class))
|
||||
.willReturn(new HttpTraceKeysInjector(this.tracer, new TraceKeys()));
|
||||
BDDMockito.given(this.beanFactory.getBean(HttpSpanInjector.class))
|
||||
.willReturn(new ZipkinHttpSpanInjector());
|
||||
BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(this.tracer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.common;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.HttpSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
@@ -34,7 +32,7 @@ public abstract class AbstractMvcIntegrationTest {
|
||||
protected MockMvc mockMvc;
|
||||
@Autowired protected Tracer tracer;
|
||||
@Autowired protected TraceKeys traceKeys;
|
||||
@Autowired protected SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
@Autowired protected HttpSpanExtractor spanExtractor;
|
||||
@Autowired protected HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
@Before
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ApacheHttpClientRibbonRequestCustomizerTests {
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET");
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
Header header = request.getFirstHeader(Span.SAMPLED_NAME);
|
||||
@@ -62,7 +62,7 @@ public class ApacheHttpClientRibbonRequestCustomizerTests {
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET");
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
|
||||
@@ -49,7 +49,7 @@ public class OkHttpClientRibbonRequestCustomizerTests {
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
then(request.header(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
@@ -59,7 +59,7 @@ public class OkHttpClientRibbonRequestCustomizerTests {
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
|
||||
@@ -49,7 +49,7 @@ public class RestClientRibbonRequestCustomizerTests {
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
then(request.getHttpHeaders().getFirstValue(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
@@ -59,7 +59,7 @@ public class RestClientRibbonRequestCustomizerTests {
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder));
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.cloud.sleuth.NoOpSpanReporter;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
@@ -55,7 +56,7 @@ public class TracePreZuulFilterTests {
|
||||
private DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
|
||||
private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new RequestContextInjector(),
|
||||
private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()));
|
||||
|
||||
@After
|
||||
@@ -106,7 +107,7 @@ public class TracePreZuulFilterTests {
|
||||
Span startedSpan = this.tracer.createSpan("http:start");
|
||||
final AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
new TracePreZuulFilter(this.tracer, new RequestContextInjector(),
|
||||
new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys())) {
|
||||
@Override
|
||||
public Object run() {
|
||||
@@ -127,7 +128,7 @@ public class TracePreZuulFilterTests {
|
||||
Span startedSpan = this.tracer.createSpan("http:start");
|
||||
final AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
new TracePreZuulFilter(this.tracer, new RequestContextInjector(),
|
||||
new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys())) {
|
||||
@Override
|
||||
public Object run() {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.cloud.sleuth.util;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class TextMapUtilTests {
|
||||
|
||||
@Test
|
||||
public void should_convert_an_iterable_to_a_caseinsensitive_map() throws Exception {
|
||||
List<Map.Entry<String, String>> iterable = new ArrayList<>();
|
||||
iterable.add(new AbstractMap.SimpleEntry<>("foo", "bar"));
|
||||
|
||||
Map<String, String> map = TextMapUtil.asMap(iterable);
|
||||
|
||||
then(map)
|
||||
.containsKey("foo")
|
||||
.containsKey("FOO")
|
||||
.containsKey("FoO")
|
||||
.contains(new AbstractMap.SimpleEntry<>("foo", "bar"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user