Issue 424 - Documentation for adding trace/span headers to http response (#429)
fixes #424
This commit is contained in:
committed by
Marcin Grzejszczak
parent
56048b1852
commit
8437bef6a9
@@ -228,8 +228,7 @@ 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.
|
||||
For HTTP these are the beans responsible for creation of a Span from a `HttpServletRequest`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -237,11 +236,6 @@ For HTTP these are the beans responsible for creation of a Span from a `HttpServ
|
||||
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor() {
|
||||
...
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<HttpServletResponse> httpServletResponseSpanInjector() {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
You can override them by providing your own implementation and by adding a `@Primary` annotation
|
||||
@@ -262,20 +256,27 @@ This is a an example of a `SpanExtractor`
|
||||
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:
|
||||
And you could register it like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
Spring Cloud Sleuth does not add trace/span related headers to the Http Response for security reasons. If you need the headers then a custom `SpanInjector`
|
||||
that injects the headers into the Http Response and a Servlet filter which makes use of this can be added the following way:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.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/TraceCustomFilterResponseInjectorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
=== Custom SA tag in Zipkin
|
||||
|
||||
Sometimes you want to create a manual Span that will wrap a call to an external service which is not instrumented.
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
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.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
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 org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(TraceCustomFilterResponseInjectorTests.Config.class)
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
@DirtiesContext
|
||||
public class TraceCustomFilterResponseInjectorTests {
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
@Autowired CustomRestController customRestController;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void should_inject_trace_and_span_ids_in_response_headers() {
|
||||
RequestEntity<?> requestEntity = RequestEntity
|
||||
.get(URI.create("http://localhost:" + this.config.port + "/headers"))
|
||||
.build();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity, Map.class);
|
||||
|
||||
then(responseEntity.getHeaders())
|
||||
.containsKeys(Span.TRACE_ID_NAME, Span.SPAN_ID_NAME)
|
||||
.as("Trace headers must be present in response headers");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config
|
||||
implements ApplicationListener<EmbeddedServletContainerInitializedEvent> {
|
||||
int port;
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Tracer tracer) {
|
||||
return new HttpResponseInjectingTraceFilter(tracer, customHttpServletResponseSpanInjector());
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
this.port = event.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CustomRestController customRestController() {
|
||||
return new CustomRestController();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// tag::injector[]
|
||||
static class CustomHttpServletResponseSpanInjector
|
||||
implements SpanInjector<HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
}
|
||||
}
|
||||
|
||||
static class HttpResponseInjectingTraceFilter extends GenericFilterBean {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpServletResponse> spanInjector;
|
||||
|
||||
public HttpResponseInjectingTraceFilter(Tracer tracer, SpanInjector<HttpServletResponse> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
Span currentSpan = this.tracer.getCurrentSpan();
|
||||
this.spanInjector.inject(currentSpan, response);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
// end::injector[]
|
||||
|
||||
@RestController
|
||||
static class CustomRestController {
|
||||
|
||||
@RequestMapping("/headers")
|
||||
public Map<String, String> headers(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String key : headers.keySet()) {
|
||||
map.put(key, headers.getFirst(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -34,7 +33,6 @@ import org.springframework.boot.test.WebIntegrationTest;
|
||||
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.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
@@ -83,13 +81,13 @@ public class TraceFilterCustomExtractorTests {
|
||||
.header("mySpanId", Span.idToHex(spanId)).build();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> responseHeaders = this.restTemplate.exchange(requestEntity,
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity,
|
||||
Map.class);
|
||||
|
||||
await().until(() -> then(this.accumulator.getSpans().stream().filter(
|
||||
span -> span.getSpanId() == spanId).findFirst().get())
|
||||
.hasTraceIdEqualTo(traceId));
|
||||
then(responseHeaders.getBody())
|
||||
then(responseEntity.getBody())
|
||||
.containsEntry("correlationid", Span.idToHex(traceId))
|
||||
.containsEntry("myspanid", Span.idToHex(spanId))
|
||||
.as("input request headers");
|
||||
@@ -107,12 +105,6 @@ public class TraceFilterCustomExtractorTests {
|
||||
SpanExtractor<HttpServletRequest> customHttpServletRequestSpanExtractor() {
|
||||
return new CustomHttpServletRequestSpanExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@Override
|
||||
@@ -157,19 +149,6 @@ public class TraceFilterCustomExtractorTests {
|
||||
}
|
||||
// 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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user