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.
@Bean
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor() {
...
-}
-
-@Bean
-public SpanInjector<HttpServletResponse> httpServletResponseSpanInjector() {
- ...
}
static class CustomHttpServletRequestSpanExtractor
- implements SpanExtractor<HttpServletRequest> {
+static class CustomHttpSpanExtractor implements HttpSpanExtractor {
- @Override
- public Span joinTrace(HttpServletRequest carrier) {
- long traceId = Span.hexToId(carrier.getHeader("correlationId"));
- long spanId = Span.hexToId(carrier.getHeader("mySpanId"));
+ @Override public Span joinTrace(SpanTextMap carrier) {
+ Map<String, String> map = TextMapUtil.asMap(carrier);
+ long traceId = Span.hexToId(map.get("correlationid"));
+ long spanId = Span.hexToId(map.get("myspanid"));
// extract all necessary headers
Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId);
// build rest of the Span
return builder.build();
}
+}
+
+static class CustomHttpSpanInjector implements HttpSpanInjector {
+
+ @Override
+ public void inject(Span span, SpanTextMap carrier) {
+ carrier.put("correlationId", Span.idToHex(span.getTraceId()));
+ carrier.put("mySpanId", Span.idToHex(span.getSpanId()));
+ }
}
The following SpanInjector could be created
And you could register it like this:
static class CustomHttpServletResponseSpanInjector
- implements SpanInjector<HttpServletResponse> {
+@Bean
+HttpSpanInjector customHttpSpanInjector() {
+ return new CustomHttpSpanInjector();
+}
+
+@Bean
+HttpSpanExtractor customHttpSpanExtractor() {
+ return new CustomHttpSpanExtractor();
+}
+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:
static class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector {
@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
+ public void inject(Span span, SpanTextMap carrier) {
+ super.inject(span, carrier);
+ carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
+ carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
}
+}
+
+static class HttpResponseInjectingTraceFilter extends GenericFilterBean {
+
+ private final Tracer tracer;
+ private final HttpSpanInjector spanInjector;
+
+ public HttpResponseInjectingTraceFilter(Tracer tracer, HttpSpanInjector 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, new HttpServletResponseTextMap(response));
+ filterChain.doFilter(request, response);
+ }
+
+ class HttpServletResponseTextMap implements SpanTextMap {
+
+ private final HttpServletResponse delegate;
+
+ HttpServletResponseTextMap(HttpServletResponse delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Iterator<Map.Entry<String, String>> iterator() {
+ Map<String, String> map = new HashMap<>();
+ for (String header : this.delegate.getHeaderNames()) {
+ map.put(header, this.delegate.getHeader(header));
+ }
+ return map.entrySet().iterator();
+ }
+
+ @Override
+ public void put(String key, String value) {
+ this.delegate.addHeader(key, value);
+ }
+ }
}