Distribution Tracing Implementation

As part of our efforts to enhance our logging module, we are currently working on integrating trace ID support to make it easier to trace requests and identify issues across all microservices.

We have successfully configured the gateway to generate and forward the trace ID. However, we are encountering a challenge when trying to propagate this trace ID through each WebClient instance.

We discovered that Broadleaf implements a getHeaders() method within its provider classes. Unfortunately, this method selectively forwards certain headers and does not include all headers by default—including our custom trace ID.

Given this, we are looking for guidance or best practices on how to reliably propagate the trace ID across all microservices. Specifically, we would appreciate your recommendations on:

  • How to ensure that the trace ID is included in all outgoing WebClient requests.

  • How to make sure the trace ID is correctly received and utilized downstream, especially considering Broadleaf’s custom header handling.

Thank you in advance for your support and insights.

We did something similar to this recently with on of our clients. It sounds like you want a header to be forwarded all around across distributed boundaries. A combination of a standard HTTP filter and BeanPostProcessor work well for this. These can each be registered as beans in standard Spring configuration in a common location so that all concerned applications can benefit from the behavior.

public class TraceFilter extends OncePerRequestFilter {

    public static final ThreadLocal<String> THREADITEM = new ThreadLocal<>();

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain) throws ServletException, IOException {
        String myHeader = request.getHeader(MY_HEADER);
        if (myHeader != null) {
            THREADITEM.set(myHeader);
        }
    }

}
public class WebClientBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof WebClient) {
            return ((WebClient) bean).mutate().defaultRequest(
                    requestHeadersSpec -> requestHeadersSpec.headers(httpHeaders -> {
                        String myHeader = THREADITEM.get();
                        if (myHeader != null) {
                            httpHeaders.add(MY_HEADER, myHeader);
                        }
                    })).build();
        }
        return bean;
    }

}