Class HttpTraceHeaderCarrier

java.lang.Object
com.fayupable.logged.spring.http.HttpTraceHeaderCarrier

public final class HttpTraceHeaderCarrier extends Object
Carries a @Logged call chain's FlowContext across a synchronous HTTP call, by writing it into the outgoing request's headers and reading it back from the incoming request's headers.

Unlike KafkaTraceHeaderCarrier and RabbitTraceHeaderCarrier, which operate on one concrete header type each (Headers, MessageProperties), no single header type is common to every HTTP client a consuming application might use — RestTemplate, Feign, WebClient, and a hand-rolled HttpURLConnection each expose headers through a different API. This class instead operates on plain BiConsumer/Function references, so it can be pointed at whichever client's own header-writing/reading method already exists:


 // RestTemplate / a plain HttpHeaders instance
 HttpTraceHeaderCarrier.writeToHeaders(headers::set);

 // Feign
 HttpTraceHeaderCarrier.writeToHeaders(requestTemplate::header);

 // java.net.HttpURLConnection
 HttpTraceHeaderCarrier.writeToHeaders(connection::setRequestProperty);
 

HttpTraceClientHttpRequestInterceptor, FeignTraceRequestInterceptor, HttpTraceExchangeFilterFunction, and HttpTraceServletFilter are ready-made integrations built on top of this class for RestTemplate, Feign, WebClient, and inbound Servlet requests, respectively. A consuming application using a different HTTP client can still participate in cross-service tracing by calling this class directly, the same way those four classes do internally.

Only the traceId and depth cross the wire — never any other request data — under the "X-Logged-Trace-Id"/ "X-Logged-Depth" header names.

Unlike KafkaTraceHeaderCarrier/ RabbitTraceHeaderCarrier, whose message headers typically originate from another of a consuming application's own services, an HTTP request's headers can originate from a completely untrusted caller if the endpoint receiving it is reachable from outside the application's own trust boundary. Without validation, an external caller could set TRACE_ID_HEADER to an arbitrary string — including one crafted to look like a fabricated log line, if it contains characters like \n and the receiving side's logging pattern does not escape them — which would then flow, unexamined, into this thread's @Logged log output and MDC. readAndAdopt(java.util.function.Function<java.lang.String, java.lang.String>, java.lang.Runnable) therefore only accepts a TRACE_ID_HEADER value that matches the same shape this library itself always produces (a short hexadecimal string, see FlowContext.root()); anything else is treated exactly like a missing header, not merely logged-but-rejected, so no attacker-controlled string ever reaches this thread's context at all.

  • Field Details

  • Method Details

    • writeToHeaders

      public static void writeToHeaders(BiConsumer<String,String> headerWriter)
      Writes the current thread's FlowContext, if any is active, through headerWriter under TRACE_ID_HEADER/ DEPTH_HEADER.

      Does nothing if no @Logged call is currently active on this thread: an outgoing request made from outside any call chain simply carries no trace headers, exactly as if this method had never been called.

      Parameters:
      headerWriter - a reference to the target's own (name, value) -> void header-writing method
    • readAndAdopt

      public static void readAndAdopt(Function<String,String> headerReader, Runnable work)
      Reads a FlowContext through headerReader, if present, adopts it as the active context on this thread for the duration of work, and restores this thread's previous context again afterward, regardless of whether work completes normally or throws.

      If no TRACE_ID_HEADER is found, or its value does not match VALID_TRACE_ID — for example, a request from a caller that does not use this library, or one crafted by an untrusted caller to inject something other than a real trace id — work is simply run as-is, with no context adopted. Any @Logged call made from within it then starts a new chain of its own, exactly as it would without this class involved at all.

      Parameters:
      headerReader - a reference to the source's own (name) -> String header-reading method; may return null for a missing header
      work - the request-handling code to run with the incoming request's trace context active