Bush
Bush

Reputation: 261

log4j is not able to print MDC key-value pair in threads

I am setting key-value pair as MDC.put("txnId", UUID.randomUUID().toString()); with log4j.properties as

log4j.appender.R.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}]  [%m] [%M] [%L] [%p] [%c] [%X{txnId}] %n

This works absolutely fine while logging the non-threaded logs, but the logs of classes which implements Runnable, txnId is coming as empty.

Below is the thread code

public class ConsT implements Runnable{
    public ConsT() {

        }

        @Override
        public void run() {

            log.info("Start thread"));
    }

I have tried putting my values in both org.apache.log4j.MDC and org.slf4j.MDC but none works.

Upvotes: 1

Views: 1785

Answers (2)

imperezivan
imperezivan

Reputation: 781

pass the current txnId as parameter in constructor when you create a new DeviceEventWorker

new DeviceEventWorker(.... , MDC.get("txnId"));

and set again in run method

public class DeviceEventWorker implements Runnable{

    private String txnId;

    public DeviceEventWorker(String tenantId, DeviceResource device, String eventName, LighthouseDevice lighthouseDevice, String txnId) {
            this.tenantId = tenantId;
            this.device = device;
            this.eventName = eventName;
            this.lighthouseDevice = lighthouseDevice;
            this.txnId = txnId;    
        }

        @Override
        public void run() {
            MDC.put("txnId", this.txnId);
            log.info("Start thread"));
    }
...
}

Upvotes: 1

Karol Dowbecki
Karol Dowbecki

Reputation: 44952

This problem nothing to do with the Runnable instance. MDC must be set per-thread basis as it uses a ThreadLocal internally to remember the values.

If you are using Thread directly or ExecutoService you must ensure that MDC value is set directly before your Runnable runs. If you use a thread pool it also must be cleared after the Runnable finishes.

There are many ways to achieve it but one possible way is to define a wrapper class:

class MDCWrapper implements Runnable {

  private final Runnable target;

  @Override
  public void run() {
    MDC.put("txnId", UUID.randomUUID().toString());
    try {
      target.run();
    } finally {
      MDC.remove("txnId");
    }
  } 

}   

and then use it as:

Thread t = new Thread(new MDCWrapper(new DeviceEventWorker()));
t.start();

Upvotes: 2

Related Questions