4

My application has several JAX-RS API, all of them are getting a transaction id as header, is there way we can access transaction id into Jboss Logger? we tried MDC but does that not help. Basically I am looking efficient way to add transaction id into each log.

CodeDCode
  • 390
  • 5
  • 17
  • 1
    You can create a wrapper of your logger as a CDI bean and inject the transaction id into this object, the implement the required logger operations, info, warm, debug ..., having access to this transaction ID, If you make this wrapper a RequestScoped bean you will be able to inject this specific instance into other beans while they are bound to a http request, could this work for you ? – Javier Toja Aug 06 '21 at 07:53

1 Answers1

7

You did not mention how you actually do the logging: explicit log. statements in the code, or some CDI/JAXRS interceptors...

A common way to achieve the desired functionality is to define a filter/interceptor on the boundary layer (JAX-RS in your case), that extracts the relevant request data and stores it in a context thats available to logger during execution of that request. Which is exactly what JAX-RS filters and MDC are for.

A simple example:

@Provider
public class TransactionLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Context
    HttpServerRequest request;

    @Override
    public void filter(ContainerRequestContext context) {
        MDC.put("transactionId", request.getHeader("transactionId"));
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        MDC.remove("transactionId");
    }
}

With that, you will store the value of your transactionId header in MDC scope before each HTTP request is processed and remove it after processing is complete.

Note: if you have other JAX-RS filters, you might need to configure priorities correctly (so that your logging extraction filter runs before others for example) see documentation

MDC scope is bound the thread that executes the request(careful if you use Quarkus reactive, make sure that it is propagated correctly) and will be passed to the logger impl with every log invocation.

To actually print out the value from MDC in your logs, you need to modify the Quarkus log format via:

quarkus.log.console.format=%d{HH:mm:ss} %-5p %X{transactionId} [%c{2.}] (%t) %s%e%n

You can access any MDC scope var with expression %X{var_name}.

See Quarkus documentation on logging for more info.

yntelectual
  • 3,028
  • 18
  • 24
  • Please, could you provide a link with an example for propagating the MDC in a reactive environment? – Mihai P. Nov 12 '21 at 10:29
  • 1
    But what happens if multiple calls are made in parallel? The MDC put and remove methods are static, and generic to the whole application. I think your solution won't work in this case – Federico Bellini Mar 03 '22 at 10:51
  • 1
    MDC is a thread scoped by default (basically behaves like ThreadLocal in imperative mode), so you will not run into any issues. For reactive mode checkout context propagation docs https://quarkus.io/guides/context-propagation and https://stackoverflow.com/questions/65039035/manual-context-propagation-in-quarkus-native-mode – yntelectual Mar 03 '22 at 15:45
  • Example does not work. Logs something like: 23:19:38 INFO null [io.qu....Tested on a quarkus-resteasy endpoint method annotated with @Transactional – syr Jul 01 '22 at 21:21
  • The example works when **the client sends** a header `transactionId` as the OP asked. If you want to log actual Narayana transaction id, then you need to obtain the current transaction ctx first - you can try injecting `TransactionManager` into the interceptor. Of course, you also need to make sure, that your log interceptor is invoked after the transaction interceptor(which might be a little tricky as tx interceptor is not a Jaxrs filter) – yntelectual Jul 30 '22 at 11:27