It seems that MDC can be propagated between applications by using context together with Spring WebFlux.

Preface

To use MDC with Spring WebFlux, use context I wrote about the story of propagating MDC between applications. I will. Knowledge of the above article is required in terms of content.

The story of the pioneer

-Examples of using Spring and WebFlux in the chat system of Line official account ――How can I get request information on MDC? The topic. Patterns to solve using WebFilter, Hook, Context

Main subject

Microservices, which are popular these days, come with a lot of complexity, one of which is logs. There is a natural demand for the communication that flows from A system-> B system-> C system to be made into a single log later, and various methods have been taken to solve this. (Spring Sleuth, etc.)

In the same way, there was a demand to let us take over various debug information together, and our system was just caught up in this.

This time, I will take up one of the methods introduced in Passing Context with Spring Web Flux.

Specific code

To briefly describe what WebFilter is doing, which I will introduce from now on

--For the request, I got all the HttpHeaders with the prefix X-MDC-, stripped the prefix and put it in the context. The key value of the context is the constant CONTEXT_MAP. --For the response, the value taken from the constant CONTEXT_MAP is returned with the prefix X-MDC-.

MdcHeaderFilter.java


import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

import java.util.Map;

import static java.util.stream.Collectors.toMap;
import static jp.co.example.helper.LogHelper.*;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcHeaderFilter implements WebFilter {
  private static final String MDC_HEADER_PREFIX = "X-MDC-";

  @Override
  public Mono<Void> filter(
    ServerWebExchange ex,
    WebFilterChain chain) {
    ex.getResponse().beforeCommit(
      () -> addContextToHttpResponseHeaders(ex.getResponse())
    );

    return chain.filter(ex)
      .subscriberContext(
        ctx -> addRequestHeadersToContext(ex.getRequest(), ctx)
      );
  }

  private Context addRequestHeadersToContext(
    final ServerHttpRequest request,
    final Context context) {

    final Map<String, String> contextMap = request
      .getHeaders().toSingleValueMap().entrySet()
      .stream()
      .filter(x -> x.getKey().startsWith(MDC_HEADER_PREFIX))
      .collect(
        toMap(v -> v.getKey().substring(MDC_HEADER_PREFIX.length()),
          Map.Entry::getValue
        )
      );

    //For example, if you want to put a cookie, do this
    String cookie = request.getCookies().containsKey("EXAMPLE") ?
      request.getCookies().getFirst("EXAMPLE").getValue() : "none";
    contextMap.put("example-cookie", cookie);

    return context.put(CONTEXT_MAP, contextMap);
  }

  private Mono<Void> addContextToHttpResponseHeaders(
    final ServerHttpResponse res) {

    return Mono.subscriberContext().doOnNext(ctx -> {
      if (!ctx.hasKey(CONTEXT_MAP)) return;

      final HttpHeaders headers = res.getHeaders();
      ctx.<Map<String, String>>get(CONTEXT_MAP).forEach(
        (key, value) -> headers.add(MDC_HEADER_PREFIX + key, value)
      );
    }).then();
  }
}

It's very simple to do, but it's versatile. It can also be used when you have to use a cookie as user information for debugging like our ~~ F ● ck ~~ system.

end

Don't get caught up in the response you get back to your customers!

Recommended Posts

It seems that MDC can be propagated between applications by using context together with Spring WebFlux.
You use context to use MDC with Spring WebFlux
Object-oriented that can be understood by fairies