Customizable headers
* Fixed the HttpServlet extractors
(now the response can contain custom headers)
* Changed header names to be Zipkin compatible
* removed qualifiers and properties
* updated the docs
fixes #19
This commit is contained in:
@@ -184,7 +184,7 @@ IMPORTANT: If using Zipkin or Stream, configure the percentage of spans exported
|
||||
|
||||
NOTE: the SLF4J MDC is always set and logback users will immediately see the trace and span ids in logs per the example
|
||||
above. Other logging systems have to configure their own formatter to get the same result. The default is
|
||||
`logging.pattern.level` set to `%clr(%5p) %clr([${spring.application.name:},%X{X-Trace-Id:-},%X{X-Span-Id:-},%X{X-Span-Export:-}]){yellow}`
|
||||
`logging.pattern.level` set to `%clr(%5p) %clr([${spring.application.name:},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow}`
|
||||
(this is a Spring Boot feature for logback users).
|
||||
*This means that if you're not using SLF4J this pattern WILL NOT be automatically applied*.
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ latency in your applications. Sleuth is written to not log too much, and to not
|
||||
* Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints,
|
||||
rest template, scheduled actions, message channels, zuul filters, feign client).
|
||||
|
||||
* Sleuth includes default logic to join a trace across http or messaging boundaries. For example, http propagation
|
||||
works via Zipkin-compatible request headers. This propagation logic is defined and customized via
|
||||
`SpanInjector` and `SpanExtractor` implementations.
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
@@ -45,6 +49,6 @@ IMPORTANT: If using Zipkin or Stream, configure the percentage of spans exported
|
||||
|
||||
NOTE: the SLF4J MDC is always set and logback users will immediately see the trace and span ids in logs per the example
|
||||
above. Other logging systems have to configure their own formatter to get the same result. The default is
|
||||
`logging.pattern.level` set to `%clr(%5p) %clr([${spring.application.name:},%X{X-Trace-Id:-},%X{X-Span-Id:-},%X{X-Span-Export:-}]){yellow}`
|
||||
`logging.pattern.level` set to `%clr(%5p) %clr([${spring.application.name:},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow}`
|
||||
(this is a Spring Boot feature for logback users).
|
||||
*This means that if you're not using SLF4J this pattern WILL NOT be automatically applied*.
|
||||
@@ -191,7 +191,92 @@ adding a Channel Binder implementation
|
||||
(e.g. `spring-cloud-starter-stream-rabbit` for RabbitMQ or
|
||||
`spring-cloud-starter-stream-kafka` for Kafka). This will
|
||||
automatically turn your app into a producer of messages with payload
|
||||
type `Spans`.
|
||||
type `Spans`.
|
||||
|
||||
== Customizations
|
||||
|
||||
Thanks to the `SpanInjector` and `SpanExtractor` you can customize the way spans
|
||||
are created and propagated.
|
||||
|
||||
There are currently two built-in ways to pass tracing information between processes:
|
||||
|
||||
* via Spring Integration
|
||||
* via HTTP
|
||||
|
||||
Span ids are extracted from Zipkin-compatible (B3) headers (either `Message`
|
||||
or HTTP headers), to start or join an existing trace. Trace information is
|
||||
injected into any outbound requests so the next hop can extract them.
|
||||
|
||||
=== Spring Integration
|
||||
|
||||
For Spring Integration these are the beans responsible for creation of a Span from a `Message`
|
||||
and filling in the `MessageBuilder` with tracing information.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SpanExtractor<Message> messagingSpanExtractor() {
|
||||
...
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<MessageBuilder> messagingSpanInjector() {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
You can override them by providing your own implementation and by adding a `@Primary` annotation
|
||||
to your bean definition.
|
||||
|
||||
=== HTTP
|
||||
|
||||
For HTTP these are the beans responsible for creation of a Span from a `HttpServletRequest`
|
||||
and filling in the `HttpServletResponse` with tracing information.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor() {
|
||||
...
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<HttpServletResponse> httpServletResponseSpanInjector() {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
You can override them by providing your own implementation and by adding a `@Primary` annotation
|
||||
to your bean definition.
|
||||
|
||||
=== Example
|
||||
|
||||
Let's assume that instead of the standard Zipkin compatible tracing HTTP header names
|
||||
you have
|
||||
|
||||
* for trace id - `correlationId`
|
||||
* for span id - `mySpanId`
|
||||
|
||||
This is a an example of a `SpanExtractor`
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=extractor,indent=0]
|
||||
----
|
||||
|
||||
The following `SpanInjector` could be created
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=injector,indent=0]
|
||||
----
|
||||
|
||||
And you could register them like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
=== Zipkin Consumer
|
||||
|
||||
|
||||
@@ -45,6 +45,15 @@ import org.springframework.util.StringUtils;
|
||||
* <li><b>cr</b> - Client Received</li>
|
||||
* </ul>
|
||||
*
|
||||
* Spring Cloud Sleuth uses Zipkin compatible header names
|
||||
*
|
||||
* <ul>
|
||||
* <li>X-B3-TraceId: 64 encoded bits</li>
|
||||
* <li>X-B3-SpanId: 64 encoded bits</li>
|
||||
* <li>X-B3-ParentSpanId: 64 encoded bits</li>
|
||||
* <li>X-B3-Sampled: Boolean (either “1” or “0”)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.0
|
||||
@@ -56,14 +65,17 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class Span {
|
||||
|
||||
public static final String NOT_SAMPLED_NAME = "X-Not-Sampled";
|
||||
public static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
public static final String PROCESS_ID_NAME = "X-Process-Id";
|
||||
public static final String PARENT_ID_NAME = "X-Parent-Id";
|
||||
public static final String TRACE_ID_NAME = "X-Trace-Id";
|
||||
public static final String PARENT_ID_NAME = "X-B3-ParentSpanId";
|
||||
public static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
public static final String SPAN_NAME_NAME = "X-Span-Name";
|
||||
public static final String SPAN_ID_NAME = "X-Span-Id";
|
||||
public static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
|
||||
|
||||
public static final String SPAN_SAMPLED = "1";
|
||||
public static final String SPAN_NOT_SAMPLED = "0";
|
||||
|
||||
public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc";
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
// This doesn't work with all logging systems but it's a useful default so you see
|
||||
// traces in logs without having to configure it.
|
||||
map.put("logging.pattern.level",
|
||||
"%clr(%5p) %clr([${spring.application.name:},%X{X-Trace-Id:-},%X{X-Span-Id:-},%X{X-Span-Export:-}]){yellow}");
|
||||
"%clr(%5p) %clr([${spring.application.name:},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow}");
|
||||
map.put("spring.aop.proxyTargetClass", "true");
|
||||
addOrReplace(environment.getPropertySources(), map);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,7 @@ public class MessagingSpanExtractor implements SpanExtractor<Message> {
|
||||
: 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);
|
||||
}
|
||||
spanBuilder.exportable(Span.SPAN_SAMPLED.equals(getHeader(carrier, Span.SAMPLED_NAME)));
|
||||
String parentId = getHeader(carrier, Span.PARENT_ID_NAME);
|
||||
String processId = getHeader(carrier, Span.PROCESS_ID_NAME);
|
||||
String spanName = getHeader(carrier, Span.SPAN_NAME_NAME);
|
||||
|
||||
@@ -52,8 +52,8 @@ public class MessagingSpanInjector implements SpanInjector<MessageBuilder> {
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor
|
||||
.getMutableAccessor(initialMessage);
|
||||
if (span == null) {
|
||||
if (!initialMessage.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
|
||||
accessor.setHeader(Span.NOT_SAMPLED_NAME, "true");
|
||||
if (!Span.SPAN_SAMPLED.equals(initialMessage.getHeaders().get(Span.SAMPLED_NAME))) {
|
||||
accessor.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
carrier.setHeaders(accessor);
|
||||
return;
|
||||
}
|
||||
@@ -70,9 +70,10 @@ public class MessagingSpanInjector implements SpanInjector<MessageBuilder> {
|
||||
}
|
||||
addHeader(headers, Span.SPAN_NAME_NAME, span.getName());
|
||||
addHeader(headers, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
addHeader(headers, Span.SAMPLED_NAME, Span.SPAN_SAMPLED);
|
||||
}
|
||||
else {
|
||||
addHeader(headers, Span.NOT_SAMPLED_NAME, "true");
|
||||
addHeader(headers, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
accessor.setHeader(SPAN_HEADER, span);
|
||||
accessor.copyHeaders(headers);
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
if (span != null) {
|
||||
return getTracer().createSpan(name, span);
|
||||
}
|
||||
if (message.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
|
||||
if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(Span.SAMPLED_NAME))) {
|
||||
return getTracer().createSpan(name, NeverSampler.INSTANCE);
|
||||
}
|
||||
return getTracer().createSpan(name);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* AutoConfiguration containing Span extractor and injector for messaging.
|
||||
* Will be reused by Messaging and WebSockets
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class TraceSpanMessagingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpanExtractor<Message> messagingSpanExtractor(Random random) {
|
||||
return new MessagingSpanExtractor(random);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<MessageBuilder> messagingSpanInjector(TraceKeys traceKeys) {
|
||||
return new MessagingSpanInjector(traceKeys);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ 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;
|
||||
@@ -47,7 +46,7 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||
@Configuration
|
||||
@ConditionalOnClass(GlobalChannelInterceptor.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@AutoConfigureAfter({TraceAutoConfiguration.class, TraceSpanMessagingAutoConfiguration.class})
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TraceKeys.class)
|
||||
public class TraceSpringIntegrationAutoConfiguration {
|
||||
@@ -56,25 +55,9 @@ public class TraceSpringIntegrationAutoConfiguration {
|
||||
@GlobalChannelInterceptor
|
||||
public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer,
|
||||
TraceKeys traceKeys, Random random,
|
||||
@Qualifier("messagingSpanExtractor") SpanExtractor<Message> spanExtractor,
|
||||
@Qualifier("messagingSpanInjector") SpanInjector<MessageBuilder> spanInjector) {
|
||||
SpanExtractor<Message> spanExtractor,
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
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;
|
||||
@@ -12,11 +9,8 @@ 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.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
@@ -37,16 +31,17 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
*/
|
||||
@Component
|
||||
@Configuration
|
||||
@AutoConfigureAfter(TraceSpringIntegrationAutoConfiguration.class)
|
||||
@AutoConfigureAfter(TraceSpanMessagingAutoConfiguration.class)
|
||||
@ConditionalOnClass(DelegatingWebSocketMessageBrokerConfiguration.class)
|
||||
@ConditionalOnBean(AbstractWebSocketMessageBrokerConfigurer.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.websockets.enabled", matchIfMissing = true)
|
||||
public class TraceWebSocketAutoConfiguration
|
||||
extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired @Qualifier("stompMessagingSpanExtractor") SpanExtractor<Message> spanExtractor;
|
||||
@Autowired @Qualifier("stompMessagingSpanInjector") SpanInjector<MessageBuilder> spanInjector;
|
||||
@Autowired SpanExtractor<Message> spanExtractor;
|
||||
@Autowired SpanInjector<MessageBuilder> spanInjector;
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
@@ -64,20 +59,4 @@ public class TraceWebSocketAutoConfiguration
|
||||
registration.setInterceptors(
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -50,15 +50,23 @@ class HttpServletRequestExtractor implements SpanExtractor<HttpServletRequest> {
|
||||
|
||||
@Override
|
||||
public Span joinTrace(HttpServletRequest carrier) {
|
||||
if (carrier.getHeader(Span.TRACE_ID_NAME) == null) {
|
||||
// can't build a Span without trace id
|
||||
return null;
|
||||
}
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(carrier);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| carrier.getHeader(Span.NOT_SAMPLED_NAME) != null;
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(carrier.getHeader(Span.SAMPLED_NAME));
|
||||
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();
|
||||
return buildParentSpan(carrier, uri, skip, traceId, spanId);
|
||||
}
|
||||
|
||||
private Span buildParentSpan(HttpServletRequest carrier, String uri, boolean skip,
|
||||
long traceId, long spanId) {
|
||||
SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
|
||||
String processId = carrier.getHeader(Span.PROCESS_ID_NAME);
|
||||
String parentName = carrier.getHeader(Span.SPAN_NAME_NAME);
|
||||
|
||||
@@ -39,7 +39,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -107,16 +106,14 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
throws ServletException, IOException {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| ServletUtils.getHeader(request, response, Span.NOT_SAMPLED_NAME) != null;
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(ServletUtils.getHeader(request, response, Span.SAMPLED_NAME));
|
||||
Span spanFromRequest = (Span) request.getAttribute(TRACE_REQUEST_ATTR);
|
||||
if (spanFromRequest != null) {
|
||||
this.tracer.continueSpan(spanFromRequest);
|
||||
}
|
||||
else if (skip) {
|
||||
addToResponseIfNotPresent(response, Span.NOT_SAMPLED_NAME, "");
|
||||
}
|
||||
addToResponseIfNotPresent(response, Span.SAMPLED_NAME, skip ? Span.SPAN_NOT_SAMPLED : Span.SPAN_SAMPLED);
|
||||
String name = HTTP_COMPONENT + ":" + uri;
|
||||
spanFromRequest = createSpan(request, response, skip, spanFromRequest, name);
|
||||
spanFromRequest = createSpan(request, skip, spanFromRequest, name);
|
||||
Throwable exception = null;
|
||||
try {
|
||||
addRequestTags(request);
|
||||
@@ -135,9 +132,7 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
// TODO: how to deal with response annotations and async?
|
||||
return;
|
||||
}
|
||||
if (skip) {
|
||||
addToResponseIfNotPresent(response, Span.NOT_SAMPLED_NAME, "");
|
||||
}
|
||||
addToResponseIfNotPresent(response, Span.SAMPLED_NAME, skip ? Span.SPAN_NOT_SAMPLED : Span.SPAN_SAMPLED);
|
||||
if (spanFromRequest != null) {
|
||||
addResponseTags(response, exception);
|
||||
if (spanFromRequest.hasSavedSpan()) {
|
||||
@@ -156,16 +151,16 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
/**
|
||||
* Creates a span and appends it as the current request's attribute
|
||||
*/
|
||||
private Span createSpan(HttpServletRequest request, HttpServletResponse response,
|
||||
private Span createSpan(HttpServletRequest request,
|
||||
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);
|
||||
Span parent = this.spanExtractor
|
||||
.joinTrace(request);
|
||||
if (parent != null) {
|
||||
spanFromRequest = this.tracer.createSpan(name, parent);
|
||||
if (parent != null && parent.isRemote()) {
|
||||
if (parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_RECV);
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.regex.Pattern;
|
||||
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;
|
||||
@@ -77,26 +76,20 @@ public class TraceWebAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
@Qualifier("httpServletRequestSpanExtractor") SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
@Qualifier("httpServletResponseInjector") SpanInjector<HttpServletResponse> spanInjector) {
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
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() {
|
||||
public SpanInjector<HttpServletResponse> httpServletResponseSpanInjector() {
|
||||
return new HttpServletResponseInjector();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@ class HttpRequestInjector implements SpanInjector<HttpRequest> {
|
||||
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.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
|
||||
@@ -66,7 +66,7 @@ public class TraceWebClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector httpRequestInjector() {
|
||||
public SpanInjector<HttpRequest> httpRequestSpanInjector() {
|
||||
return new HttpRequestInjector();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,15 +34,14 @@ class FeignRequestTemplateInjector implements SpanInjector<RequestTemplate> {
|
||||
@Override
|
||||
public void inject(Span span, RequestTemplate carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
setHeader(carrier, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
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");
|
||||
}
|
||||
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
Long parentId = getParentId(span);
|
||||
if (parentId != null) {
|
||||
setHeader(carrier, Span.PARENT_ID_NAME, Span.idToHex(parentId));
|
||||
|
||||
@@ -47,9 +47,11 @@ class FeignResponseHeadersInjector implements SpanInjector<FeignResponseHeadersH
|
||||
Map<String, Collection<String>> newHeaders = new HashMap<>();
|
||||
newHeaders.putAll(headers);
|
||||
if (span == null) {
|
||||
setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
setHeader(newHeaders, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return newHeaders;
|
||||
}
|
||||
setHeader(newHeaders, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(newHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
return newHeaders;
|
||||
|
||||
@@ -34,9 +34,11 @@ class RequestBuilderContextInjector implements SpanInjector<Builder> {
|
||||
@Override
|
||||
public void inject(Span span, Builder carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.NOT_SAMPLED_NAME, "true");
|
||||
setHeader(carrier, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(carrier, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
setHeader(carrier, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
|
||||
@@ -37,15 +37,14 @@ class RequestContextInjector implements SpanInjector<RequestContext> {
|
||||
public void inject(Span span, RequestContext carrier) {
|
||||
Map<String, String> requestHeaders = carrier.getZuulRequestHeaders();
|
||||
if (span == null) {
|
||||
setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
setHeader(requestHeaders, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
}
|
||||
setHeader(requestHeaders, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
setHeader(requestHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(requestHeaders, Span.SPAN_NAME_NAME, span.getName());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
setHeader(requestHeaders, Span.SAMPLED_NAME, span.isExportable() ?
|
||||
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
setHeader(requestHeaders, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(requestHeaders, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
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;
|
||||
@@ -51,13 +52,14 @@ public class TraceZuulAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TracePreZuulFilter tracePreZuulFilter(Tracer tracer, SpanInjector<RequestContext> spanInjector) {
|
||||
public TracePreZuulFilter tracePreZuulFilter(Tracer tracer,
|
||||
SpanInjector<RequestContext> spanInjector) {
|
||||
return new TracePreZuulFilter(tracer, spanInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory,
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector) {
|
||||
Tracer tracer, @Qualifier("requestBuilderContextSpanInjector") SpanInjector<HttpRequest.Builder> spanInjector) {
|
||||
return new TraceRestClientRibbonCommandFactory(factory, tracer, spanInjector);
|
||||
}
|
||||
|
||||
@@ -68,12 +70,12 @@ public class TraceZuulAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanInjector<RequestContext> requestContextInjector() {
|
||||
public SpanInjector<RequestContext> requestContextSpanInjector() {
|
||||
return new RequestContextInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanInjector<HttpRequest.Builder> requestBuilderContextInjector() {
|
||||
public SpanInjector<HttpRequest.Builder> requestBuilderContextSpanInjector() {
|
||||
return new RequestBuilderContextInjector();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SleuthLogAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "spring.sleuth.log.slf4j.enabled", matchIfMissing = true)
|
||||
public SpanLogger slf4jSpanLogger() {
|
||||
// Sets up MDC entries X-Trace-Id and X-Span-Id
|
||||
// Sets up MDC entries X-B3-TraceId and X-B3-SpanId
|
||||
return new Slf4jSpanLogger(this.nameSkipPattern);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.metric.TraceMetricsAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.async.AsyncCustomAutoConfiguration,\
|
||||
|
||||
@@ -94,7 +94,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
@Test
|
||||
public void nonExportableSpanCreation() {
|
||||
this.channel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "true").build());
|
||||
.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
|
||||
assertNotNull("message was null", this.message);
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
|
||||
@@ -99,9 +99,7 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra
|
||||
.accept(MediaType.TEXT_PLAIN)
|
||||
.header(headerName, Span.idToHex(correlationId))
|
||||
.header(Span.SPAN_ID_NAME, Span.idToHex(new Random().nextLong()));
|
||||
if (!sampling) {
|
||||
request.header(Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
request.header(Span.SAMPLED_NAME, sampling ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
|
||||
return this.mockMvc.perform(request).andReturn();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(TraceFilterCustomExtractorTests.Config.class)
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
public class TraceFilterCustomExtractorTests {
|
||||
@Autowired Random random;
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
@Autowired CustomRestController customRestController;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void should_create_a_valid_span_from_custom_headers() {
|
||||
long spanId = this.random.nextLong();
|
||||
long traceId = this.random.nextLong();
|
||||
RequestEntity requestEntity = RequestEntity.get(
|
||||
URI.create("http://localhost:" + this.config.port + "/headers"))
|
||||
.header("correlationId", Span.idToHex(traceId))
|
||||
.header("mySpanId", Span.idToHex(spanId))
|
||||
.build();
|
||||
|
||||
ResponseEntity<Map> requestHeaders =
|
||||
this.restTemplate.exchange(requestEntity, Map.class);
|
||||
|
||||
then(this.customRestController.span)
|
||||
.hasTraceIdEqualTo(traceId);
|
||||
then(requestHeaders.getBody())
|
||||
.containsEntry("correlationId", Span.idToHex(traceId))
|
||||
.containsEntry("mySpanId", Span.idToHex(spanId))
|
||||
.as("input request headers");
|
||||
then(requestHeaders.getHeaders())
|
||||
.containsEntry("correlationId", Collections.singletonList(Span.idToHex(traceId)))
|
||||
.containsKey("mySpanId")
|
||||
.as("response headers");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config implements
|
||||
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
|
||||
int port;
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
@Primary
|
||||
SpanExtractor<HttpServletRequest> customHttpServletRequestSpanExtractor() {
|
||||
return new CustomHttpServletRequestSpanExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
this.port = event.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Bean CustomRestController customRestController() {
|
||||
return new CustomRestController();
|
||||
}
|
||||
}
|
||||
|
||||
// tag::extractor[]
|
||||
static class CustomHttpServletRequestSpanExtractor implements SpanExtractor<HttpServletRequest> {
|
||||
|
||||
@Override
|
||||
public Span joinTrace(HttpServletRequest carrier) {
|
||||
long traceId = Span.hexToId(carrier.getHeader("correlationId"));
|
||||
long spanId = Span.hexToId(carrier.getHeader("mySpanId"));
|
||||
// extract all necessary headers
|
||||
Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId);
|
||||
// build rest of the Span
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
// end::extractor[]
|
||||
|
||||
// tag::injector[]
|
||||
static class CustomHttpServletResponseSpanInjector implements SpanInjector<HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
carrier.addHeader("correlationId", Span.idToHex(span.getTraceId()));
|
||||
carrier.addHeader("mySpanId", Span.idToHex(span.getSpanId()));
|
||||
// inject the rest of Span values to the header
|
||||
}
|
||||
}
|
||||
// end::injector[]
|
||||
|
||||
@RestController
|
||||
static class CustomRestController {
|
||||
Span span;
|
||||
|
||||
@RequestMapping("/headers")
|
||||
public Map<String, String> headers(@RequestHeader HttpHeaders headers) {
|
||||
this.span = TestSpanContextHolder.getCurrentSpan();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String key : headers.keySet()) {
|
||||
map.put(key, headers.getFirst(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
}
|
||||
|
||||
private boolean notSampledHeaderIsPresent(MvcResult mvcResult) {
|
||||
return mvcResult.getResponse().containsHeader(Span.NOT_SAMPLED_NAME);
|
||||
return Span.SPAN_NOT_SAMPLED.equals(mvcResult.getResponse().getHeader(Span.SAMPLED_NAME));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -103,7 +103,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
.getBody();
|
||||
then(Span.hexToId(headers.get(Span.TRACE_ID_NAME))).isEqualTo(1L);
|
||||
then(Span.hexToId(headers.get(Span.SPAN_ID_NAME))).isNotEqualTo(2L);
|
||||
then(headers.get(Span.NOT_SAMPLED_NAME)).isEqualTo("true");
|
||||
then(headers.get(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
// issue #198
|
||||
@@ -141,7 +141,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
this.span = TraceRestTemplateInterceptorTests.this.tracer.getCurrentSpan();
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
addHeaders(map, headers, Span.SPAN_ID_NAME, Span.TRACE_ID_NAME,
|
||||
Span.PARENT_ID_NAME, Span.NOT_SAMPLED_NAME);
|
||||
Span.PARENT_ID_NAME, Span.SAMPLED_NAME);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ public class WebClientTests {
|
||||
ResponseEntity<Map<String, String>> response = provider.get(this);
|
||||
|
||||
then(response.getBody().get(Span.TRACE_ID_NAME)).isNotNull();
|
||||
then(response.getBody().get(Span.NOT_SAMPLED_NAME)).isNotNull();
|
||||
then(response.getBody().get(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
then(this.listener.getEvents()).isNotEmpty();
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ public class WebClientTests {
|
||||
|
||||
ResponseEntity<String> response = provider.get(this);
|
||||
|
||||
then(getHeader(response, Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_SAMPLED);
|
||||
then(Span.hexToId(getHeader(response, Span.TRACE_ID_NAME)))
|
||||
.isEqualTo(currentTraceId);
|
||||
thenRegisteredClientSentAndReceivedEvents(spanWithClientEvents());
|
||||
|
||||
@@ -32,10 +32,7 @@ import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -58,23 +55,27 @@ public class TracePreZuulFilterTests {
|
||||
@Test
|
||||
public void filterAddsHeaders() throws Exception {
|
||||
this.tracer.createSpan("http:start");
|
||||
|
||||
this.filter.run();
|
||||
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
assertThat(ctx.getZuulRequestHeaders().get(Span.TRACE_ID_NAME),
|
||||
is(notNullValue()));
|
||||
assertThat(ctx.getZuulRequestHeaders().get(Span.NOT_SAMPLED_NAME),
|
||||
is(nullValue()));
|
||||
then(ctx.getZuulRequestHeaders().get(Span.TRACE_ID_NAME))
|
||||
.isNotNull();
|
||||
then(ctx.getZuulRequestHeaders().get(Span.SAMPLED_NAME))
|
||||
.isEqualTo(Span.SPAN_SAMPLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSampledIfNotExportable() throws Exception {
|
||||
this.tracer.createSpan("http:start", NeverSampler.INSTANCE);
|
||||
|
||||
this.filter.run();
|
||||
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
assertThat(ctx.getZuulRequestHeaders().get(Span.TRACE_ID_NAME),
|
||||
is(notNullValue()));
|
||||
assertThat(ctx.getZuulRequestHeaders().get(Span.NOT_SAMPLED_NAME),
|
||||
is(notNullValue()));
|
||||
then(ctx.getZuulRequestHeaders().get(Span.TRACE_ID_NAME))
|
||||
.isNotNull();
|
||||
then(ctx.getZuulRequestHeaders().get(Span.SAMPLED_NAME))
|
||||
.isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
|
||||
* A {@link HostLocator} that retrieves:
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>service name</b> - either from {@link span#getProcessId()} or current application name</li>
|
||||
* <li><b>service name</b> - either from {@link Span#getProcessId()} or current application name</li>
|
||||
* <li><b>address</b> - from {@link ServerProperties}</li>
|
||||
* <li><b>port</b> - from lazily assigned port or {@link ServerProperties}</li>
|
||||
* </ul>
|
||||
|
||||
@@ -49,7 +49,7 @@ public class StreamEnvironmentPostProcessor implements EnvironmentPostProcessor
|
||||
private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
|
||||
private static String[] headers = new String[] { Span.SPAN_ID_NAME,
|
||||
Span.TRACE_ID_NAME, Span.PARENT_ID_NAME, Span.PROCESS_ID_NAME,
|
||||
Span.NOT_SAMPLED_NAME, Span.SPAN_NAME_NAME };
|
||||
Span.SAMPLED_NAME, Span.SPAN_NAME_NAME };
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment,
|
||||
|
||||
@@ -43,7 +43,7 @@ class TracerIgnoringChannelInterceptor extends ChannelInterceptorAdapter {
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
return MessageBuilder.fromMessage(message)
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "true").build();
|
||||
.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -49,8 +49,8 @@ public class TracerIgnoringChannelInterceptorTest {
|
||||
|
||||
Message interceptedMessage = this.tracerIgnoringChannelInterceptor.preSend(message, this.messageChannel);
|
||||
|
||||
then(interceptedMessage.getHeaders().containsKey(
|
||||
Span.NOT_SAMPLED_NAME)).isTrue();
|
||||
then(interceptedMessage.getHeaders().get(
|
||||
Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user