diff --git a/log-mdc/README.md b/log-mdc/README.md new file mode 100644 index 0000000000..c271cbc63a --- /dev/null +++ b/log-mdc/README.md @@ -0,0 +1,16 @@ +### Relevant Articles: +- TBD + +### References + +_Log4j MDC_ +* +* + +_Log4j2 MDC_ +* + +_Logback MDC_ +* + + diff --git a/log-mdc/pom.xml b/log-mdc/pom.xml new file mode 100644 index 0000000000..7d68049cde --- /dev/null +++ b/log-mdc/pom.xml @@ -0,0 +1,66 @@ + + 4.0.0 + com.baeldung + logmdc + 0.0.1-SNAPSHOT + logmdc + tutorial on logging with MDC + + + + + org.springframework + spring-context + 4.3.3.RELEASE + + + org.springframework + spring-webmvc + 4.3.3.RELEASE + + + + + log4j + log4j + 1.2.17 + + + + + org.apache.logging.log4j + log4j-api + 2.7 + + + org.apache.logging.log4j + log4j-core + 2.7 + + + + + com.lmax + disruptor + 3.3.4 + + + + + ch.qos.logback + logback-classic + 1.1.7 + + + + junit + junit + 4.12 + test + + + + + + \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/TransactionFactory.java b/log-mdc/src/main/java/com/baeldung/mdc/TransactionFactory.java new file mode 100644 index 0000000000..ec1887eea6 --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/TransactionFactory.java @@ -0,0 +1,21 @@ +package com.baeldung.mdc; + +import static java.lang.Math.floor; +import static java.lang.Math.random; + +import java.util.UUID; + +public class TransactionFactory { + + private static final String[] NAMES = {"John", "Susan", "Marc", "Samantha"}; + private static long nextId = 1; + + public Transfer newInstance() { + String transactionId = String.valueOf( nextId++ ); + String owner = NAMES[ (int) floor(random()*NAMES.length) ]; + long amount = (long) (random()*1500 + 500); + Transfer tx = new Transfer(transactionId, owner, amount); + return tx; + } + +} diff --git a/log-mdc/src/main/java/com/baeldung/mdc/Transfer.java b/log-mdc/src/main/java/com/baeldung/mdc/Transfer.java new file mode 100644 index 0000000000..1a27fe4eec --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/Transfer.java @@ -0,0 +1,27 @@ +package com.baeldung.mdc; + +public class Transfer { + + private String transactionId; + private String sender; + private Long amount; + + public Transfer(String transactionId, String sender, long amount) { + this.transactionId = transactionId; + this.sender = sender; + this.amount = amount; + } + + public String getSender() { + return sender; + } + + public String getTransactionId() { + return transactionId; + } + + public Long getAmount() { + return amount; + } + +} diff --git a/log-mdc/src/main/java/com/baeldung/mdc/TransferDemo.java b/log-mdc/src/main/java/com/baeldung/mdc/TransferDemo.java new file mode 100644 index 0000000000..daf256007c --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/TransferDemo.java @@ -0,0 +1,32 @@ +package com.baeldung.mdc; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.log4j.Logger; + +import com.baeldung.mdc.log4j.Log4JRunnable; +import com.baeldung.mdc.log4j2.Log4J2Runnable; +import com.baeldung.mdc.slf4j.Slf4jRunnable; + +public class TransferDemo { + + public static void main(String[] args) { + + ExecutorService executor = Executors.newFixedThreadPool(3); + TransactionFactory transactionFactory = new TransactionFactory(); + + for (int i = 0; i < 10; i++) { + Transfer tx = transactionFactory.newInstance(); + + //Runnable task = new Log4JRunnable(tx); + //Runnable task = new Log4J2Runnable(tx); + Runnable task = new Slf4jRunnable(tx); + + executor.submit(task); + } + + executor.shutdown(); + + } +} diff --git a/log-mdc/src/main/java/com/baeldung/mdc/TransferService.java b/log-mdc/src/main/java/com/baeldung/mdc/TransferService.java new file mode 100644 index 0000000000..95e7d38630 --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/TransferService.java @@ -0,0 +1,28 @@ +package com.baeldung.mdc; + +/** + * A fake transfer service simulating an actual one. + */ +public abstract class TransferService { + + /** Sample service transferring a given amount of money. + * @return {@code true} when the transfer complete successfully, {@code false} otherwise. */ + public boolean transfer(long amount) { + beforeTransfer(amount); + // exchange messages with a remote system to transfer the money + try { + // let's pause randomly to properly simulate an actual system. + Thread.sleep((long) (500 + Math.random() * 500)); + } catch (InterruptedException e) { + // should never happen + } + // let's simulate both failing and successful transfers + boolean outcome = Math.random() >= 0.25; + afterTransfer(amount, outcome); + return outcome; + } + + abstract protected void beforeTransfer(long amount); + + abstract protected void afterTransfer(long amount, boolean outcome); +} diff --git a/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JRunnable.java b/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JRunnable.java new file mode 100644 index 0000000000..7711795a59 --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JRunnable.java @@ -0,0 +1,26 @@ +package com.baeldung.mdc.log4j; + +import org.apache.log4j.MDC; + +import com.baeldung.mdc.Transfer; + +public class Log4JRunnable implements Runnable { + + private Transfer tx; + private static Log4JTransferService log4jBusinessService = new Log4JTransferService(); + + public Log4JRunnable(Transfer tx) { + this.tx = tx; + } + + public void run() { + + MDC.put("transaction.id", tx.getTransactionId()); + MDC.put("transaction.owner", tx.getSender()); + + log4jBusinessService.transfer(tx.getAmount()); + + MDC.clear(); + + } +} \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JTransferService.java b/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JTransferService.java new file mode 100644 index 0000000000..a8bfe7957a --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/log4j/Log4JTransferService.java @@ -0,0 +1,21 @@ +package com.baeldung.mdc.log4j; + +import org.apache.log4j.Logger; + +import com.baeldung.mdc.TransferService; + +public class Log4JTransferService extends TransferService { + + private Logger logger = Logger.getLogger(Log4JTransferService.class); + + @Override + protected void beforeTransfer(long amount) { + logger.info("Preparing to transfer " + amount + "$."); + } + + @Override + protected void afterTransfer(long amount, boolean outcome) { + logger.info("Has transfer of " + amount + "$ completed successfully ? " + outcome + "."); + } + +} \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2Runnable.java b/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2Runnable.java new file mode 100644 index 0000000000..0b7f516bd1 --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2Runnable.java @@ -0,0 +1,25 @@ +package com.baeldung.mdc.log4j2; + +import org.apache.logging.log4j.ThreadContext; + +import com.baeldung.mdc.Transfer; + +public class Log4J2Runnable implements Runnable { + private final Transfer tx; + private Log4J2TransferService log4j2BusinessService = new Log4J2TransferService(); + + public Log4J2Runnable(Transfer tx) { + this.tx = tx; + } + + public void run() { + + ThreadContext.put("transaction.id", tx.getTransactionId()); + ThreadContext.put("transaction.owner", tx.getSender()); + + log4j2BusinessService.transfer(tx.getAmount()); + + ThreadContext.clearAll(); + + } +} \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2TransferService.java b/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2TransferService.java new file mode 100644 index 0000000000..fb6eeccc9e --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/log4j2/Log4J2TransferService.java @@ -0,0 +1,22 @@ +package com.baeldung.mdc.log4j2; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.baeldung.mdc.TransferService; + +public class Log4J2TransferService extends TransferService { + + private static final Logger logger = LogManager.getLogger(); + + @Override + protected void beforeTransfer(long amount) { + logger.info("Preparing to transfer {}$.", amount); + } + + @Override + protected void afterTransfer(long amount, boolean outcome) { + logger.info("Has transfer of {}$ completed successfully ? {}.", amount, outcome); + } + +} \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4TransferService.java b/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4TransferService.java new file mode 100644 index 0000000000..f4df150a6a --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4TransferService.java @@ -0,0 +1,22 @@ +package com.baeldung.mdc.slf4j; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.baeldung.mdc.TransferService; + +final class Slf4TransferService extends TransferService { + + private static final Logger logger = LoggerFactory.getLogger(Slf4TransferService.class); + + @Override + protected void beforeTransfer(long amount) { + logger.info("Preparing to transfer {}$.", amount); + } + + @Override + protected void afterTransfer(long amount, boolean outcome) { + logger.info("Has transfer of {}$ completed successfully ? {}.", amount, outcome); + } + +} \ No newline at end of file diff --git a/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4jRunnable.java b/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4jRunnable.java new file mode 100644 index 0000000000..e30a28a3c7 --- /dev/null +++ b/log-mdc/src/main/java/com/baeldung/mdc/slf4j/Slf4jRunnable.java @@ -0,0 +1,24 @@ +package com.baeldung.mdc.slf4j; + +import org.slf4j.MDC; + +import com.baeldung.mdc.Transfer; + +public class Slf4jRunnable implements Runnable { + private final Transfer tx; + + public Slf4jRunnable(Transfer tx) { + this.tx = tx; + } + + public void run() { + + MDC.put("transaction.id", tx.getTransactionId()); + MDC.put("transaction.owner", tx.getSender()); + + new Slf4TransferService().transfer(tx.getAmount()); + + MDC.clear(); + + } +} \ No newline at end of file diff --git a/log-mdc/src/main/resources/log4j.properties b/log-mdc/src/main/resources/log4j.properties new file mode 100644 index 0000000000..39be027f3f --- /dev/null +++ b/log-mdc/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender +log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout + +#note the %X{userName} - this is how you fetch data from Mapped Diagnostic Context (MDC) +#log4j.appender.consoleAppender.layout.ConversionPattern=%-4r [%t] %5p %c{1} %x - %m%n +log4j.appender.consoleAppender.layout.ConversionPattern=%-4r [%t] %5p %c{1} %x - %m - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n + +log4j.rootLogger = TRACE, consoleAppender \ No newline at end of file diff --git a/log-mdc/src/main/resources/log4j2.xml b/log-mdc/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..800cfacafe --- /dev/null +++ b/log-mdc/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/log-mdc/src/main/resources/logback.xml b/log-mdc/src/main/resources/logback.xml new file mode 100644 index 0000000000..44d247c87e --- /dev/null +++ b/log-mdc/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %-4r [%t] %5p %c{1} - %m - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n + + + + + + + + \ No newline at end of file diff --git a/log-mdc/src/test/java/com/baeldung/mdc/log4j/Demo.java b/log-mdc/src/test/java/com/baeldung/mdc/log4j/Demo.java new file mode 100644 index 0000000000..f9a210606f --- /dev/null +++ b/log-mdc/src/test/java/com/baeldung/mdc/log4j/Demo.java @@ -0,0 +1,26 @@ +package com.baeldung.mdc.log4j; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import com.baeldung.mdc.TransactionFactory; +import com.baeldung.mdc.Transfer; + +public class Demo { + + @Test + public void main() throws InterruptedException { + ExecutorService executor = Executors.newFixedThreadPool(3); + TransactionFactory transactionFactory = new TransactionFactory(); + for (int i = 0; i < 10; i++) { + Transfer tx = transactionFactory.newInstance(); + Runnable task = new Log4JRunnable(tx); + executor.submit(task); + } + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } +} diff --git a/log-mdc/src/test/java/com/baeldung/mdc/log4j2/Demo.java b/log-mdc/src/test/java/com/baeldung/mdc/log4j2/Demo.java new file mode 100644 index 0000000000..3f7c1d37d5 --- /dev/null +++ b/log-mdc/src/test/java/com/baeldung/mdc/log4j2/Demo.java @@ -0,0 +1,30 @@ +package com.baeldung.mdc.log4j2; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.junit.Test; + +import com.baeldung.mdc.TransactionFactory; +import com.baeldung.mdc.Transfer; +import com.baeldung.mdc.log4j.Log4JRunnable; +import com.baeldung.mdc.log4j2.Log4J2Runnable; +import com.baeldung.mdc.slf4j.Slf4jRunnable; + +public class Demo { + + @Test + public void main() throws InterruptedException { + ExecutorService executor = Executors.newFixedThreadPool(3); + TransactionFactory transactionFactory = new TransactionFactory(); + for (int i = 0; i < 10; i++) { + Transfer tx = transactionFactory.newInstance(); + Runnable task = new Log4J2Runnable(tx); + executor.submit(task); + } + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } +} diff --git a/log-mdc/src/test/java/com/baeldung/mdc/slf4j/Demo.java b/log-mdc/src/test/java/com/baeldung/mdc/slf4j/Demo.java new file mode 100644 index 0000000000..98db698f47 --- /dev/null +++ b/log-mdc/src/test/java/com/baeldung/mdc/slf4j/Demo.java @@ -0,0 +1,30 @@ +package com.baeldung.mdc.slf4j; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.junit.Test; + +import com.baeldung.mdc.TransactionFactory; +import com.baeldung.mdc.Transfer; +import com.baeldung.mdc.log4j.Log4JRunnable; +import com.baeldung.mdc.log4j2.Log4J2Runnable; +import com.baeldung.mdc.slf4j.Slf4jRunnable; + +public class Demo { + + @Test + public void main() throws InterruptedException { + ExecutorService executor = Executors.newFixedThreadPool(3); + TransactionFactory transactionFactory = new TransactionFactory(); + for (int i = 0; i < 10; i++) { + Transfer tx = transactionFactory.newInstance(); + Runnable task = new Slf4jRunnable(tx); + executor.submit(task); + } + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } +}