Spring-cloud-sleuth: Adding TraceId to Response - Improvements?

Created on 10 Jul 2017  路  14Comments  路  Source: spring-cloud/spring-cloud-sleuth

Is this (https://stackoverflow.com/questions/41222405/adding-the-traceid-from-spring-cloud-sleuth-to-response) still the best way to do it in > 1.2.0?

Thanks.

documentation question

Most helpful comment

:+1: Thanks and yes, it does.

Wish :):) spring.sleuth.responseheader = true

All 14 comments

You can extend the TraceFilter and register your own version that will override this method https://github.com/spring-cloud/spring-cloud-sleuth/blob/1.2.x/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java#L403-L416 . In that method you have access to the Response and you can inject the Tracer to get the current span. That way you have both the trace id and you can modify the response headers. Does it make sense?

Note to myself: Add a test and embed that test in the docs

:+1: Thanks and yes, it does.

Wish :):) spring.sleuth.responseheader = true

Heh - I'll close this issue for now but if anyone is interested in reopening it please upvote. If there are quite a few people interested in it we'll come back to the discussion.

is there any example for this, thanks

BTW there is a discussion about this going on here - https://github.com/openzipkin/openzipkin.github.io/issues/48 . So in case of questions "why" we don't allow this by default please let's go to that issue.

I'll prepare sample code of how you can achieve this and update the documentation too.

One note... If you want to do this with versions prior to 1.2.5 you have to create a bean like this, with a @Primary annotation:

@Bean
@Primary
        TraceFilter myTraceFilter(BeanFactory beanFactory, final Tracer tracer) {
            return new TraceFilter(beanFactory) {
                @Override protected void addResponseTags(HttpServletResponse response,
                        Throwable e) {
                    // execute the default behaviour
                    super.addResponseTags(response, e);
                    // for readability we're returning trace id in a hex form
                    response.addHeader("ZIPKIN-TRACE-ID",
                            Span.idToHex(tracer.getCurrentSpan().getTraceId()));
                    // we can also add some custom tags
                    tracer.addTag("custom", "tag");
                }
            };
        }

@marcingrzejszczak We used HandlerInterceptorAdapter to implement this with the following code:

@Slf4j
@RequiredArgsConstructor
public class TraceHeaderInterceptor extends HandlerInterceptorAdapter {

    private static final String TRACE_ID = "TraceId";
    private final BeanFactory beanFactory;
    private Tracer tracer;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
        Object handler) throws Exception {
        if (getTracer().isTracing() && StringUtils.isEmpty(response.getHeader(TRACE_ID))) {
            response
                .setHeader(TRACE_ID, Span.idToHex(getTracer().getCurrentSpan().getTraceId()));
        }
        return true;
    }

    private Tracer getTracer() {
        if (tracer == null) {
            tracer = beanFactory.getBean(Tracer.class);
        }
        return tracer;
    }

}

Seems like TraceFilter was removed on 2.0.0. Is there an alternative to have the trace ID on response headers?

We're doing the following bean registration

@Bean
    public FilterRegistrationBean traceWebFilter(
            TracingFilter tracingFilter) {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(tracingFilter);
        filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
        filterRegistrationBean.setOrder(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER);
        return filterRegistrationBean;
    }

@Bean
    public FilterRegistrationBean exceptionThrowingFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new ExceptionLoggingFilter());
        filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
        filterRegistrationBean.setOrder(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1);
        return filterRegistrationBean;
    }

    @Bean
    @ConditionalOnMissingBean
    public TracingFilter tracingFilter(HttpTracing tracing) {
        return (TracingFilter) TracingFilter.create(tracing);
    }

You can create your own filter that starts after the TracingFilter one and set the trace id in the response.

Can this be as simple as an annotation @EnableTraceIdsInResponse?

If you create such an annotation then for sure it can :)

One note... If you want to do this with versions prior to 1.2.5 you have to create a bean like this, with a @Primary annotation:

@Bean
@Primary
      TraceFilter myTraceFilter(BeanFactory beanFactory, final Tracer tracer) {
          return new TraceFilter(beanFactory) {
              @Override protected void addResponseTags(HttpServletResponse response,
                      Throwable e) {
                  // execute the default behaviour
                  super.addResponseTags(response, e);
                  // for readability we're returning trace id in a hex form
                  response.addHeader("ZIPKIN-TRACE-ID",
                          Span.idToHex(tracer.getCurrentSpan().getTraceId()));
                  // we can also add some custom tags
                  tracer.addTag("custom", "tag");
              }
          };
      }

response.isCommitted() and response can't Modify

with spring-cloud Hoxton.RELEASE, I have done following implementation

public class ResponseTrackingFilter extends OncePerRequestFilter {

    private static final String TRACKING_HEADER = "X-TRACKING-ID";
    private Tracer tracer;

    @Override
    protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
                                    final FilterChain filterChain) throws ServletException, IOException {

        final Span currentSpan = tracer.currentSpan();
        if (null != currentSpan && StringUtils.isEmpty(response.getHeader(TRACKING_HEADER))) {

            final String traceId = currentSpan.context().traceIdString();
            log.debug("Added tracking id in response - {}", traceId);
            response.setHeader(TRACKING_HEADER, traceId);
        }
        filterChain.doFilter(request, response);
    }
}
Was this page helpful?
0 / 5 - 0 ratings