[#19] Introduced SpanInjector and SpanExtractor
This commit is contained in:
@@ -65,6 +65,7 @@ public class Span {
|
||||
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
|
||||
|
||||
public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc";
|
||||
|
||||
/**
|
||||
* <b>cr</b> - Client Receive. Signifies the end of the span. The client has successfully received the
|
||||
* response from the server side. If one subtracts the cs timestamp from this timestamp one
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Adopted from <a href="https://github.com/opentracing/opentracing-java/pull/11/files#diff-eb9c3460aba76aabc0de04b05e4a2b3d"></a>OpenTracing</a>
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanExtractor<T> {
|
||||
/** Returns a SpanBuilder provided
|
||||
* a “carrier” object from which to extract identifying information needed by the new Span instance.
|
||||
*
|
||||
* If the carrier object has no such span stored within it, a new Span is created.
|
||||
*
|
||||
* Unless there’s an error, it returns a Span.
|
||||
* The Span generated from the builder can be used in the host process like any other.
|
||||
*
|
||||
* (Note that some OpenTracing implementations consider the Spans on either side of an RPC to have the same identity,
|
||||
* and others consider the caller to be the parent and the receiver to be the child).
|
||||
*/
|
||||
Span joinTrace(T carrier);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Adopted from <a href="https://github.com/opentracing/opentracing-java/blob/master/opentracing/src/main/java/opentracing/Tracer.java"></a>OpenTracing</a>
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SpanInjector<T> {
|
||||
/** Takes two arguments:
|
||||
* a Span instance, and
|
||||
* a “carrier” object in which to inject that Span for cross-process propagation.
|
||||
*
|
||||
* A “carrier” object is some sort of http or rpc envelope, for example HeaderGroup (from Apache HttpComponents).
|
||||
*
|
||||
* Attempting to inject to a carrier that has been registered/configured to this Tracer will result in a
|
||||
* IllegalStateException.
|
||||
*/
|
||||
void inject(Span span, T carrier);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Names of default headers that need to be sent between processes
|
||||
* for tracing to be operational.
|
||||
*
|
||||
* Default Zipkin Headers are
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code X-B3-TraceId} 64 encoded bits</li>
|
||||
* <li>{@code X-B3-SpanId} 64 encoded bits</li>
|
||||
* <li>{@code X-B3-ParentSpanId} 64 encoded bits</li>
|
||||
* <li>{@code X-B3-Sampled} Boolean (either “1” or “0”)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.sleuth.headers")
|
||||
public class TraceHeaders {
|
||||
|
||||
private Zipkin zipkin;
|
||||
private Sleuth sleuth;
|
||||
|
||||
public Zipkin getZipkin() {
|
||||
return this.zipkin;
|
||||
}
|
||||
|
||||
public void setZipkin(Zipkin zipkin) {
|
||||
this.zipkin = zipkin;
|
||||
}
|
||||
|
||||
public Sleuth getSleuth() {
|
||||
return this.sleuth;
|
||||
}
|
||||
|
||||
public void setSleuth(Sleuth sleuth) {
|
||||
this.sleuth = sleuth;
|
||||
}
|
||||
|
||||
private static class Zipkin {
|
||||
private String traceId = "X-B3-TraceId";
|
||||
private String spanId = "X-B3-SpanId";
|
||||
private String parentSpanId = "X-B3-ParentSpanId";
|
||||
private String sampled = "X-B3-Sampled";
|
||||
|
||||
public String getTraceId() {
|
||||
return this.traceId;
|
||||
}
|
||||
|
||||
public void setTraceId(String traceId) {
|
||||
this.traceId = traceId;
|
||||
}
|
||||
|
||||
public String getSpanId() {
|
||||
return this.spanId;
|
||||
}
|
||||
|
||||
public void setSpanId(String spanId) {
|
||||
this.spanId = spanId;
|
||||
}
|
||||
|
||||
public String getParentSpanId() {
|
||||
return this.parentSpanId;
|
||||
}
|
||||
|
||||
public void setParentSpanId(String parentSpanId) {
|
||||
this.parentSpanId = parentSpanId;
|
||||
}
|
||||
|
||||
public String getSampled() {
|
||||
return this.sampled;
|
||||
}
|
||||
|
||||
public void setSampled(String sampled) {
|
||||
this.sampled = sampled;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Sleuth {
|
||||
private String processId = "X-Process-Id";
|
||||
private String spanName = "X-Span-Name";
|
||||
private String exportable = "X-Span-Export";
|
||||
|
||||
public String getProcessId() {
|
||||
return this.processId;
|
||||
}
|
||||
|
||||
public void setProcessId(String processId) {
|
||||
this.processId = processId;
|
||||
}
|
||||
|
||||
public String getSpanName() {
|
||||
return this.spanName;
|
||||
}
|
||||
|
||||
public void setSpanName(String spanName) {
|
||||
this.spanName = spanName;
|
||||
}
|
||||
|
||||
public String getExportable() {
|
||||
return this.exportable;
|
||||
}
|
||||
|
||||
public void setExportable(String exportable) {
|
||||
this.exportable = exportable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,8 @@ public class TraceAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Tracer.class)
|
||||
public DefaultTracer traceManager(Sampler sampler, Random random,
|
||||
SpanNamer spanNamer, SpanLogger spanLogger,
|
||||
SpanReporter spanReporter) {
|
||||
SpanNamer spanNamer, SpanLogger spanLogger,
|
||||
SpanReporter spanReporter) {
|
||||
return new DefaultTracer(sampler, random, spanNamer, spanLogger,
|
||||
spanReporter);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
@@ -31,16 +32,17 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
protected static final String MESSAGE_COMPONENT = "message";
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private final Random random;
|
||||
|
||||
private final TraceKeys traceKeys;
|
||||
private final SpanExtractor<Message> spanExtractor;
|
||||
private final SpanInjector<MessageBuilder> spanInjector;
|
||||
|
||||
protected AbstractTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys,
|
||||
Random random) {
|
||||
SpanExtractor<Message> spanExtractor,
|
||||
SpanInjector<MessageBuilder> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.random = random;
|
||||
this.spanExtractor = spanExtractor;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
protected Tracer getTracer() {
|
||||
@@ -51,49 +53,16 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
protected SpanInjector<MessageBuilder> getSpanInjector() {
|
||||
return this.spanInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a span given the message and a channel. Returns null when there was no
|
||||
* trace id passed initially.
|
||||
* Returns a span given the message and a channel. Returns {@code null} if ids
|
||||
* are missing.
|
||||
*/
|
||||
protected Span buildSpan(Message<?> message) {
|
||||
if (!hasHeader(message, Span.TRACE_ID_NAME)
|
||||
|| !hasHeader(message, Span.SPAN_ID_NAME)) {
|
||||
return null; // cannot build a span without ids
|
||||
}
|
||||
long spanId = hasHeader(message, Span.SPAN_ID_NAME)
|
||||
? Span.hexToId(getHeader(message, Span.SPAN_ID_NAME))
|
||||
: this.random.nextLong();
|
||||
long traceId = Span.hexToId(getHeader(message, Span.TRACE_ID_NAME));
|
||||
Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
|
||||
if (hasHeader(message, Span.NOT_SAMPLED_NAME)) {
|
||||
span.exportable(false);
|
||||
}
|
||||
String parentId = getHeader(message, Span.PARENT_ID_NAME);
|
||||
String processId = getHeader(message, Span.PROCESS_ID_NAME);
|
||||
String spanName = getHeader(message, Span.SPAN_NAME_NAME);
|
||||
if (spanName != null) {
|
||||
span.name(spanName);
|
||||
}
|
||||
if (processId != null) {
|
||||
span.processId(processId);
|
||||
}
|
||||
if (parentId != null) {
|
||||
span.parent(Span.hexToId(parentId));
|
||||
}
|
||||
span.remote(true);
|
||||
return span.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);
|
||||
return this.spanExtractor.joinTrace(message);
|
||||
}
|
||||
|
||||
String getChannelName(MessageChannel channel) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* Utility class to contain both {@link MessageBuilder} and the {@link Message}.
|
||||
*
|
||||
* {@link MessageBuilder} is mutable
|
||||
* {@link Message} is immutable
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class MessageBuilderHolder {
|
||||
final MessageBuilder messageBuilder;
|
||||
final Message message;
|
||||
|
||||
public MessageBuilderHolder(MessageBuilder messageBuilder, Message message) {
|
||||
this.messageBuilder = messageBuilder;
|
||||
this.message = message;
|
||||
}
|
||||
public MessageBuilderHolder(MessageBuilder messageBuilder) {
|
||||
this.messageBuilder = messageBuilder;
|
||||
this.message = messageBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.Random;
|
||||
|
||||
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
|
||||
*/
|
||||
public class MessagingSpanExtractor implements SpanExtractor<Message> {
|
||||
|
||||
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)) {
|
||||
return null;
|
||||
//TODO: Consider throwing IllegalArgumentException;
|
||||
}
|
||||
long spanId = hasHeader(carrier, Span.SPAN_ID_NAME)
|
||||
? Span.hexToId(getHeader(carrier, Span.SPAN_ID_NAME))
|
||||
: this.random.nextLong();
|
||||
long traceId = Span.hexToId(getHeader(carrier, Span.TRACE_ID_NAME));
|
||||
SpanBuilder spanBuilder = Span.builder().traceId(traceId).spanId(spanId);
|
||||
if (hasHeader(carrier, Span.NOT_SAMPLED_NAME)) {
|
||||
spanBuilder.exportable(false);
|
||||
}
|
||||
String parentId = getHeader(carrier, Span.PARENT_ID_NAME);
|
||||
String processId = getHeader(carrier, Span.PROCESS_ID_NAME);
|
||||
String spanName = getHeader(carrier, Span.SPAN_NAME_NAME);
|
||||
if (spanName != null) {
|
||||
spanBuilder.name(spanName);
|
||||
}
|
||||
if (processId != null) {
|
||||
spanBuilder.processId(processId);
|
||||
}
|
||||
if (parentId != null) {
|
||||
spanBuilder.parent(Span.hexToId(parentId));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
@@ -29,53 +30,40 @@ import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility for manipulating message headers related to span data.
|
||||
* Creates a {@link Span.SpanBuilder} from {@link Message}
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SpanMessageHeaders {
|
||||
public class MessagingSpanInjector implements SpanInjector<MessageBuilder> {
|
||||
|
||||
public static final String SPAN_HEADER = "X-Current-Span";
|
||||
|
||||
public static Span getSpanFromHeader(Message<?> message) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
Object object = message.getHeaders().get(SPAN_HEADER);
|
||||
if (object instanceof Span) {
|
||||
return (Span) object;
|
||||
}
|
||||
return null;
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public MessagingSpanInjector(TraceKeys traceKeys) {
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default headers for a message. Check {@link Span} constants for
|
||||
* more information what the default headers are. If a span already has
|
||||
* a tag set it will not get overridden.
|
||||
*
|
||||
* @param traceKeys - the global configuration for trace keys
|
||||
* @param message - message to which headers will be added
|
||||
* @param span - span from which headers will be taken
|
||||
* @return the input message with updated headers
|
||||
*/
|
||||
public static Message<?> addSpanHeaders(TraceKeys traceKeys, Message<?> message,
|
||||
Span span) {
|
||||
@Override
|
||||
public void inject(Span span, MessageBuilder carrier) {
|
||||
Message initialMessage = carrier.build();
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor
|
||||
.getMutableAccessor(message);
|
||||
.getMutableAccessor(initialMessage);
|
||||
if (span == null) {
|
||||
if (!message.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
|
||||
if (!initialMessage.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
|
||||
accessor.setHeader(Span.NOT_SAMPLED_NAME, "true");
|
||||
return MessageBuilder.createMessage(message.getPayload(),
|
||||
accessor.getMessageHeaders());
|
||||
carrier.setHeaders(accessor);
|
||||
return;
|
||||
}
|
||||
return message;
|
||||
return;
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
addHeader(headers, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
addHeader(headers, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
if (span.isExportable()) {
|
||||
addAnnotations(traceKeys, message, span);
|
||||
addAnnotations(this.traceKeys, initialMessage, span);
|
||||
Long parentId = getFirst(span.getParents());
|
||||
if (parentId != null) {
|
||||
addHeader(headers, Span.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
@@ -94,11 +82,10 @@ public class SpanMessageHeaders {
|
||||
nativeAccessor.setNativeHeader(name, headers.get(name));
|
||||
}
|
||||
}
|
||||
return MessageBuilder.createMessage(message.getPayload(),
|
||||
accessor.getMessageHeaders());
|
||||
carrier.setHeaders(accessor);
|
||||
}
|
||||
|
||||
public static void addAnnotations(TraceKeys traceKeys, Message<?> message,
|
||||
private void addAnnotations(TraceKeys traceKeys, Message<?> message,
|
||||
Span span) {
|
||||
for (String name : traceKeys.getMessage().getHeaders()) {
|
||||
if (message.getHeaders().containsKey(name)) {
|
||||
@@ -113,7 +100,7 @@ public class SpanMessageHeaders {
|
||||
addPayloadAnnotations(traceKeys, message.getPayload(), span);
|
||||
}
|
||||
|
||||
static void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) {
|
||||
private void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) {
|
||||
if (payload != null) {
|
||||
tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(),
|
||||
payload.getClass().getCanonicalName());
|
||||
@@ -128,21 +115,20 @@ public class SpanMessageHeaders {
|
||||
}
|
||||
}
|
||||
|
||||
private static void tagIfEntryMissing(Span span, String key, String value) {
|
||||
private void tagIfEntryMissing(Span span, String key, String value) {
|
||||
if (!span.tags().containsKey(key)) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addHeader(Map<String, String> headers, String name,
|
||||
private void addHeader(Map<String, String> headers, String name,
|
||||
String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
headers.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Long getFirst(List<Long> parents) {
|
||||
private Long getFirst(List<Long> parents) {
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
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;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* A channel interceptor that automatically starts / continues / closes and detaches spans.
|
||||
@@ -34,13 +35,17 @@ import org.springframework.messaging.MessageHandler;
|
||||
*/
|
||||
public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
|
||||
public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, Random random) {
|
||||
super(tracer, traceKeys, random);
|
||||
private static final String SPAN_HEADER = "X-Current-Span";
|
||||
|
||||
public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys,
|
||||
SpanExtractor<Message> spanExtractor,
|
||||
SpanInjector<MessageBuilder> spanInjector) {
|
||||
super(tracer, traceKeys, spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
getTracer().close(SpanMessageHeaders.getSpanFromHeader(message));
|
||||
getTracer().close(getSpanFromHeader(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +54,9 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
: buildSpan(message);
|
||||
String name = getMessageChannelName(channel);
|
||||
Span span = startSpan(parentSpan, name, message);
|
||||
return SpanMessageHeaders.addSpanHeaders(getTraceKeys(), message, span);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message);
|
||||
getSpanInjector().inject(span, messageBuilder);
|
||||
return messageBuilder.build();
|
||||
}
|
||||
|
||||
private Span startSpan(Span span, String name, Message<?> message) {
|
||||
@@ -65,14 +72,25 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
@Override
|
||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler) {
|
||||
getTracer().continueSpan(SpanMessageHeaders.getSpanFromHeader(message));
|
||||
getTracer().continueSpan(getSpanFromHeader(message));
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
||||
MessageHandler handler, Exception ex) {
|
||||
getTracer().detach(SpanMessageHeaders.getSpanFromHeader(message));
|
||||
getTracer().detach(getSpanFromHeader(message));
|
||||
}
|
||||
|
||||
private Span getSpanFromHeader(Message<?> message) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
Object object = message.getHeaders().get(SPAN_HEADER);
|
||||
if (object instanceof Span) {
|
||||
return (Span) object;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,17 +18,22 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
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 Auto-configuration}
|
||||
@@ -50,8 +55,26 @@ public class TraceSpringIntegrationAutoConfiguration {
|
||||
@Bean
|
||||
@GlobalChannelInterceptor
|
||||
public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer,
|
||||
TraceKeys traceKeys, Random random) {
|
||||
return new TraceChannelInterceptor(tracer, traceKeys, random);
|
||||
TraceKeys traceKeys, Random random,
|
||||
@Qualifier("messagingSpanExtractor") SpanExtractor<Message> spanExtractor,
|
||||
@Qualifier("messagingSpanInjector") SpanInjector<MessageBuilder> spanInjector) {
|
||||
return new TraceChannelInterceptor(tracer, traceKeys, spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("messagingSpanExtractor")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.injector.enabled", matchIfMissing = true)
|
||||
public SpanExtractor<Message> messagingSpanExtractor(Random random) {
|
||||
return new MessagingSpanExtractor(random);
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("messagingSpanInjector")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.injector.enabled", matchIfMissing = true)
|
||||
public SpanInjector<MessageBuilder> messagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new MessagingSpanInjector(traceKeys);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,24 @@ package org.springframework.cloud.sleuth.instrument.messaging.websocket;
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
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.MessagingSpanExtractor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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;
|
||||
@@ -24,19 +36,17 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
* @see AbstractWebSocketMessageBrokerConfigurer
|
||||
*/
|
||||
@Component
|
||||
@Configuration
|
||||
@AutoConfigureAfter(TraceSpringIntegrationAutoConfiguration.class)
|
||||
@ConditionalOnClass(DelegatingWebSocketMessageBrokerConfiguration.class)
|
||||
@ConditionalOnBean(AbstractWebSocketMessageBrokerConfigurer.class)
|
||||
public class TraceWebSocketAutoConfiguration
|
||||
extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
@Autowired
|
||||
private TraceKeys traceKeys;
|
||||
|
||||
@Autowired
|
||||
private Random random;
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired @Qualifier("stompMessagingSpanExtractor") SpanExtractor<Message> spanExtractor;
|
||||
@Autowired @Qualifier("stompMessagingSpanInjector") SpanInjector<MessageBuilder> spanInjector;
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
@@ -46,12 +56,28 @@ public class TraceWebSocketAutoConfiguration
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(
|
||||
new TraceChannelInterceptor(this.tracer, this.traceKeys, this.random));
|
||||
new TraceChannelInterceptor(this.tracer, this.traceKeys, this.spanExtractor, this.spanInjector));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(
|
||||
new TraceChannelInterceptor(this.tracer, this.traceKeys, this.random));
|
||||
new TraceChannelInterceptor(this.tracer, this.traceKeys, this.spanExtractor, this.spanInjector));
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("stompMessagingSpanExtractor")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.websocket.injector.enabled", matchIfMissing = true)
|
||||
public SpanExtractor<Message> stompMessagingSpanExtractor(Random random) {
|
||||
return new MessagingSpanExtractor(random);
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("stompMessagingSpanInjector")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.websocket.injector.enabled", matchIfMissing = true)
|
||||
public SpanInjector<MessageBuilder> stompMessagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new MessagingSpanInjector(traceKeys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
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 String HTTP_COMPONENT = "http";
|
||||
|
||||
private final Random random;
|
||||
private final Pattern skipPattern;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public HttpServletRequestExtractor(Random random, Pattern skipPattern) {
|
||||
this.random = random;
|
||||
this.skipPattern = skipPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span joinTrace(HttpServletRequest carrier) {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(carrier);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| carrier.getHeader(Span.NOT_SAMPLED_NAME) != null;
|
||||
long traceId = Span
|
||||
.hexToId(carrier.getHeader(Span.TRACE_ID_NAME));
|
||||
long spanId = carrier.getHeader(Span.SPAN_ID_NAME) != null
|
||||
? Span.hexToId(carrier.getHeader(Span.SPAN_ID_NAME))
|
||||
: this.random.nextLong();
|
||||
|
||||
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,44 @@
|
||||
/*
|
||||
* 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.HttpServletResponse;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link HttpServletResponse}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class HttpServletResponseInjector implements SpanInjector<HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
if (span == null) {
|
||||
return;
|
||||
}
|
||||
if (!carrier.containsHeader(Span.SPAN_ID_NAME)) {
|
||||
carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Utility class to retrieve data from Servlet
|
||||
* HTTP request and response
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ServletUtils {
|
||||
static boolean hasHeader(HttpServletRequest request, HttpServletResponse response,
|
||||
String name) {
|
||||
String value = request.getHeader(name);
|
||||
return value != null || response.getHeader(name) != null;
|
||||
}
|
||||
|
||||
static String getHeader(HttpServletRequest request, HttpServletResponse response,
|
||||
String name) {
|
||||
String value = request.getHeader(name);
|
||||
return value != null ? value : response.getHeader(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
@@ -28,7 +27,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Span.SpanBuilder;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import static org.springframework.cloud.sleuth.instrument.web.ServletUtils.hasHeader;
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
|
||||
/**
|
||||
@@ -66,6 +67,8 @@ import static org.springframework.util.StringUtils.hasText;
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 5)
|
||||
public class TraceFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
protected static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName()
|
||||
+ ".TRACE";
|
||||
|
||||
@@ -75,34 +78,36 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
private final Pattern skipPattern;
|
||||
private final Random random;
|
||||
private final SpanReporter spanReporter;
|
||||
private final SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
private final SpanInjector<HttpServletResponse> spanInjector;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter) {
|
||||
this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), new Random(),
|
||||
spanReporter);
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor, SpanInjector<HttpServletResponse> spanInjector) {
|
||||
this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), spanReporter,
|
||||
spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern,
|
||||
Random random, SpanReporter spanReporter) {
|
||||
SpanReporter spanReporter, SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
SpanInjector<HttpServletResponse> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.skipPattern = skipPattern;
|
||||
this.random = random;
|
||||
this.spanReporter = spanReporter;
|
||||
this.spanExtractor = spanExtractor;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| getHeader(request, response, Span.NOT_SAMPLED_NAME) != null;
|
||||
|
||||
|| ServletUtils.getHeader(request, response, Span.NOT_SAMPLED_NAME) != null;
|
||||
Span spanFromRequest = (Span) request.getAttribute(TRACE_REQUEST_ATTR);
|
||||
if (spanFromRequest != null) {
|
||||
this.tracer.continueSpan(spanFromRequest);
|
||||
@@ -110,66 +115,15 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
else if (skip) {
|
||||
addToResponseIfNotPresent(response, Span.NOT_SAMPLED_NAME, "");
|
||||
}
|
||||
|
||||
String protocol = "http";
|
||||
String name = protocol + ":" + uri;
|
||||
if (spanFromRequest == null) {
|
||||
if (hasHeader(request, response, Span.TRACE_ID_NAME)) {
|
||||
long traceId = Span
|
||||
.hexToId(getHeader(request, response, Span.TRACE_ID_NAME));
|
||||
long spanId = hasHeader(request, response, Span.SPAN_ID_NAME)
|
||||
? Span.hexToId(getHeader(request, response, Span.SPAN_ID_NAME))
|
||||
: this.random.nextLong();
|
||||
|
||||
SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
|
||||
if (skip) {
|
||||
span.exportable(false);
|
||||
}
|
||||
String processId = getHeader(request, response, Span.PROCESS_ID_NAME);
|
||||
String parentName = getHeader(request, response, Span.SPAN_NAME_NAME);
|
||||
if (StringUtils.hasText(parentName)) {
|
||||
span.name(parentName);
|
||||
}
|
||||
else {
|
||||
span.name(protocol + ":" + "/parent" + uri);
|
||||
}
|
||||
if (StringUtils.hasText(processId)) {
|
||||
span.processId(processId);
|
||||
}
|
||||
if (hasHeader(request, response, Span.PARENT_ID_NAME)) {
|
||||
span.parent(Span
|
||||
.hexToId(getHeader(request, response, Span.PARENT_ID_NAME)));
|
||||
}
|
||||
span.remote(true);
|
||||
|
||||
Span parent = span.build();
|
||||
spanFromRequest = this.tracer.createSpan(name, parent);
|
||||
if (parent != null && parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
|
||||
}
|
||||
else {
|
||||
if (skip) {
|
||||
spanFromRequest = this.tracer.createSpan(name, NeverSampler.INSTANCE);
|
||||
}
|
||||
else {
|
||||
spanFromRequest = this.tracer.createSpan(name);
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
}
|
||||
}
|
||||
|
||||
String name = HTTP_COMPONENT + ":" + uri;
|
||||
spanFromRequest = createSpan(request, response, skip, spanFromRequest, name);
|
||||
Throwable exception = null;
|
||||
try {
|
||||
|
||||
addRequestTags(request);
|
||||
// Add headers before filter chain in case one of the filters flushes the
|
||||
// response...
|
||||
addResponseHeaders(response, spanFromRequest);
|
||||
this.spanInjector.inject(spanFromRequest, response);
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
}
|
||||
catch (Throwable e) {
|
||||
exception = e;
|
||||
@@ -199,13 +153,33 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
}
|
||||
}
|
||||
|
||||
private void addResponseHeaders(HttpServletResponse response, Span span) {
|
||||
if (span != null) {
|
||||
if (!response.containsHeader(Span.SPAN_ID_NAME)) {
|
||||
response.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
response.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
}
|
||||
/**
|
||||
* Creates a span and appends it as the current request's attribute
|
||||
*/
|
||||
private Span createSpan(HttpServletRequest request, HttpServletResponse response,
|
||||
boolean skip, Span spanFromRequest, String name) {
|
||||
if (spanFromRequest != null) {
|
||||
return spanFromRequest;
|
||||
}
|
||||
if (hasHeader(request, response, Span.TRACE_ID_NAME)) {
|
||||
Span parent = this.spanExtractor
|
||||
.joinTrace(request);
|
||||
spanFromRequest = this.tracer.createSpan(name, parent);
|
||||
if (parent != null && parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
}
|
||||
else {
|
||||
if (skip) {
|
||||
spanFromRequest = this.tracer.createSpan(name, NeverSampler.INSTANCE);
|
||||
}
|
||||
else {
|
||||
spanFromRequest = this.tracer.createSpan(name);
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
}
|
||||
return spanFromRequest;
|
||||
}
|
||||
|
||||
/** Override to add annotations not defined in {@link TraceKeys}. */
|
||||
@@ -242,18 +216,6 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasHeader(HttpServletRequest request, HttpServletResponse response,
|
||||
String name) {
|
||||
String value = request.getHeader(name);
|
||||
return value != null || response.getHeader(name) != null;
|
||||
}
|
||||
|
||||
private String getHeader(HttpServletRequest request, HttpServletResponse response,
|
||||
String name) {
|
||||
String value = request.getHeader(name);
|
||||
return value != null ? value : response.getHeader(name);
|
||||
}
|
||||
|
||||
private void addToResponseIfNotPresent(HttpServletResponse response, String name,
|
||||
String value) {
|
||||
if (!hasText(response.getHeader(name))) {
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.async.TraceContinuingCallable;
|
||||
@@ -72,12 +71,10 @@ public class TraceWebAspect {
|
||||
.getLog(TraceWebAspect.class);
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanAccessor accessor;
|
||||
private final SpanNamer spanNamer;
|
||||
|
||||
public TraceWebAspect(Tracer tracer, SpanAccessor accessor, SpanNamer spanNamer) {
|
||||
public TraceWebAspect(Tracer tracer, SpanNamer spanNamer) {
|
||||
this.tracer = tracer;
|
||||
this.accessor = accessor;
|
||||
this.spanNamer = spanNamer;
|
||||
}
|
||||
|
||||
@@ -109,9 +106,9 @@ public class TraceWebAspect {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
|
||||
Callable<Object> callable = (Callable<Object>) pjp.proceed();
|
||||
if (this.accessor.isTracing()) {
|
||||
if (this.tracer.isTracing()) {
|
||||
log.debug("Wrapping callable with span ["
|
||||
+ this.accessor.getCurrentSpan() + "]");
|
||||
+ this.tracer.getCurrentSpan() + "]");
|
||||
return new TraceContinuingCallable<>(this.tracer, this.spanNamer, callable);
|
||||
}
|
||||
else {
|
||||
@@ -122,10 +119,10 @@ public class TraceWebAspect {
|
||||
@Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()")
|
||||
public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
|
||||
final WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) pjp.proceed();
|
||||
if (this.accessor.isTracing()) {
|
||||
if (this.tracer.isTracing()) {
|
||||
try {
|
||||
log.debug("Wrapping callable with span ["
|
||||
+ this.accessor.getCurrentSpan() + "]");
|
||||
+ this.tracer.getCurrentSpan() + "]");
|
||||
Field callableField = WebAsyncTask.class.getDeclaredField("callable");
|
||||
callableField.setAccessible(true);
|
||||
callableField.set(webAsyncTask, new TraceContinuingCallable<>(this.tracer,
|
||||
|
||||
@@ -18,7 +18,10 @@ package org.springframework.cloud.sleuth.instrument.web;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -29,7 +32,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
@@ -64,26 +68,36 @@ public class TraceWebAutoConfiguration {
|
||||
@Value("${spring.sleuth.web.skipPattern:}")
|
||||
private String skipPattern;
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
@Autowired
|
||||
private SpanAccessor accessor;
|
||||
|
||||
@Autowired
|
||||
private TraceKeys traceKeys;
|
||||
|
||||
@Bean
|
||||
public TraceWebAspect traceWebAspect(SpanNamer spanNamer) {
|
||||
return new TraceWebAspect(this.tracer, this.accessor, spanNamer);
|
||||
public TraceWebAspect traceWebAspect(Tracer tracer, SpanNamer spanNamer) {
|
||||
return new TraceWebAspect(tracer, spanNamer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceFilter traceFilter(Random random,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter) {
|
||||
return new TraceFilter(this.tracer, this.traceKeys, skipPatternProvider.skipPattern(), random,
|
||||
spanReporter);
|
||||
public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
@Qualifier("httpServletRequestSpanExtractor") SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
@Qualifier("httpServletResponseInjector") SpanInjector<HttpServletResponse> spanInjector) {
|
||||
return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(),
|
||||
spanReporter, spanExtractor, spanInjector);
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("httpServletRequestSpanExtractor")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.extractor.enabled", matchIfMissing = true)
|
||||
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor(Random random,
|
||||
SkipPatternProvider skipPatternProvider) {
|
||||
return new HttpServletRequestExtractor(random, skipPatternProvider.skipPattern());
|
||||
}
|
||||
|
||||
// TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work
|
||||
@Bean
|
||||
@Qualifier("httpServletResponseInjector")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.injector.enabled", matchIfMissing = true)
|
||||
public SpanInjector<HttpServletResponse> httpServletResponseInjector() {
|
||||
return new HttpServletResponseInjector();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -19,9 +19,9 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstraction over classes that interact with Http requests. Allows you
|
||||
@@ -34,38 +34,12 @@ import org.springframework.util.StringUtils;
|
||||
abstract class AbstractTraceHttpRequestInterceptor {
|
||||
|
||||
protected final Tracer tracer;
|
||||
protected final SpanInjector<HttpRequest> spanInjector;
|
||||
|
||||
protected AbstractTraceHttpRequestInterceptor(Tracer tracer) {
|
||||
protected AbstractTraceHttpRequestInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
|
||||
private void enrichWithTraceHeaders(HttpRequest request, Span span) {
|
||||
setIdHeader(request, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setIdHeader(request, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(request, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
setHeader(request, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(request, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(request, 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) &&
|
||||
this.tracer.isTracing()) {
|
||||
request.getHeaders().add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIdHeader(HttpRequest request, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(request, name, Span.idToHex(value));
|
||||
}
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +50,7 @@ abstract class AbstractTraceHttpRequestInterceptor {
|
||||
URI uri = request.getURI();
|
||||
String spanName = uriScheme(uri) + ":" + uri.getPath();
|
||||
Span newSpan = this.tracer.createSpan(spanName);
|
||||
enrichWithTraceHeaders(request, newSpan);
|
||||
this.spanInjector.inject(newSpan, request);
|
||||
newSpan.logEvent(Span.CLIENT_SEND);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +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.Tracer;
|
||||
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;
|
||||
@@ -53,9 +55,9 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
*
|
||||
* @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory)
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate) {
|
||||
super(tracer);
|
||||
super(tracer, spanInjector);
|
||||
this.asyncDelegate = asyncDelegate;
|
||||
this.syncDelegate = asyncDelegate instanceof ClientHttpRequestFactory ?
|
||||
(ClientHttpRequestFactory) asyncDelegate : defaultClientHttpRequestFactory();
|
||||
@@ -65,16 +67,16 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
* Default implementation that creates a {@link SimpleClientHttpRequestFactory} that
|
||||
* has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor}
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer) {
|
||||
super(tracer);
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector) {
|
||||
super(tracer, spanInjector);
|
||||
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory();
|
||||
this.asyncDelegate = simpleClientHttpRequestFactory;
|
||||
this.syncDelegate = simpleClientHttpRequestFactory;
|
||||
}
|
||||
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate, ClientHttpRequestFactory syncDelegate) {
|
||||
super(tracer);
|
||||
super(tracer, spanInjector);
|
||||
this.asyncDelegate = asyncDelegate;
|
||||
this.syncDelegate = syncDelegate;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
@@ -38,8 +39,8 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||
public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterceptor
|
||||
implements ClientHttpRequestInterceptor {
|
||||
|
||||
public TraceRestTemplateInterceptor(Tracer tracer) {
|
||||
super(tracer);
|
||||
public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector<HttpRequest> spanInjector) {
|
||||
super(tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,10 +22,12 @@ 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.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
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.http.HttpRequest;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
|
||||
@@ -47,8 +49,9 @@ public class TraceWebAsyncClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(Tracer tracer) {
|
||||
return new TraceAsyncClientHttpRequestFactoryWrapper(tracer);
|
||||
public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector) {
|
||||
return new TraceAsyncClientHttpRequestFactoryWrapper(tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -28,10 +28,12 @@ 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.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
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;
|
||||
|
||||
@@ -52,8 +54,9 @@ public class TraceWebClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer) {
|
||||
return new TraceRestTemplateInterceptor(tracer);
|
||||
public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector) {
|
||||
return new TraceRestTemplateInterceptor(tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -62,6 +65,11 @@ public class TraceWebClientAutoConfiguration {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector httpRequestInjector() {
|
||||
return new HttpRequestInjector();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TraceInterceptorConfiguration {
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Abstract class for publishing logging the client received event
|
||||
* Abstract class for logging the client received event
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
|
||||
@@ -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.client.feign;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link RequestTemplate}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class FeignRequestTemplateInjector implements SpanInjector<RequestTemplate> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, RequestTemplate carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
carrier.header(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(carrier, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
if (!span.isExportable()) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
Long parentId = getParentId(span);
|
||||
if (parentId != null) {
|
||||
setHeader(carrier, Span.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
}
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
protected void setHeader(RequestTemplate request, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !request.headers().containsKey(name)) {
|
||||
request.header(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mutable holder for Feign Response headers
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class FeignResponseHeadersHolder {
|
||||
final Map<String, Collection<String>> responseHeaders;
|
||||
|
||||
FeignResponseHeadersHolder(Map<String, Collection<String>> responseHeaders) {
|
||||
this.responseHeaders = responseHeaders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to
|
||||
* {@link FeignResponseHeadersHolder#responseHeaders}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class FeignResponseHeadersInjector implements SpanInjector<FeignResponseHeadersHolder> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, FeignResponseHeadersHolder carrier) {
|
||||
Map<String, Collection<String>> headers = carrier.responseHeaders;
|
||||
headersWithTraceId(span, headers);
|
||||
}
|
||||
|
||||
private Map<String, Collection<String>> headersWithTraceId(Span span,
|
||||
Map<String, Collection<String>> headers) {
|
||||
Map<String, Collection<String>> newHeaders = new HashMap<>();
|
||||
newHeaders.putAll(headers);
|
||||
if (span == null) {
|
||||
setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
return newHeaders;
|
||||
}
|
||||
setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(newHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
return newHeaders;
|
||||
}
|
||||
|
||||
void setHeader(Map<String, Collection<String>> headers, String name,
|
||||
String value) {
|
||||
if (StringUtils.hasText(value) && !headers.containsKey(name)) {
|
||||
headers.put(name, singletonList(value));
|
||||
}
|
||||
}
|
||||
|
||||
void setHeader(Map<String, Collection<String>> headers, String name,
|
||||
Long value) {
|
||||
if (value != null) {
|
||||
setHeader(headers, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,8 @@ final class TraceFeignClient extends FeignEventPublisher implements Client {
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
Response response = this.delegate.execute(request, options);
|
||||
if (response.body() == null || (response.body() != null && Objects.equals(response.body().length(), 0))) {
|
||||
if (response.body() == null || (response.body() != null
|
||||
&& Objects.equals(response.body().length(), 0))) {
|
||||
finish();
|
||||
}
|
||||
return response;
|
||||
|
||||
@@ -18,9 +18,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -32,7 +29,7 @@ import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration;
|
||||
@@ -40,7 +37,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
@@ -48,11 +44,10 @@ import feign.Client;
|
||||
import feign.Feign;
|
||||
import feign.FeignException;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import feign.codec.Decoder;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
* enables span information propagation when using Feign.
|
||||
@@ -71,34 +66,33 @@ public class TraceFeignClientAutoConfiguration {
|
||||
@Autowired
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
private final FeignRequestContext feignRequestContext = FeignRequestContext.getInstance();
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnClass(HystrixCommand.class)
|
||||
@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
|
||||
public Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) {
|
||||
Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) {
|
||||
return SleuthFeignBuilder.builder(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.sleuth.feign.processor.enabled", matchIfMissing = true)
|
||||
public FeignBeanPostProcessor feignBeanPostProcessor(Tracer tracer) {
|
||||
FeignBeanPostProcessor feignBeanPostProcessor(Tracer tracer) {
|
||||
return new FeignBeanPostProcessor(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public Decoder feignDecoder(final Tracer tracer) {
|
||||
Decoder feignDecoder(final Tracer tracer) {
|
||||
return new TraceFeignDecoder(tracer, new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)) {
|
||||
@Override
|
||||
public Object decode(Response response, Type type)
|
||||
throws IOException, FeignException {
|
||||
FeignRequestContext feignRequestContext = FeignRequestContext.getInstance();
|
||||
FeignResponseHeadersHolder feignResponseHeadersHolder =
|
||||
new FeignResponseHeadersHolder(response.headers());
|
||||
feignResponseHeadersInjector().inject(feignRequestContext.getCurrentSpan(), feignResponseHeadersHolder);
|
||||
return super.decode(Response.create(response.status(),
|
||||
response.reason(), headersWithTraceId(response.headers()),
|
||||
response.reason(), feignResponseHeadersHolder.responseHeaders,
|
||||
response.body()), type);
|
||||
}
|
||||
});
|
||||
@@ -110,36 +104,14 @@ public class TraceFeignClientAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
public RequestInterceptor traceIdRequestInterceptor(Tracer tracer) {
|
||||
return new TraceFeignRequestInterceptor(tracer);
|
||||
return new TraceFeignRequestInterceptor(tracer, feignRequestTemplateInjector());
|
||||
}
|
||||
|
||||
private Map<String, Collection<String>> headersWithTraceId(
|
||||
Map<String, Collection<String>> headers) {
|
||||
Map<String, Collection<String>> newHeaders = new HashMap<>();
|
||||
newHeaders.putAll(headers);
|
||||
Span span = this.feignRequestContext.getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
return newHeaders;
|
||||
}
|
||||
setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(newHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
return newHeaders;
|
||||
private SpanInjector<RequestTemplate> feignRequestTemplateInjector() {
|
||||
return new FeignRequestTemplateInjector();
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, Collection<String>> headers, String name,
|
||||
String value) {
|
||||
if (StringUtils.hasText(value) && !headers.containsKey(name)
|
||||
&& this.tracer.isTracing()) {
|
||||
headers.put(name, singletonList(value));
|
||||
}
|
||||
private SpanInjector<FeignResponseHeadersHolder> feignResponseHeadersInjector() {
|
||||
return new FeignResponseHeadersInjector();
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, Collection<String>> headers, String name,
|
||||
Long value) {
|
||||
if (value != null) {
|
||||
setHeader(headers, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
@@ -36,31 +36,20 @@ import feign.RequestTemplate;
|
||||
final class TraceFeignRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<RequestTemplate> spanInjector;
|
||||
private final FeignRequestContext feignRequestContext = FeignRequestContext.getInstance();
|
||||
|
||||
TraceFeignRequestInterceptor(Tracer tracer) {
|
||||
TraceFeignRequestInterceptor(Tracer tracer,
|
||||
SpanInjector<RequestTemplate> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
String spanName = getSpanName(template);
|
||||
Span span = getSpan(spanName);
|
||||
if (span == null) {
|
||||
setHeader(template, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
template.header(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(template, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(template, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
if (!span.isExportable()) {
|
||||
setHeader(template, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
Long parentId = getParentId(span);
|
||||
if (parentId != null) {
|
||||
setHeader(template, Span.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
}
|
||||
setHeader(template, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
this.spanInjector.inject(span, template);
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
}
|
||||
|
||||
@@ -90,15 +79,4 @@ final class TraceFeignRequestInterceptor implements RequestInterceptor {
|
||||
return uri.getScheme() == null ? "http" : uri.getScheme();
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
protected void setHeader(RequestTemplate request, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !request.headers().containsKey(name)
|
||||
&& this.tracer.isTracing()) {
|
||||
request.header(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpRequest.Builder;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link Builder}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class RequestBuilderContextInjector implements SpanInjector<Builder> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, Builder carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
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());
|
||||
if (getParentId(span) != null) {
|
||||
setHeader(carrier, Span.PARENT_ID_NAME,
|
||||
Span.idToHex(getParentId(span)));
|
||||
}
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME,
|
||||
span.getProcessId());
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty()
|
||||
? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(HttpRequest.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* 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.NOT_SAMPLED_NAME, "true");
|
||||
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());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -36,10 +33,12 @@ import com.netflix.zuul.context.RequestContext;
|
||||
*/
|
||||
public class TracePreZuulFilter extends ZuulFilter {
|
||||
|
||||
private final SpanAccessor accessor;
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<RequestContext> spanInjector;
|
||||
|
||||
public TracePreZuulFilter(SpanAccessor accessor) {
|
||||
this.accessor = accessor;
|
||||
public TracePreZuulFilter(Tracer tracer, SpanInjector<RequestContext> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,49 +49,17 @@ public class TracePreZuulFilter extends ZuulFilter {
|
||||
@Override
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
Map<String, String> requestHeaders = ctx.getZuulRequestHeaders();
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
setHeader(requestHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
setHeader(requestHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(requestHeaders, Span.SPAN_NAME_NAME, span.getName());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
setHeader(requestHeaders, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(requestHeaders, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
// TODO: the client sent event should come from the client not the filter!
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ReflectionUtils.rethrowRuntimeException(ex);
|
||||
}
|
||||
this.spanInjector.inject(span, ctx);
|
||||
// TODO: the client sent event should come from the client not the filter!
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Span getCurrentSpan() {
|
||||
return this.accessor.getCurrentSpan();
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
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) && this.accessor.isTracing()) {
|
||||
request.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, String> request, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(request, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
|
||||
@@ -26,7 +26,8 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
@@ -43,12 +44,14 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceRestClientRibbonCommandFactory.class);
|
||||
|
||||
private final SpanAccessor accessor;
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpRequest.Builder> spanInjector;
|
||||
|
||||
public TraceRestClientRibbonCommandFactory(SpringClientFactory clientFactory,
|
||||
SpanAccessor accessor) {
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector) {
|
||||
super(clientFactory);
|
||||
this.accessor = accessor;
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +63,7 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
|
||||
return new TraceRestClientRibbonCommand(context.getServiceId(), restClient,
|
||||
getVerb(context.getVerb()), context.getUri(), context.getRetryable(),
|
||||
context.getHeaders(), context.getParams(), context.getRequestEntity(),
|
||||
this.accessor);
|
||||
this.tracer, this.spanInjector);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
log.error("Exception occurred while trying to create the TraceRestClientRibbonCommand", e);
|
||||
@@ -70,52 +73,31 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
|
||||
|
||||
class TraceRestClientRibbonCommand extends RestClientRibbonCommand {
|
||||
|
||||
private final SpanAccessor accessor;
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpRequest.Builder> spanInjector;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public TraceRestClientRibbonCommand(String commandKey, RestClient restClient,
|
||||
HttpRequest.Verb verb, String uri, Boolean retryable,
|
||||
MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity,
|
||||
SpanAccessor accessor)
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector)
|
||||
throws URISyntaxException {
|
||||
super(commandKey, restClient, verb, uri, retryable, headers, params,
|
||||
requestEntity);
|
||||
this.accessor = accessor;
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeRequest(HttpRequest.Builder requestBuilder) {
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(requestBuilder, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
setHeader(requestBuilder, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(requestBuilder, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
setHeader(requestBuilder, Span.SPAN_NAME_NAME, span.getName());
|
||||
if (getParentId(span) != null) {
|
||||
setHeader(requestBuilder, Span.PARENT_ID_NAME,
|
||||
Span.idToHex(getParentId(span)));
|
||||
}
|
||||
setHeader(requestBuilder, Span.PROCESS_ID_NAME,
|
||||
span.getProcessId());
|
||||
this.spanInjector.inject(span, requestBuilder);
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty()
|
||||
? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(HttpRequest.Builder builder, String name, String value) {
|
||||
if (value != null && this.accessor.isTracing()) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Span getCurrentSpan() {
|
||||
return this.accessor.getCurrentSpan();
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,12 +23,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
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 com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
@@ -48,13 +51,14 @@ public class TraceZuulAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TracePreZuulFilter tracePreZuulFilter(SpanAccessor accessor) {
|
||||
return new TracePreZuulFilter(accessor);
|
||||
public TracePreZuulFilter tracePreZuulFilter(Tracer tracer, SpanInjector<RequestContext> spanInjector) {
|
||||
return new TracePreZuulFilter(tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory, SpanAccessor accessor) {
|
||||
return new TraceRestClientRibbonCommandFactory(factory, accessor);
|
||||
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory,
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector) {
|
||||
return new TraceRestClientRibbonCommandFactory(factory, tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -63,4 +67,14 @@ public class TraceZuulAutoConfiguration {
|
||||
return new TracePostZuulFilter(accessor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanInjector<RequestContext> requestContextInjector() {
|
||||
return new RequestContextInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanInjector<HttpRequest.Builder> requestBuilderContextInjector() {
|
||||
return new RequestBuilderContextInjector();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,9 +48,8 @@ public class DefaultTracer implements Tracer {
|
||||
|
||||
private final SpanReporter spanReporter;
|
||||
|
||||
public DefaultTracer(Sampler defaultSampler, Random random,
|
||||
SpanNamer spanNamer, SpanLogger spanLogger,
|
||||
SpanReporter spanReporter) {
|
||||
public DefaultTracer(Sampler defaultSampler, Random random, SpanNamer spanNamer,
|
||||
SpanLogger spanLogger, SpanReporter spanReporter) {
|
||||
this.defaultSampler = defaultSampler;
|
||||
this.random = random;
|
||||
this.spanNamer = spanNamer;
|
||||
@@ -227,5 +226,4 @@ public class DefaultTracer implements Tracer {
|
||||
}
|
||||
return runnable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -154,24 +154,24 @@ public class SpringCloudSleuthDocTests {
|
||||
assertThat(initialSpan.logs()).extracting("event").doesNotContain("taxCalculated");
|
||||
|
||||
executorService.submit(() -> {
|
||||
// tag::manual_span_continuation[]
|
||||
// let's assume that we're in a thread Y and we've received
|
||||
// the `initialSpan` from thread X
|
||||
Span continuedSpan = this.tracer.continueSpan(initialSpan);
|
||||
try {
|
||||
// ...
|
||||
// You can tag a span
|
||||
this.tracer.addTag("taxValue", taxValue);
|
||||
// ...
|
||||
// You can log an event on a span
|
||||
continuedSpan.logEvent("taxCalculated");
|
||||
} finally {
|
||||
// Once done remember to detach the span. That way you'll
|
||||
// safely remove it from the current thread without closing it
|
||||
this.tracer.detach(continuedSpan);
|
||||
}
|
||||
// end::manual_span_continuation[]
|
||||
}
|
||||
// tag::manual_span_continuation[]
|
||||
// let's assume that we're in a thread Y and we've received
|
||||
// the `initialSpan` from thread X
|
||||
Span continuedSpan = this.tracer.continueSpan(initialSpan);
|
||||
try {
|
||||
// ...
|
||||
// You can tag a span
|
||||
this.tracer.addTag("taxValue", taxValue);
|
||||
// ...
|
||||
// You can log an event on a span
|
||||
continuedSpan.logEvent("taxCalculated");
|
||||
} finally {
|
||||
// Once done remember to detach the span. That way you'll
|
||||
// safely remove it from the current thread without closing it
|
||||
this.tracer.detach(continuedSpan);
|
||||
}
|
||||
// end::manual_span_continuation[]
|
||||
}
|
||||
).get();
|
||||
|
||||
this.tracer.close(initialSpan);
|
||||
@@ -191,26 +191,26 @@ public class SpringCloudSleuthDocTests {
|
||||
assertThat(initialSpan.logs()).extracting("event").doesNotContain("commissionCalculated");
|
||||
|
||||
executorService.submit(() -> {
|
||||
// tag::manual_span_joining[]
|
||||
// let's assume that we're in a thread Y and we've received
|
||||
// the `initialSpan` from thread X. `initialSpan` will be the parent
|
||||
// of the `newSpan`
|
||||
Span newSpan = this.tracer.createSpan("calculateCommission", initialSpan);
|
||||
try {
|
||||
// ...
|
||||
// You can tag a span
|
||||
this.tracer.addTag("commissionValue", commissionValue);
|
||||
// ...
|
||||
// You can log an event on a span
|
||||
newSpan.logEvent("commissionCalculated");
|
||||
} finally {
|
||||
// Once done remember to close the span. This will allow collecting
|
||||
// the span to send it to Zipkin. The tags and events set on the
|
||||
// newSpan will not be present on the parent
|
||||
this.tracer.close(newSpan);
|
||||
}
|
||||
// end::manual_span_joining[]
|
||||
}
|
||||
// tag::manual_span_joining[]
|
||||
// let's assume that we're in a thread Y and we've received
|
||||
// the `initialSpan` from thread X. `initialSpan` will be the parent
|
||||
// of the `newSpan`
|
||||
Span newSpan = this.tracer.createSpan("calculateCommission", initialSpan);
|
||||
try {
|
||||
// ...
|
||||
// You can tag a span
|
||||
this.tracer.addTag("commissionValue", commissionValue);
|
||||
// ...
|
||||
// You can log an event on a span
|
||||
newSpan.logEvent("commissionCalculated");
|
||||
} finally {
|
||||
// Once done remember to close the span. This will allow collecting
|
||||
// the span to send it to Zipkin. The tags and events set on the
|
||||
// newSpan will not be present on the parent
|
||||
this.tracer.close(newSpan);
|
||||
}
|
||||
// end::manual_span_joining[]
|
||||
}
|
||||
).get();
|
||||
|
||||
this.tracer.close(initialSpan);
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
@@ -16,6 +17,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnableAutoConfiguration(exclude = { LoadBalancerAutoConfiguration.class,
|
||||
JmxAutoConfiguration.class, TraceSpringIntegrationAutoConfiguration.class,
|
||||
TraceWebSocketAutoConfiguration.class,
|
||||
LoadBalancerAutoConfiguration.class })
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@Configuration
|
||||
|
||||
@@ -32,26 +32,30 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.asser
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SpanMessageHeadersTests {
|
||||
public class MessagingSpanInjectorTests {
|
||||
|
||||
private TraceKeys traceKeys = new TraceKeys();
|
||||
private MessagingSpanInjector messagingSpanInjector = new MessagingSpanInjector(this.traceKeys);
|
||||
|
||||
@Test
|
||||
public void spanHeadersAdded() {
|
||||
Span span = Span.builder().name("http:foo").spanId(1L).traceId(2L).build();
|
||||
Message<?> message = new GenericMessage<>("Hello World");
|
||||
message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span);
|
||||
assertThat(message.getHeaders()).containsKey(Span.SPAN_ID_NAME);
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message);
|
||||
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
|
||||
assertThat(messageBuilder.build().getHeaders()).containsKey(Span.SPAN_ID_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotOverrideSpanTags() {
|
||||
Span span = spanWithStringPayloadType();
|
||||
Message<?> message = messageWithIntegerPayloadType();
|
||||
MessageBuilder messageBuilder = messageWithIntegerPayloadType();
|
||||
|
||||
message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span);
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
|
||||
assertThat(message.getHeaders())
|
||||
assertThat(messageBuilder.build().getHeaders())
|
||||
.containsKeys(Span.SPAN_ID_NAME, "message/payload-type");
|
||||
assertThat(span).hasATag("message/payload-type", "java.lang.String");
|
||||
}
|
||||
@@ -62,18 +66,22 @@ public class SpanMessageHeadersTests {
|
||||
return span;
|
||||
}
|
||||
|
||||
private Message<?> messageWithIntegerPayloadType() {
|
||||
private MessageBuilder messageWithIntegerPayloadType() {
|
||||
MessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
|
||||
accessor.setHeader("message/payload-type", "java.lang.Integer");
|
||||
return MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders());
|
||||
return MessageBuilder.withPayload("Hello World").setHeaders(accessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeSpanHeadersAdded() {
|
||||
Span span = Span.builder().name("http:foo").spanId(1L).traceId(2L).build();
|
||||
MessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
|
||||
Message<?> message = MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders());
|
||||
message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span);
|
||||
Message messageToBuild = MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders());
|
||||
MessageBuilder<String> messageBuilder = MessageBuilder.fromMessage(messageToBuild);
|
||||
|
||||
this.messagingSpanInjector.inject(span, messageBuilder);
|
||||
|
||||
Message<String> message = messageBuilder.build();
|
||||
assertThat(message.getHeaders())
|
||||
.containsKey(NativeMessageHeaderAccessor.NATIVE_HEADERS);
|
||||
MessageHeaderAccessor natives = NativeMessageHeaderAccessor
|
||||
@@ -76,7 +76,7 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys,
|
||||
new NoOpSpanReporter()));
|
||||
new NoOpSpanReporter(), this.spanExtractor, this.spanInjector));
|
||||
}
|
||||
|
||||
private MvcResult whenSentPingWithTraceIdAndNotSampling(Long traceId)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -70,7 +71,9 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new HttpServletResponseInjector());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
@@ -80,7 +83,9 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
Random generator = new Random();
|
||||
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());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new HttpServletResponseInjector());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -24,9 +28,11 @@ import org.mockito.Mock;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.log.SpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
@@ -55,6 +61,9 @@ public class TraceFilterTests {
|
||||
|
||||
@Mock SpanLogger spanLogger;
|
||||
@Mock SpanReporter spanReporter;
|
||||
SpanExtractor<HttpServletRequest> spanExtractor = new HttpServletRequestExtractor(new Random(), Pattern
|
||||
.compile(TraceFilter.DEFAULT_SKIP_PATTERN));
|
||||
SpanInjector<HttpServletResponse> spanInjector = new HttpServletResponseInjector();
|
||||
|
||||
private Tracer tracer;
|
||||
private TraceKeys traceKeys = new TraceKeys();
|
||||
@@ -91,7 +100,8 @@ public class TraceFilterTests {
|
||||
@Test
|
||||
public void notTraced() throws Exception {
|
||||
this.sampler = NeverSampler.INSTANCE;
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
|
||||
this.request = get("/favicon.ico").accept(MediaType.ALL)
|
||||
.buildRequest(new MockServletContext());
|
||||
@@ -104,7 +114,8 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
verifyHttpTags();
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
@@ -117,7 +128,8 @@ public class TraceFilterTests {
|
||||
.header(Span.TRACE_ID_NAME, Span.idToHex(2L))
|
||||
.header(Span.PARENT_ID_NAME, Span.idToHex(3L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -133,7 +145,8 @@ public class TraceFilterTests {
|
||||
// It should have been removed from the thread local context so simulate that
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
verifyHttpTags();
|
||||
@@ -146,7 +159,8 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, 10L)
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
verifyHttpTags();
|
||||
@@ -160,7 +174,8 @@ public class TraceFilterTests {
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -175,7 +190,8 @@ public class TraceFilterTests {
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
this.request.addHeader("X-Foo", "spam");
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -187,7 +203,8 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void catchesException() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter);
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.filterChain = new MockFilterChain() {
|
||||
@Override
|
||||
public void doFilter(javax.servlet.ServletRequest request,
|
||||
|
||||
@@ -59,7 +59,7 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer)));
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector())));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer)));
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector())));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.common;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.util.ExceptionUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@@ -23,9 +30,12 @@ import org.springframework.web.context.WebApplicationContext;
|
||||
@WebAppConfiguration
|
||||
public abstract class AbstractMvcIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
protected WebApplicationContext webApplicationContext;
|
||||
@Autowired protected WebApplicationContext webApplicationContext;
|
||||
protected MockMvc mockMvc;
|
||||
@Autowired protected Tracer tracer;
|
||||
@Autowired protected TraceKeys traceKeys;
|
||||
@Autowired protected SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
@Autowired protected SpanInjector<HttpServletResponse> spanInjector;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
@@ -3,10 +3,7 @@ package org.springframework.cloud.sleuth.instrument.web.common;
|
||||
import org.junit.Before;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.sleuth.NoOpSpanReporter;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceFilter;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
|
||||
@@ -30,8 +27,7 @@ public abstract class AbstractMvcWiremockIntegrationTest extends AbstractMvcInte
|
||||
|
||||
protected WireMock wireMock;
|
||||
@Autowired protected HttpMockServer httpMockServer;
|
||||
@Autowired protected Tracer tracer;
|
||||
@Autowired protected TraceKeys traceKeys;
|
||||
|
||||
|
||||
@Override
|
||||
@Before
|
||||
@@ -56,6 +52,6 @@ public abstract class AbstractMvcWiremockIntegrationTest extends AbstractMvcInte
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys,
|
||||
new NoOpSpanReporter()));
|
||||
new NoOpSpanReporter(), this.spanExtractor, this.spanInjector));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired TraceFilter traceFilter;
|
||||
@Autowired ArrayListSpanAccumulator arrayListSpanAccumulator;
|
||||
@Autowired SpanReporter spanReporter;
|
||||
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys,
|
||||
this.spanReporter));
|
||||
mockMvcBuilder.addFilters(this.traceFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -46,7 +46,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);
|
||||
private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new RequestContextInjector());
|
||||
|
||||
@After
|
||||
@Before
|
||||
|
||||
@@ -25,7 +25,8 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
@@ -41,15 +42,16 @@ import static org.mockito.Matchers.anyString;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceRestClientRibbonCommandFactoryTest {
|
||||
|
||||
@Mock SpanAccessor accessor;
|
||||
@Mock Tracer tracer;
|
||||
@Mock SpringClientFactory springClientFactory;
|
||||
SpanInjector<HttpRequest.Builder> spanInjector = new RequestBuilderContextInjector();
|
||||
TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({"deprecation", "unchecked"})
|
||||
public void setup() {
|
||||
this.traceRestClientRibbonCommandFactory = new TraceRestClientRibbonCommandFactory(
|
||||
this.springClientFactory, this.accessor);
|
||||
this.springClientFactory, this.tracer, this.spanInjector);
|
||||
given(this.springClientFactory.getClient(anyString(), any(Class.class))).willReturn(new RestClient());
|
||||
Span span = Span.builder()
|
||||
.name("name")
|
||||
@@ -58,13 +60,14 @@ public class TraceRestClientRibbonCommandFactoryTest {
|
||||
.parent(3L)
|
||||
.processId("processId")
|
||||
.build();
|
||||
given(this.accessor.getCurrentSpan()).willReturn(span);
|
||||
given(this.accessor.isTracing()).willReturn(true);
|
||||
given(this.tracer.getCurrentSpan()).willReturn(span);
|
||||
given(this.tracer.isTracing()).willReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_wrap_ribbon_command_in_a_sleuth_representation() throws Exception {
|
||||
RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext());
|
||||
RestClientRibbonCommand restClientRibbonCommand =
|
||||
this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext());
|
||||
|
||||
then(restClientRibbonCommand)
|
||||
.isInstanceOf(TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand.class);
|
||||
@@ -73,7 +76,8 @@ public class TraceRestClientRibbonCommandFactoryTest {
|
||||
@Test
|
||||
public void should_attach_trace_headers_to_the_sent_request() throws Exception {
|
||||
RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext());
|
||||
TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand traceRestClientRibbonCommand = (TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand) restClientRibbonCommand;
|
||||
TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand traceRestClientRibbonCommand =
|
||||
(TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand) restClientRibbonCommand;
|
||||
HttpRequest.Builder builder = new HttpRequest.Builder();
|
||||
|
||||
traceRestClientRibbonCommand.customizeRequest(builder);
|
||||
|
||||
Reference in New Issue
Block a user