akka-http icon indicating copy to clipboard operation
akka-http copied to clipboard

Describe on how a logger can be customised in the Route-Chain (Java) / modify MDC

Open domdorn opened this issue 5 years ago • 2 comments

I need to customise the logger that gets returned by extractLog( log -> () ) so I can attach the current correlationId / userId / other fields into the MDC. However, I'm unable to accomplish this with the documentation I found. This is what I got so far:

  public static RouteAdapter extractCorrelationId(Function<String, Route> inner) {
    return optionalHeaderValueByName("X-CorrelationId", (corr) -> extractLog(log -> {
      String correlationId = corr.orElseGet(() -> {
        String c = UUID.randomUUID().toString();
        log.debug("request does not have a upstream correlationId, assigning " + c);
        return c;
      });
      return provide(correlationId, inner);
    }));
  }

and I was planning to decorate my root-route as follows:

  public Route createRoute() {
    return extractCorrelationId(corr -> extractLog(log -> {
      LoggingAdapter newLog = log;
      var newMDC = newLog.mdc().$plus(Tuple2.apply("correlationId", corr)); // this is a MDC and not a LoggingAdapter :( 
      // how to bring the newMDC into the newLog ?  because AFAIK the MDC is a immutable list, thus I cannot simply change its values 
      return withLog(newLog, () -> allRoutes());
    }));
  }

Please provide a way on how a valid "LoggingAdapter" can be constructed from Java.

domdorn avatar Jul 20 '20 13:07 domdorn

You will have to create a new LoggingAdapter in the first place because at least that part of LoggingAdapters is not thread-safe and use log.setMdc or log.mdc(map) to set the mdc.

The other option is to use the slf4j APIs directly but then you'll have to make to set the MDC whenever you run a chunk of code in a new thread (or use the automatic MDC propagation in commercial Lightbend Telemetry).

I keep this ticket open to create documentation around this particular pattern.

jrudolph avatar Jul 30 '20 08:07 jrudolph

I recently found a way to do this using the existing loggers. There are basically three directives:

  • a directive that takes the RouteContext and provides it with a new DiagnosticMarkerBusLoggingAdapter
  • a directive that extracts the DiagnosticMarkerBusLoggingAdapter from the RouteContext
  • a directive that takes a key/value pair and appends it to the MDC map on the DiagnosticMarkerBusLoggingAdapter.

As far as I can tell, this is enough to make it thread-safe: each request gets a new logger, and, AFAIK, each request's directives are executed serially, so the map updates must be serialized. In any case I also have tests that hit the routes in parallel to test the thread-safety.

We currently have this in our internal utilities. Would there be interest in making a PR with these directives? LMK what you think @jrudolph or anyone else in the akka team.

alexklibisz avatar Aug 12 '21 00:08 alexklibisz