diff --git a/log-mdc/pom.xml b/log-mdc/pom.xml
index 4cabce502e..28c8bb820e 100644
--- a/log-mdc/pom.xml
+++ b/log-mdc/pom.xml
@@ -5,20 +5,37 @@
logmdc
0.0.1-SNAPSHOT
logmdc
- tutorial on logging with MDC
+ war
+ tutorial on logging with MDC and NDC
-
- org.springframework
- spring-context
- ${springframework.version}
-
-
- org.springframework
- spring-webmvc
- ${springframework.version}
-
+
+ org.springframework
+ spring-core
+ ${springframework.version}
+
+
+ org.springframework
+ spring-web
+ ${springframework.version}
+
+
+ org.springframework
+ spring-webmvc
+ ${springframework.version}
+
+
+ javax.servlet
+ javax.servlet-api
+ ${javax.servlet.version}
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.library}
+
@@ -31,12 +48,12 @@
org.apache.logging.log4j
log4j-api
- 2.7
+ ${log4j2.version}
org.apache.logging.log4j
log4j-core
- ${log4j-api.version}
+ ${log4j2.version}
@@ -52,6 +69,13 @@
logback-classic
${logback.version}
+
+
+
+ org.jboss.logging
+ jboss-logging
+ ${jbosslogging.version}
+
junit
@@ -59,15 +83,51 @@
${junit.version}
test
+
+ org.springframework
+ spring-test
+ ${springframework.version}
+ test
+
4.3.4.RELEASE
1.2.17
- 2.7
+ 2.7
3.3.6
1.1.7
+ 3.3.0.Final
+ 3.1.0
+ 2.8.5
4.12
-
\ No newline at end of file
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.2
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.4
+
+ src/main/webapp
+ logging-service
+ false
+
+
+
+
+ logging-service
+
+
diff --git a/log-mdc/src/main/java/com/baeldung/config/AppConfiguration.java b/log-mdc/src/main/java/com/baeldung/config/AppConfiguration.java
new file mode 100644
index 0000000000..5e52c5f25e
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/config/AppConfiguration.java
@@ -0,0 +1,19 @@
+package com.baeldung.config;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+@EnableWebMvc
+@ComponentScan(basePackages = "com.baeldung")
+public class AppConfiguration extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+ configurer.enable();
+ }
+
+}
diff --git a/log-mdc/src/main/java/com/baeldung/config/AppInitializer.java b/log-mdc/src/main/java/com/baeldung/config/AppInitializer.java
new file mode 100644
index 0000000000..828c2b2efa
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/config/AppInitializer.java
@@ -0,0 +1,29 @@
+package com.baeldung.config;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
+
+public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+ super.onStartup(servletContext);
+ }
+
+ @Override
+ protected Class>[] getRootConfigClasses() {
+ return new Class[] { AppConfiguration.class };
+ }
+
+ @Override
+ protected Class>[] getServletConfigClasses() {
+ return null;
+ }
+
+ @Override
+ protected String[] getServletMappings() {
+ return new String[] { "/" };
+ }
+}
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/Investment.java b/log-mdc/src/main/java/com/baeldung/ndc/Investment.java
new file mode 100644
index 0000000000..4275c6ef21
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/Investment.java
@@ -0,0 +1,41 @@
+package com.baeldung.ndc;
+
+public class Investment {
+ private String transactionId;
+ private String owner;
+ private Long amount;
+
+ public Investment() {
+ }
+
+ public Investment(String transactionId, String owner, Long amount) {
+ this.transactionId = transactionId;
+ this.owner = owner;
+ this.amount = amount;
+ }
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public void setTransactionId(String transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public Long getAmount() {
+ return amount;
+ }
+
+ public void setAmount(Long amount) {
+ this.amount = amount;
+ }
+
+}
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/controller/JBossLoggingController.java b/log-mdc/src/main/java/com/baeldung/ndc/controller/JBossLoggingController.java
new file mode 100644
index 0000000000..b024f3ec81
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/controller/JBossLoggingController.java
@@ -0,0 +1,42 @@
+package com.baeldung.ndc.controller;
+
+import org.jboss.logging.NDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.ndc.Investment;
+import com.baeldung.ndc.service.InvestmentService;
+
+
+@RestController
+public class JBossLoggingController {
+ @Autowired
+ @Qualifier("JBossLoggingInvestmentService")
+ private InvestmentService jbossLoggingBusinessService;
+
+ @RequestMapping(value = "/ndc/jboss-logging", method = RequestMethod.POST)
+ public ResponseEntity postPayment(@RequestBody Investment investment) {
+ // Add transactionId and owner to NDC
+ NDC.push("tx.id=" + investment.getTransactionId());
+ NDC.push("tx.owner=" + investment.getOwner());
+
+ try {
+ jbossLoggingBusinessService.transfer(investment.getAmount());
+ } finally {
+ // take out owner from the NDC stack
+ NDC.pop();
+
+ // take out transactionId from the NDC stack
+ NDC.pop();
+
+ NDC.clear();
+ }
+ return new ResponseEntity(investment, HttpStatus.OK);
+ }
+}
\ No newline at end of file
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4J2Controller.java b/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4J2Controller.java
new file mode 100644
index 0000000000..9cc57d9fa7
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4J2Controller.java
@@ -0,0 +1,41 @@
+package com.baeldung.ndc.controller;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.ndc.Investment;
+import com.baeldung.ndc.service.InvestmentService;
+
+@RestController
+public class Log4J2Controller {
+ @Autowired
+ @Qualifier("log4j2InvestmentService")
+ private InvestmentService log4j2BusinessService;
+
+ @RequestMapping(value = "/ndc/log4j2", method = RequestMethod.POST)
+ public ResponseEntity postPayment(@RequestBody Investment investment) {
+ // Add transactionId and owner to NDC
+ ThreadContext.push("tx.id=" + investment.getTransactionId());
+ ThreadContext.push("tx.owner=" + investment.getOwner());
+
+ try {
+ log4j2BusinessService.transfer(investment.getAmount());
+ } finally {
+ // take out owner from the NDC stack
+ ThreadContext.pop();
+
+ // take out transactionId from the NDC stack
+ ThreadContext.pop();
+
+ ThreadContext.clearAll();
+ }
+ return new ResponseEntity(investment, HttpStatus.OK);
+ }
+}
\ No newline at end of file
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4JController.java b/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4JController.java
new file mode 100644
index 0000000000..daf7994a88
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/controller/Log4JController.java
@@ -0,0 +1,41 @@
+package com.baeldung.ndc.controller;
+
+import org.apache.log4j.NDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.ndc.Investment;
+import com.baeldung.ndc.service.InvestmentService;
+
+@RestController
+public class Log4JController {
+ @Autowired
+ @Qualifier("log4jInvestmentService")
+ private InvestmentService log4jBusinessService;
+
+ @RequestMapping(value = "/ndc/log4j", method = RequestMethod.POST)
+ public ResponseEntity postPayment(@RequestBody Investment investment) {
+ // Add transactionId and owner to NDC
+ NDC.push("tx.id=" + investment.getTransactionId());
+ NDC.push("tx.owner=" + investment.getOwner());
+
+ try {
+ log4jBusinessService.transfer(investment.getAmount());
+ } finally {
+ // take out owner from the NDC stack
+ NDC.pop();
+
+ // take out transactionId from the NDC stack
+ NDC.pop();
+
+ NDC.remove();
+ }
+ return new ResponseEntity(investment, HttpStatus.OK);
+ }
+}
\ No newline at end of file
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/service/InvestmentService.java b/log-mdc/src/main/java/com/baeldung/ndc/service/InvestmentService.java
new file mode 100644
index 0000000000..13d8e6a71b
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/service/InvestmentService.java
@@ -0,0 +1,31 @@
+package com.baeldung.ndc.service;
+
+/**
+ * A fake investment service.
+ */
+public interface InvestmentService {
+
+ /**
+ * Sample service transferring a given amount of money.
+ * @param amount
+ * @return {@code true} when the transfer complete successfully, {@code false} otherwise.
+ */
+ default 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;
+ }
+
+ void beforeTransfer(long amount);
+
+ void afterTransfer(long amount, boolean outcome);
+}
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/service/JBossLoggingInvestmentService.java b/log-mdc/src/main/java/com/baeldung/ndc/service/JBossLoggingInvestmentService.java
new file mode 100644
index 0000000000..e1e5e0a083
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/service/JBossLoggingInvestmentService.java
@@ -0,0 +1,21 @@
+package com.baeldung.ndc.service;
+
+import org.jboss.logging.Logger;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+@Qualifier("JBossLoggingInvestmentService")
+public class JBossLoggingInvestmentService implements InvestmentService {
+ private static final Logger logger = Logger.getLogger(JBossLoggingInvestmentService.class);
+
+ @Override
+ public void beforeTransfer(long amount) {
+ logger.infov("Preparing to transfer {0}$.", amount);
+ }
+
+ @Override
+ public void afterTransfer(long amount, boolean outcome) {
+ logger.infov("Has transfer of {0}$ completed successfully ? {1}.", amount, outcome);
+ }
+}
\ No newline at end of file
diff --git a/log-mdc/src/main/java/com/baeldung/ndc/service/Log4J2InvestmentService.java b/log-mdc/src/main/java/com/baeldung/ndc/service/Log4J2InvestmentService.java
new file mode 100644
index 0000000000..6e55796574
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/service/Log4J2InvestmentService.java
@@ -0,0 +1,22 @@
+package com.baeldung.ndc.service;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+@Qualifier("log4j2InvestmentService")
+public class Log4J2InvestmentService implements InvestmentService {
+ private static final Logger logger = LogManager.getLogger();
+
+ @Override
+ public void beforeTransfer(long amount) {
+ logger.info("Preparing to transfer {}$.", amount);
+ }
+
+ @Override
+ public 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/ndc/service/Log4JInvestmentService.java b/log-mdc/src/main/java/com/baeldung/ndc/service/Log4JInvestmentService.java
new file mode 100644
index 0000000000..1f581554e5
--- /dev/null
+++ b/log-mdc/src/main/java/com/baeldung/ndc/service/Log4JInvestmentService.java
@@ -0,0 +1,21 @@
+package com.baeldung.ndc.service;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+@Qualifier("log4jInvestmentService")
+public class Log4JInvestmentService implements InvestmentService {
+ private Logger logger = Logger.getLogger(Log4JInvestmentService.class);
+
+ @Override
+ public void beforeTransfer(long amount) {
+ logger.info("Preparing to transfer " + amount + "$.");
+ }
+
+ @Override
+ public 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/resources/log4j.properties b/log-mdc/src/main/resources/log4j.properties
index 39be027f3f..575ebcca8d 100644
--- a/log-mdc/src/main/resources/log4j.properties
+++ b/log-mdc/src/main/resources/log4j.properties
@@ -3,6 +3,10 @@ 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
+# %x is used to fetch data from NDC. So below setting uses both MDC and NDC
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
+# NDC only setting - %x is used to fetch data from NDC
+#log4j.appender.consoleAppender.layout.ConversionPattern=%-4r [%t] %5p %c{1} - %m - [%x]%n
+
+log4j.rootLogger = INFO, 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
index 800cfacafe..cbdf02fc51 100644
--- a/log-mdc/src/main/resources/log4j2.xml
+++ b/log-mdc/src/main/resources/log4j2.xml
@@ -2,8 +2,15 @@
+
+ pattern="%-4r [%t] %5p %c{1} - %m - %x - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n" />
+
+
+
+
diff --git a/log-mdc/src/test/java/com/baeldung/ndc/NDCLogTest.java b/log-mdc/src/test/java/com/baeldung/ndc/NDCLogTest.java
new file mode 100644
index 0000000000..8cba176f7e
--- /dev/null
+++ b/log-mdc/src/test/java/com/baeldung/ndc/NDCLogTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.ndc;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.baeldung.config.AppConfiguration;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = AppConfiguration.class)
+@WebAppConfiguration
+public class NDCLogTest {
+
+ private MockMvc mockMvc;
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ private Investment investment;
+
+ @Before
+ public void setUp() {
+ mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
+
+ investment = new Investment();
+ investment.setTransactionId("123");
+ investment.setOwner("Mark");
+ investment.setAmount(1000L);
+ }
+
+ @Test
+ public void givenLog4jLogger_whenNDCAdded_thenResponseOkAndNDCInLog() throws Exception {
+ mockMvc.perform(post("/ndc/log4j", investment).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(investment))).andExpect(status().is2xxSuccessful());
+
+ }
+
+ @Test
+ public void givenLog4j2Logger_whenNDCAdded_thenResponseOkAndNDCInLog() throws Exception {
+ mockMvc.perform(post("/ndc/log4j2", investment).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(investment))).andExpect(status().is2xxSuccessful());
+
+ }
+
+ @Test
+ public void givenJBossLoggerBridge_whenNDCAdded_thenResponseOkAndNDCInLog() throws Exception {
+ mockMvc.perform(post("/ndc/jboss-logging", investment).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(investment))).andExpect(status().is2xxSuccessful());
+
+ }
+
+}